From 0041bf6544b9b46022b6362eae834a36505d7baa Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 29 Mar 2017 16:39:15 +0200 Subject: [PATCH 001/566] name-rev: refactor logic to see if a new candidate is a better name When we encounter a new ref that could describe the commit we are looking at, we compare the name that is formed using that ref and the name we found so far and pick a better one. Factor the comparison logic out to a separate helper function, while keeping the current logic the same (i.e. a name that is based on an older tag is better, and if two tags of the same age can reach the commit, the one with fewer number of hops to reach the commit is better). Signed-off-by: Junio C Hamano Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/name-rev.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 8bdc3eaa6f..7890f826ce 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -20,6 +20,17 @@ static long cutoff = LONG_MAX; /* How many generations are maximally preferred over _one_ merge traversal? */ #define MERGE_TRAVERSAL_WEIGHT 65535 +static int is_better_name(struct rev_name *name, + const char *tip_name, + unsigned long taggerdate, + int generation, + int distance) +{ + return (name->taggerdate > taggerdate || + (name->taggerdate == taggerdate && + name->distance > distance)); +} + static void name_rev(struct commit *commit, const char *tip_name, unsigned long taggerdate, int generation, int distance, @@ -45,9 +56,8 @@ static void name_rev(struct commit *commit, name = xmalloc(sizeof(rev_name)); commit->util = name; goto copy_data; - } else if (name->taggerdate > taggerdate || - (name->taggerdate == taggerdate && - name->distance > distance)) { + } else if (is_better_name(name, tip_name, taggerdate, + generation, distance)) { copy_data: name->tip_name = tip_name; name->taggerdate = taggerdate; From ef1e74065c19cc427c4a1b322154fd55d7a3588b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 29 Mar 2017 16:39:16 +0200 Subject: [PATCH 002/566] name-rev: favor describing with tags and use committer date to tiebreak "git name-rev" assigned a phony "far in the future" date to tips of refs that are not pointing at tag objects, and favored names based on a ref with the oldest date. This made it almost impossible for an unannotated tags and branches to be counted as a viable base, which was especially problematic when the command is run with the "--tags" option. If an unannotated tag that points at an ancient commit and an annotated tag that points at a much newer commit reaches the commit that is being named, the old unannotated tag was ignored. Update the "taggerdate" field of the rev-name structure, which is initialized from the tip of ref, to have the committer date if the object at the tip of ref is a commit, not a tag, so that we can optionally take it into account when doing "is this name better?" comparison logic. When "name-rev" is run without the "--tags" option, the general expectation is still to name the commit based on a tag if possible, but use non-tag refs as fallback, and tiebreak among these non-tag refs by favoring names with shorter hops from the tip. The use of a phony "far in the future" date in the original code was an effective way to ensure this expectation is held: a non-tag tip gets the same "far in the future" timestamp, giving precedence to tags, and among non-tag tips, names with shorter hops are preferred over longer hops, without taking the "taggerdate" into account. As we are taking over the "taggerdate" field to store the committer date for tips with commits: (1) keep the original logic when comparing names based on two refs both of which are from refs/tags/; (2) favoring a name based on a ref in refs/tags/ hierarchy over a ref outside the hierarchy; (3) between two names based on a ref both outside refs/tags/, give precedence to a name with shorter hops and use "taggerdate" only to tie-break. A change to t4202 is a natural consequence. The test creates a commit on a branch "side" and points at it with an unannotated tag "refs/tags/side-2". The original code couldn't decide which one to favor at all, and gave a name based on a branch (simply because refs/heads/side sorts earlier than refs/tags/side-2). Because the updated logic is taught to favor refs in refs/tags/ hierarchy, the the test is updated to expect to see tags/side-2 instead. [mjg: open-coded the comparisons in is_better_name(), dropping a helper macro used in the original] Signed-off-by: Junio C Hamano Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- builtin/name-rev.c | 53 ++++++++++++++++++++++++++++++++++++++-------- t/t4202-log.sh | 2 +- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 7890f826ce..bf7ed015ae 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -13,6 +13,7 @@ typedef struct rev_name { unsigned long taggerdate; int generation; int distance; + int from_tag; } rev_name; static long cutoff = LONG_MAX; @@ -24,16 +25,43 @@ static int is_better_name(struct rev_name *name, const char *tip_name, unsigned long taggerdate, int generation, - int distance) + int distance, + int from_tag) { - return (name->taggerdate > taggerdate || - (name->taggerdate == taggerdate && - name->distance > distance)); + /* + * When comparing names based on tags, prefer names + * based on the older tag, even if it is farther away. + */ + if (from_tag && name->from_tag) + return (name->taggerdate > taggerdate || + (name->taggerdate == taggerdate && + name->distance > distance)); + + /* + * We know that at least one of them is a non-tag at this point. + * favor a tag over a non-tag. + */ + if (name->from_tag != from_tag) + return from_tag; + + /* + * We are now looking at two non-tags. Tiebreak to favor + * shorter hops. + */ + if (name->distance != distance) + return name->distance > distance; + + /* ... or tiebreak to favor older date */ + if (name->taggerdate != taggerdate) + return name->taggerdate > taggerdate; + + /* keep the current one if we cannot decide */ + return 0; } static void name_rev(struct commit *commit, const char *tip_name, unsigned long taggerdate, - int generation, int distance, + int generation, int distance, int from_tag, int deref) { struct rev_name *name = (struct rev_name *)commit->util; @@ -57,12 +85,13 @@ static void name_rev(struct commit *commit, commit->util = name; goto copy_data; } else if (is_better_name(name, tip_name, taggerdate, - generation, distance)) { + generation, distance, from_tag)) { copy_data: name->tip_name = tip_name; name->taggerdate = taggerdate; name->generation = generation; name->distance = distance; + name->from_tag = from_tag; } else return; @@ -82,10 +111,12 @@ copy_data: parent_number); name_rev(parents->item, new_name, taggerdate, 0, - distance + MERGE_TRAVERSAL_WEIGHT, 0); + distance + MERGE_TRAVERSAL_WEIGHT, + from_tag, 0); } else { name_rev(parents->item, tip_name, taggerdate, - generation + 1, distance + 1, 0); + generation + 1, distance + 1, + from_tag, 0); } } } @@ -216,9 +247,13 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo } if (o && o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; + int from_tag = starts_with(path, "refs/tags/"); + if (taggerdate == ULONG_MAX) + taggerdate = ((struct commit *)o)->date; path = name_ref_abbrev(path, can_abbreviate_output); - name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref); + name_rev(commit, xstrdup(path), taggerdate, 0, 0, + from_tag, deref); } return 0; } diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 48b55bfd27..038911f230 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -398,7 +398,7 @@ cat > expect <<\EOF | | | | Merge branch 'side' | | -| * commit side +| * commit tags/side-2 | | Author: A U Thor | | | | side-2 From 159e6010c2d586acea9704993971365a804ea1ce Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Tue, 11 Apr 2017 10:33:07 +0200 Subject: [PATCH 003/566] travis-ci: build documentation with AsciiDoc and Asciidoctor ec3366e introduced a knob to enable the use of Asciidoctor in addition to AsciiDoc. Build the documentation on TravisCI with this knob to reduce the likeliness of breaking Asciidoctor support in the future. Signed-off-by: Lars Schneider Signed-off-by: Junio C Hamano --- .travis.yml | 2 +- ci/test-documentation.sh | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 591cc57b80..c26f3bf789 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ matrix: - asciidoc - xmlto before_install: - before_script: + before_script: gem install asciidoctor script: ci/test-documentation.sh after_failure: diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh index 579d540d32..bf23b2caea 100755 --- a/ci/test-documentation.sh +++ b/ci/test-documentation.sh @@ -7,8 +7,16 @@ set -e make check-builtins make check-docs -make doc +# Build docs with AsciiDoc +make doc test -s Documentation/git.html test -s Documentation/git.xml test -s Documentation/git.1 +grep ' Signed-off-by: Junio C Hamano --- ci/test-documentation.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh index bf23b2caea..58962d668a 100755 --- a/ci/test-documentation.sh +++ b/ci/test-documentation.sh @@ -9,7 +9,7 @@ make check-builtins make check-docs # Build docs with AsciiDoc -make doc +make --jobs=2 doc test -s Documentation/git.html test -s Documentation/git.xml test -s Documentation/git.1 @@ -17,6 +17,6 @@ grep ' Signed-off-by: Junio C Hamano --- refs.c | 64 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/refs.c b/refs.c index bad05ba861..96eba92475 100644 --- a/refs.c +++ b/refs.c @@ -1450,32 +1450,32 @@ int resolve_gitlink_ref(const char *submodule, const char *refname, return 0; } -struct submodule_hash_entry +struct ref_store_hash_entry { struct hashmap_entry ent; /* must be the first member! */ struct ref_store *refs; - /* NUL-terminated name of submodule: */ - char submodule[FLEX_ARRAY]; + /* NUL-terminated identifier of the ref store: */ + char name[FLEX_ARRAY]; }; -static int submodule_hash_cmp(const void *entry, const void *entry_or_key, +static int ref_store_hash_cmp(const void *entry, const void *entry_or_key, const void *keydata) { - const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key; - const char *submodule = keydata ? keydata : e2->submodule; + const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key; + const char *name = keydata ? keydata : e2->name; - return strcmp(e1->submodule, submodule); + return strcmp(e1->name, name); } -static struct submodule_hash_entry *alloc_submodule_hash_entry( - const char *submodule, struct ref_store *refs) +static struct ref_store_hash_entry *alloc_ref_store_hash_entry( + const char *name, struct ref_store *refs) { - struct submodule_hash_entry *entry; + struct ref_store_hash_entry *entry; - FLEX_ALLOC_STR(entry, submodule, submodule); - hashmap_entry_init(entry, strhash(submodule)); + FLEX_ALLOC_STR(entry, name, name); + hashmap_entry_init(entry, strhash(name)); entry->refs = refs; return entry; } @@ -1487,19 +1487,19 @@ static struct ref_store *main_ref_store; static struct hashmap submodule_ref_stores; /* - * Return the ref_store instance for the specified submodule. If that - * ref_store hasn't been initialized yet, return NULL. + * Look up a ref store by name. If that ref_store hasn't been + * registered yet, return NULL. */ -static struct ref_store *lookup_submodule_ref_store(const char *submodule) +static struct ref_store *lookup_ref_store_map(struct hashmap *map, + const char *name) { - struct submodule_hash_entry *entry; + struct ref_store_hash_entry *entry; - if (!submodule_ref_stores.tablesize) + if (!map->tablesize) /* It's initialized on demand in register_ref_store(). */ return NULL; - entry = hashmap_get_from_hash(&submodule_ref_stores, - strhash(submodule), submodule); + entry = hashmap_get_from_hash(map, strhash(name), name); return entry ? entry->refs : NULL; } @@ -1535,20 +1535,19 @@ struct ref_store *get_main_ref_store(void) } /* - * Register the specified ref_store to be the one that should be used - * for submodule. It is a fatal error to call this function twice for - * the same submodule. + * Associate a ref store with a name. It is a fatal error to call this + * function twice for the same name. */ -static void register_submodule_ref_store(struct ref_store *refs, - const char *submodule) +static void register_ref_store_map(struct hashmap *map, + const char *type, + struct ref_store *refs, + const char *name) { - if (!submodule_ref_stores.tablesize) - hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0); + if (!map->tablesize) + hashmap_init(map, ref_store_hash_cmp, 0); - if (hashmap_put(&submodule_ref_stores, - alloc_submodule_hash_entry(submodule, refs))) - die("BUG: ref_store for submodule '%s' initialized twice", - submodule); + if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs))) + die("BUG: %s ref_store '%s' initialized twice", type, name); } struct ref_store *get_submodule_ref_store(const char *submodule) @@ -1565,7 +1564,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule) return get_main_ref_store(); } - refs = lookup_submodule_ref_store(submodule); + refs = lookup_ref_store_map(&submodule_ref_stores, submodule); if (refs) return refs; @@ -1584,7 +1583,8 @@ struct ref_store *get_submodule_ref_store(const char *submodule) /* assume that add_submodule_odb() has been called */ refs = ref_store_init(submodule_sb.buf, REF_STORE_READ | REF_STORE_ODB); - register_submodule_ref_store(refs, submodule); + register_ref_store_map(&submodule_ref_stores, "submodule", + refs, submodule); strbuf_release(&submodule_sb); return refs; From cd279e2e1b4f41f0272d90abb2ba5a06c544b3da Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 18 Apr 2017 14:37:22 -0700 Subject: [PATCH 008/566] entry.c: submodule recursing: respect force flag correctly In case of a non-forced worktree update, the submodule movement is tested in a dry run first, such that it doesn't matter if the actual update is done via the force flag. However for correctness, we want to give the flag as specified by the user. All callers have been inspected and updated if needed. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- entry.c | 8 ++++---- unpack-trees.c | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/entry.c b/entry.c index d2b512da90..d6b263f78e 100644 --- a/entry.c +++ b/entry.c @@ -208,7 +208,8 @@ static int write_entry(struct cache_entry *ce, sub = submodule_from_ce(ce); if (sub) return submodule_move_head(ce->name, - NULL, oid_to_hex(&ce->oid), SUBMODULE_MOVE_HEAD_FORCE); + NULL, oid_to_hex(&ce->oid), + state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0); break; default: return error("unknown file mode for %s in index", path); @@ -282,12 +283,11 @@ int checkout_entry(struct cache_entry *ce, unlink_or_warn(ce->name); return submodule_move_head(ce->name, - NULL, oid_to_hex(&ce->oid), - SUBMODULE_MOVE_HEAD_FORCE); + NULL, oid_to_hex(&ce->oid), 0); } else return submodule_move_head(ce->name, "HEAD", oid_to_hex(&ce->oid), - SUBMODULE_MOVE_HEAD_FORCE); + state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0); } if (!changed) diff --git a/unpack-trees.c b/unpack-trees.c index 6b7356dab2..4b3f9518e5 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -252,14 +252,18 @@ static int check_submodule_move_head(const struct cache_entry *ce, const char *new_id, struct unpack_trees_options *o) { + unsigned flags = SUBMODULE_MOVE_HEAD_DRY_RUN; const struct submodule *sub = submodule_from_ce(ce); if (!sub) return 0; + if (o->reset) + flags |= SUBMODULE_MOVE_HEAD_FORCE; + switch (sub->update_strategy.type) { case SM_UPDATE_UNSPECIFIED: case SM_UPDATE_CHECKOUT: - if (submodule_move_head(ce->name, old_id, new_id, SUBMODULE_MOVE_HEAD_DRY_RUN)) + if (submodule_move_head(ce->name, old_id, new_id, flags)) return o->gently ? -1 : add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name); return 0; @@ -308,6 +312,7 @@ static void unlink_entry(const struct cache_entry *ce) case SM_UPDATE_CHECKOUT: case SM_UPDATE_REBASE: case SM_UPDATE_MERGE: + /* state.force is set at the caller. */ submodule_move_head(ce->name, "HEAD", NULL, SUBMODULE_MOVE_HEAD_FORCE); break; From 823bab09c68b4048e433ab48b9185302c2f4b63a Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 18 Apr 2017 14:37:23 -0700 Subject: [PATCH 009/566] submodule.c: uninitialized submodules are ignored in recursive commands This was an oversight when working on the working tree modifying commands recursing into submodules. To test for uninitialized submodules, introduce another submodule "uninitialized_sub". Adding it via `submodule add` will activate the submodule in the preparation area (in create_lib_submodule_repo we setup all the things in submodule_update_repo), but the later tests will use a new testing repo that clones the preparation repo in which the new submodule is not initialized. By adding it to the branch "add_sub1", which is the starting point of all other branches, we have wide coverage. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 3 +++ t/lib-submodule-update.sh | 1 + 2 files changed, 4 insertions(+) diff --git a/submodule.c b/submodule.c index 7c3c4b17fb..ccf8932731 100644 --- a/submodule.c +++ b/submodule.c @@ -1333,6 +1333,9 @@ int submodule_move_head(const char *path, struct child_process cp = CHILD_PROCESS_INIT; const struct submodule *sub; + if (!is_submodule_initialized(path)) + return 0; + sub = submodule_from_path(null_sha1, path); if (!sub) diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index fb4f7b014e..22dd9e060c 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -73,6 +73,7 @@ create_lib_submodule_repo () { git checkout -b "add_sub1" && git submodule add ../submodule_update_sub1 sub1 && + git submodule add ../submodule_update_sub1 uninitialized_sub && git config -f .gitmodules submodule.sub1.ignore all && git config submodule.sub1.ignore all && git add .gitmodules && From f2d48994dc11ba367e92c38c8025c3354418145a Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 18 Apr 2017 14:37:24 -0700 Subject: [PATCH 010/566] submodule.c: submodule_move_head works with broken submodules Early on in submodule_move_head just after the check if the submodule is initialized, we need to check if the submodule is populated correctly. If the submodule is initialized but doesn't look like it is populated, this is a red flag and can indicate multiple sorts of failures: (1) The submodule may be recorded at an object name, that is missing. (2) The submodule '.git' file link may be broken and it is not pointing at a repository. In both cases we want to complain to the user in the non-forced mode, and in the forced mode ignoring the old state and just moving the submodule into its new state with a fixed '.git' file link. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 28 ++++++++++++++++++++++++---- t/lib-submodule-update.sh | 23 ++++++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/submodule.c b/submodule.c index ccf8932731..20ed5b5681 100644 --- a/submodule.c +++ b/submodule.c @@ -1332,10 +1332,24 @@ int submodule_move_head(const char *path, int ret = 0; struct child_process cp = CHILD_PROCESS_INIT; const struct submodule *sub; + int *error_code_ptr, error_code; if (!is_submodule_initialized(path)) return 0; + if (flags & SUBMODULE_MOVE_HEAD_FORCE) + /* + * Pass non NULL pointer to is_submodule_populated_gently + * to prevent die()-ing. We'll use connect_work_tree_and_git_dir + * to fixup the submodule in the force case later. + */ + error_code_ptr = &error_code; + else + error_code_ptr = NULL; + + if (old && !is_submodule_populated_gently(path, error_code_ptr)) + return 0; + sub = submodule_from_path(null_sha1, path); if (!sub) @@ -1353,15 +1367,21 @@ int submodule_move_head(const char *path, absorb_git_dir_into_superproject("", path, ABSORB_GITDIR_RECURSE_SUBMODULES); } else { - struct strbuf sb = STRBUF_INIT; - strbuf_addf(&sb, "%s/modules/%s", + char *gitdir = xstrfmt("%s/modules/%s", get_git_common_dir(), sub->name); - connect_work_tree_and_git_dir(path, sb.buf); - strbuf_release(&sb); + connect_work_tree_and_git_dir(path, gitdir); + free(gitdir); /* make sure the index is clean as well */ submodule_reset_index(path); } + + if (old && (flags & SUBMODULE_MOVE_HEAD_FORCE)) { + char *gitdir = xstrfmt("%s/modules/%s", + get_git_common_dir(), sub->name); + connect_work_tree_and_git_dir(path, gitdir); + free(gitdir); + } } prepare_submodule_repo_env_no_git_dir(&cp.env_array); diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index 22dd9e060c..f0b1b18206 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -1213,14 +1213,31 @@ test_submodule_forced_switch_recursing () { ) ' # Updating a submodule from an invalid sha1 updates - test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" ' + test_expect_success "$command: modified submodule does update submodule work tree from invalid commit" ' prolog && reset_work_tree_to_interested invalid_sub1 && ( cd submodule_update && git branch -t valid_sub1 origin/valid_sub1 && - test_must_fail $command valid_sub1 && - test_superproject_content origin/invalid_sub1 + $command valid_sub1 && + test_superproject_content origin/valid_sub1 && + test_submodule_content sub1 origin/valid_sub1 + ) + ' + + # Old versions of Git were buggy writing the .git link file + # (e.g. before f8eaa0ba98b and then moving the superproject repo + # whose submodules contained absolute paths) + test_expect_success "$command: updating submodules fixes .git links" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t modify_sub1 origin/modify_sub1 && + echo "gitdir: bogus/path" >sub1/.git && + $command modify_sub1 && + test_superproject_content origin/modify_sub1 && + test_submodule_content sub1 origin/modify_sub1 ) ' } From 1fd1a919ce9135a34866d5d13aed5312ea5e07f1 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 18 Apr 2017 16:17:55 -0700 Subject: [PATCH 011/566] t5550: use write_script to generate post-update hook The post-update hooks created in t5550-http-fetch-dumb.sh is missing the "!#/bin/sh" line which can cause issues with portability. Instead create the hook using the 'write_script' function which includes the proper "#!" line. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- t/t5550-http-fetch-dumb.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index 87308cdced..8552184e74 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -20,8 +20,9 @@ test_expect_success 'create http-accessible bare repository with loose objects' (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && git config core.bare true && mkdir -p hooks && - echo "exec git update-server-info" >hooks/post-update && - chmod +x hooks/post-update && + write_script "hooks/post-update" <<-\EOF && + exec git update-server-info + EOF hooks/post-update ) && git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && From c2d3119d7bb2122a715d2adac1574514aeff7723 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:18 -0700 Subject: [PATCH 012/566] t0061: run_command executes scripts without a #! line Add a test to 't0061-run-command.sh' to ensure that run_command can continue to execute scripts which don't include a '#!' line. As shell scripts are not natively executable on Windows, we use a workaround to check "#!" when running scripts from Git. As this test requires the platform (not with Git's help) to run scripts without "#!", skipt it on Windows. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- t/t0061-run-command.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 12228b4aa6..98c09dd982 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -26,6 +26,17 @@ test_expect_success 'run_command can run a command' ' test_cmp empty err ' +test_expect_success !MINGW 'run_command can run a script without a #! line' ' + cat >hello <<-\EOF && + cat hello-script + EOF + chmod +x hello && + test-run-command run-command ./hello >actual 2>err && + + test_cmp hello-script actual && + test_cmp empty err +' + test_expect_success POSIXPERM 'run_command reports EACCES' ' cat hello-script >hello.sh && chmod -x hello.sh && From 3967e25be11ab96ced71f16b9f082de270a518db Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:19 -0700 Subject: [PATCH 013/566] run-command: prepare command before forking According to [1] we need to only call async-signal-safe operations between fork and exec. Using malloc to build the argv array isn't async-signal-safe. In order to avoid allocation between 'fork()' and 'exec()' prepare the argv array used in the exec call prior to forking the process. [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/run-command.c b/run-command.c index 574b81d3e8..d8d1437954 100644 --- a/run-command.c +++ b/run-command.c @@ -220,18 +220,6 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv) return out->argv; } -#ifndef GIT_WINDOWS_NATIVE -static int execv_shell_cmd(const char **argv) -{ - struct argv_array nargv = ARGV_ARRAY_INIT; - prepare_shell_cmd(&nargv, argv); - trace_argv_printf(nargv.argv, "trace: exec:"); - sane_execvp(nargv.argv[0], (char **)nargv.argv); - argv_array_clear(&nargv); - return -1; -} -#endif - #ifndef GIT_WINDOWS_NATIVE static int child_notifier = -1; @@ -244,6 +232,21 @@ static void notify_parent(void) */ xwrite(child_notifier, "", 1); } + +static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) +{ + if (!cmd->argv[0]) + die("BUG: command is empty"); + + if (cmd->git_cmd) { + argv_array_push(out, "git"); + argv_array_pushv(out, cmd->argv); + } else if (cmd->use_shell) { + prepare_shell_cmd(out, cmd->argv); + } else { + argv_array_pushv(out, cmd->argv); + } +} #endif static inline void set_cloexec(int fd) @@ -372,9 +375,13 @@ fail_pipe: #ifndef GIT_WINDOWS_NATIVE { int notify_pipe[2]; + struct argv_array argv = ARGV_ARRAY_INIT; + if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; + prepare_cmd(&argv, cmd); + cmd->pid = fork(); failed_errno = errno; if (!cmd->pid) { @@ -437,12 +444,9 @@ fail_pipe: unsetenv(*cmd->env); } } - if (cmd->git_cmd) - execv_git_cmd(cmd->argv); - else if (cmd->use_shell) - execv_shell_cmd(cmd->argv); - else - sane_execvp(cmd->argv[0], (char *const*) cmd->argv); + + sane_execvp(argv.argv[0], (char *const *) argv.argv); + if (errno == ENOENT) { if (!cmd->silent_exec_failure) error("cannot run %s: %s", cmd->argv[0], @@ -458,7 +462,7 @@ fail_pipe: mark_child_for_cleanup(cmd->pid, cmd); /* - * Wait for child's execvp. If the execvp succeeds (or if fork() + * Wait for child's exec. If the exec succeeds (or if fork() * failed), EOF is seen immediately by the parent. Otherwise, the * child process sends a single byte. * Note that use of this infrastructure is completely advisory, @@ -467,7 +471,7 @@ fail_pipe: close(notify_pipe[1]); if (read(notify_pipe[0], ¬ify_pipe[1], 1) == 1) { /* - * At this point we know that fork() succeeded, but execvp() + * At this point we know that fork() succeeded, but exec() * failed. Errors have been reported to our stderr. */ wait_or_whine(cmd->pid, cmd->argv[0], 0); @@ -475,6 +479,8 @@ fail_pipe: cmd->pid = -1; } close(notify_pipe[0]); + + argv_array_clear(&argv); } #else { From e3a434468fecca7c14a6bef32050dfa60534fde6 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:20 -0700 Subject: [PATCH 014/566] run-command: use the async-signal-safe execv instead of execvp Convert the function used to exec from 'execvp()' to 'execv()' as the (p) variant of exec isn't async-signal-safe and has the potential to call malloc during the path resolution it performs. Instead we simply do the path resolution ourselves during the preparation stage prior to forking. There also don't exist any portable (p) variants which also take in an environment to use in the exec'd process. This allows easy migration to using 'execve()' in a future patch. Also, as noted in [1], in the event of an ENOEXEC the (p) variants of exec will attempt to execute the command by interpreting it with the 'sh' utility. To maintain this functionality, if 'execv()' fails with ENOEXEC, start_command will atempt to execute the command by interpreting it with 'sh'. [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index d8d1437954..1c7a3b6110 100644 --- a/run-command.c +++ b/run-command.c @@ -238,6 +238,12 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) if (!cmd->argv[0]) die("BUG: command is empty"); + /* + * Add SHELL_PATH so in the event exec fails with ENOEXEC we can + * attempt to interpret the command with 'sh'. + */ + argv_array_push(out, SHELL_PATH); + if (cmd->git_cmd) { argv_array_push(out, "git"); argv_array_pushv(out, cmd->argv); @@ -246,6 +252,20 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) } else { argv_array_pushv(out, cmd->argv); } + + /* + * If there are no '/' characters in the command then perform a path + * lookup and use the resolved path as the command to exec. If there + * are no '/' characters or if the command wasn't found in the path, + * have exec attempt to invoke the command directly. + */ + if (!strchr(out->argv[1], '/')) { + char *program = locate_in_PATH(out->argv[1]); + if (program) { + free((char *)out->argv[1]); + out->argv[1] = program; + } + } } #endif @@ -445,7 +465,15 @@ fail_pipe: } } - sane_execvp(argv.argv[0], (char *const *) argv.argv); + /* + * Attempt to exec using the command and arguments starting at + * argv.argv[1]. argv.argv[0] contains SHELL_PATH which will + * be used in the event exec failed with ENOEXEC at which point + * we will try to interpret the command using 'sh'. + */ + execv(argv.argv[1], (char *const *) argv.argv + 1); + if (errno == ENOEXEC) + execv(argv.argv[0], (char *const *) argv.argv); if (errno == ENOENT) { if (!cmd->silent_exec_failure) From 3a30033327337323e91dcbcb87396d3245260585 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:21 -0700 Subject: [PATCH 015/566] string-list: add string_list_remove function Teach string-list to be able to remove a string from a sorted 'struct string_list'. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- string-list.c | 18 ++++++++++++++++++ string-list.h | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/string-list.c b/string-list.c index 45016ad86d..8f7b69ada1 100644 --- a/string-list.c +++ b/string-list.c @@ -67,6 +67,24 @@ struct string_list_item *string_list_insert(struct string_list *list, const char return list->items + index; } +void string_list_remove(struct string_list *list, const char *string, + int free_util) +{ + int exact_match; + int i = get_entry_index(list, string, &exact_match); + + if (exact_match) { + if (list->strdup_strings) + free(list->items[i].string); + if (free_util) + free(list->items[i].util); + + list->nr--; + memmove(list->items + i, list->items + i + 1, + (list->nr - i) * sizeof(struct string_list_item)); + } +} + int string_list_has_string(const struct string_list *list, const char *string) { int exact_match; diff --git a/string-list.h b/string-list.h index d3809a1417..29bfb7ae45 100644 --- a/string-list.h +++ b/string-list.h @@ -62,6 +62,13 @@ int string_list_find_insert_index(const struct string_list *list, const char *st */ struct string_list_item *string_list_insert(struct string_list *list, const char *string); +/* + * Removes the given string from the sorted list. + * If the string doesn't exist, the list is not altered. + */ +extern void string_list_remove(struct string_list *list, const char *string, + int free_util); + /* * Checks if the given string is part of a sorted list. If it is part of the list, * return the coresponding string_list_item, NULL otherwise. From ae25394b4c21f072f176ccbaf7047abe7fa5f04d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:22 -0700 Subject: [PATCH 016/566] run-command: prepare child environment before forking In order to avoid allocation between 'fork()' and 'exec()' prepare the environment to be used in the child process prior to forking. Switch to using 'execve()' so that the construct child environment can used in the exec'd process. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 66 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/run-command.c b/run-command.c index 1c7a3b6110..15e2e74a7e 100644 --- a/run-command.c +++ b/run-command.c @@ -267,6 +267,55 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) } } } + +static char **prep_childenv(const char *const *deltaenv) +{ + extern char **environ; + char **childenv; + struct string_list env = STRING_LIST_INIT_DUP; + struct strbuf key = STRBUF_INIT; + const char *const *p; + int i; + + /* Construct a sorted string list consisting of the current environ */ + for (p = (const char *const *) environ; p && *p; p++) { + const char *equals = strchr(*p, '='); + + if (equals) { + strbuf_reset(&key); + strbuf_add(&key, *p, equals - *p); + string_list_append(&env, key.buf)->util = (void *) *p; + } else { + string_list_append(&env, *p)->util = (void *) *p; + } + } + string_list_sort(&env); + + /* Merge in 'deltaenv' with the current environ */ + for (p = deltaenv; p && *p; p++) { + const char *equals = strchr(*p, '='); + + if (equals) { + /* ('key=value'), insert or replace entry */ + strbuf_reset(&key); + strbuf_add(&key, *p, equals - *p); + string_list_insert(&env, key.buf)->util = (void *) *p; + } else { + /* otherwise ('key') remove existing entry */ + string_list_remove(&env, *p, 0); + } + } + + /* Create an array of 'char *' to be used as the childenv */ + childenv = xmalloc((env.nr + 1) * sizeof(char *)); + for (i = 0; i < env.nr; i++) + childenv[i] = env.items[i].util; + childenv[env.nr] = NULL; + + string_list_clear(&env, 0); + strbuf_release(&key); + return childenv; +} #endif static inline void set_cloexec(int fd) @@ -395,12 +444,14 @@ fail_pipe: #ifndef GIT_WINDOWS_NATIVE { int notify_pipe[2]; + char **childenv; struct argv_array argv = ARGV_ARRAY_INIT; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; prepare_cmd(&argv, cmd); + childenv = prep_childenv(cmd->env); cmd->pid = fork(); failed_errno = errno; @@ -456,14 +507,6 @@ fail_pipe: if (cmd->dir && chdir(cmd->dir)) die_errno("exec '%s': cd to '%s' failed", cmd->argv[0], cmd->dir); - if (cmd->env) { - for (; *cmd->env; cmd->env++) { - if (strchr(*cmd->env, '=')) - putenv((char *)*cmd->env); - else - unsetenv(*cmd->env); - } - } /* * Attempt to exec using the command and arguments starting at @@ -471,9 +514,11 @@ fail_pipe: * be used in the event exec failed with ENOEXEC at which point * we will try to interpret the command using 'sh'. */ - execv(argv.argv[1], (char *const *) argv.argv + 1); + execve(argv.argv[1], (char *const *) argv.argv + 1, + (char *const *) childenv); if (errno == ENOEXEC) - execv(argv.argv[0], (char *const *) argv.argv); + execve(argv.argv[0], (char *const *) argv.argv, + (char *const *) childenv); if (errno == ENOENT) { if (!cmd->silent_exec_failure) @@ -509,6 +554,7 @@ fail_pipe: close(notify_pipe[0]); argv_array_clear(&argv); + free(childenv); } #else { From db015a284e74b93db9184d39eb0be749e631242d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:23 -0700 Subject: [PATCH 017/566] run-command: don't die in child when duping /dev/null Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/run-command.c b/run-command.c index 15e2e74a7e..b3a35dd82d 100644 --- a/run-command.c +++ b/run-command.c @@ -117,18 +117,6 @@ static inline void close_pair(int fd[2]) close(fd[1]); } -#ifndef GIT_WINDOWS_NATIVE -static inline void dup_devnull(int to) -{ - int fd = open("/dev/null", O_RDWR); - if (fd < 0) - die_errno(_("open /dev/null failed")); - if (dup2(fd, to) < 0) - die_errno(_("dup2(%d,%d) failed"), fd, to); - close(fd); -} -#endif - static char *locate_in_PATH(const char *file) { const char *p = getenv("PATH"); @@ -444,12 +432,20 @@ fail_pipe: #ifndef GIT_WINDOWS_NATIVE { int notify_pipe[2]; + int null_fd = -1; char **childenv; struct argv_array argv = ARGV_ARRAY_INIT; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; + if (cmd->no_stdin || cmd->no_stdout || cmd->no_stderr) { + null_fd = open("/dev/null", O_RDWR | O_CLOEXEC); + if (null_fd < 0) + die_errno(_("open /dev/null failed")); + set_cloexec(null_fd); + } + prepare_cmd(&argv, cmd); childenv = prep_childenv(cmd->env); @@ -473,7 +469,7 @@ fail_pipe: atexit(notify_parent); if (cmd->no_stdin) - dup_devnull(0); + dup2(null_fd, 0); else if (need_in) { dup2(fdin[0], 0); close_pair(fdin); @@ -483,7 +479,7 @@ fail_pipe: } if (cmd->no_stderr) - dup_devnull(2); + dup2(null_fd, 2); else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); @@ -493,7 +489,7 @@ fail_pipe: } if (cmd->no_stdout) - dup_devnull(1); + dup2(null_fd, 1); else if (cmd->stdout_to_stderr) dup2(2, 1); else if (need_out) { @@ -553,6 +549,8 @@ fail_pipe: } close(notify_pipe[0]); + if (null_fd >= 0) + close(null_fd); argv_array_clear(&argv); free(childenv); } From 79319b1949f0055bd42bac7fa398fca8c2f26116 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:24 -0700 Subject: [PATCH 018/566] run-command: eliminate calls to error handling functions in child All of our standard error handling paths have the potential to call malloc or take stdio locks; so we must avoid them inside the forked child. Instead, the child only writes an 8 byte struct atomically to the parent through the notification pipe to propagate an error. All user-visible error reporting happens from the parent; even avoiding functions like atexit(3) and exit(3). Helped-by: Eric Wong Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 121 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 32 deletions(-) diff --git a/run-command.c b/run-command.c index b3a35dd82d..1f15714b15 100644 --- a/run-command.c +++ b/run-command.c @@ -211,14 +211,82 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv) #ifndef GIT_WINDOWS_NATIVE static int child_notifier = -1; -static void notify_parent(void) +enum child_errcode { + CHILD_ERR_CHDIR, + CHILD_ERR_ENOENT, + CHILD_ERR_SILENT, + CHILD_ERR_ERRNO +}; + +struct child_err { + enum child_errcode err; + int syserr; /* errno */ +}; + +static void child_die(enum child_errcode err) { - /* - * execvp failed. If possible, we'd like to let start_command - * know, so failures like ENOENT can be handled right away; but - * otherwise, finish_command will still report the error. - */ - xwrite(child_notifier, "", 1); + struct child_err buf; + + buf.err = err; + buf.syserr = errno; + + /* write(2) on buf smaller than PIPE_BUF (min 512) is atomic: */ + xwrite(child_notifier, &buf, sizeof(buf)); + _exit(1); +} + +/* + * parent will make it look like the child spewed a fatal error and died + * this is needed to prevent changes to t0061. + */ +static void fake_fatal(const char *err, va_list params) +{ + vreportf("fatal: ", err, params); +} + +static void child_error_fn(const char *err, va_list params) +{ + const char msg[] = "error() should not be called in child\n"; + xwrite(2, msg, sizeof(msg) - 1); +} + +static void child_warn_fn(const char *err, va_list params) +{ + const char msg[] = "warn() should not be called in child\n"; + xwrite(2, msg, sizeof(msg) - 1); +} + +static void NORETURN child_die_fn(const char *err, va_list params) +{ + const char msg[] = "die() should not be called in child\n"; + xwrite(2, msg, sizeof(msg) - 1); + _exit(2); +} + +/* this runs in the parent process */ +static void child_err_spew(struct child_process *cmd, struct child_err *cerr) +{ + static void (*old_errfn)(const char *err, va_list params); + + old_errfn = get_error_routine(); + set_error_routine(fake_fatal); + errno = cerr->syserr; + + switch (cerr->err) { + case CHILD_ERR_CHDIR: + error_errno("exec '%s': cd to '%s' failed", + cmd->argv[0], cmd->dir); + break; + case CHILD_ERR_ENOENT: + error_errno("cannot run %s", cmd->argv[0]); + break; + case CHILD_ERR_SILENT: + break; + case CHILD_ERR_ERRNO: + error_errno("cannot exec '%s'", cmd->argv[0]); + break; + } + set_error_routine(old_errfn); } static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) @@ -341,13 +409,6 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal) code += 128; } else if (WIFEXITED(status)) { code = WEXITSTATUS(status); - /* - * Convert special exit code when execvp failed. - */ - if (code == 127) { - code = -1; - failed_errno = ENOENT; - } } else { error("waitpid is confused (%s)", argv0); } @@ -435,6 +496,7 @@ fail_pipe: int null_fd = -1; char **childenv; struct argv_array argv = ARGV_ARRAY_INIT; + struct child_err cerr; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; @@ -453,20 +515,16 @@ fail_pipe: failed_errno = errno; if (!cmd->pid) { /* - * Redirect the channel to write syscall error messages to - * before redirecting the process's stderr so that all die() - * in subsequent call paths use the parent's stderr. + * Ensure the default die/error/warn routines do not get + * called, they can take stdio locks and malloc. */ - if (cmd->no_stderr || need_err) { - int child_err = dup(2); - set_cloexec(child_err); - set_error_handle(fdopen(child_err, "w")); - } + set_die_routine(child_die_fn); + set_error_routine(child_error_fn); + set_warn_routine(child_warn_fn); close(notify_pipe[0]); set_cloexec(notify_pipe[1]); child_notifier = notify_pipe[1]; - atexit(notify_parent); if (cmd->no_stdin) dup2(null_fd, 0); @@ -501,8 +559,7 @@ fail_pipe: } if (cmd->dir && chdir(cmd->dir)) - die_errno("exec '%s': cd to '%s' failed", cmd->argv[0], - cmd->dir); + child_die(CHILD_ERR_CHDIR); /* * Attempt to exec using the command and arguments starting at @@ -517,12 +574,11 @@ fail_pipe: (char *const *) childenv); if (errno == ENOENT) { - if (!cmd->silent_exec_failure) - error("cannot run %s: %s", cmd->argv[0], - strerror(ENOENT)); - exit(127); + if (cmd->silent_exec_failure) + child_die(CHILD_ERR_SILENT); + child_die(CHILD_ERR_ENOENT); } else { - die_errno("cannot exec '%s'", cmd->argv[0]); + child_die(CHILD_ERR_ERRNO); } } if (cmd->pid < 0) @@ -533,17 +589,18 @@ fail_pipe: /* * Wait for child's exec. If the exec succeeds (or if fork() * failed), EOF is seen immediately by the parent. Otherwise, the - * child process sends a single byte. + * child process sends a child_err struct. * Note that use of this infrastructure is completely advisory, * therefore, we keep error checks minimal. */ close(notify_pipe[1]); - if (read(notify_pipe[0], ¬ify_pipe[1], 1) == 1) { + if (xread(notify_pipe[0], &cerr, sizeof(cerr)) == sizeof(cerr)) { /* * At this point we know that fork() succeeded, but exec() * failed. Errors have been reported to our stderr. */ wait_or_whine(cmd->pid, cmd->argv[0], 0); + child_err_spew(cmd, &cerr); failed_errno = errno; cmd->pid = -1; } From 53fa6753b30c4fb4ea768d16d41d723ea19a3b00 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:25 -0700 Subject: [PATCH 019/566] run-command: handle dup2 and close errors in child Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 58 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/run-command.c b/run-command.c index 1f15714b15..615b6e9c9c 100644 --- a/run-command.c +++ b/run-command.c @@ -213,6 +213,8 @@ static int child_notifier = -1; enum child_errcode { CHILD_ERR_CHDIR, + CHILD_ERR_DUP2, + CHILD_ERR_CLOSE, CHILD_ERR_ENOENT, CHILD_ERR_SILENT, CHILD_ERR_ERRNO @@ -235,6 +237,24 @@ static void child_die(enum child_errcode err) _exit(1); } +static void child_dup2(int fd, int to) +{ + if (dup2(fd, to) < 0) + child_die(CHILD_ERR_DUP2); +} + +static void child_close(int fd) +{ + if (close(fd)) + child_die(CHILD_ERR_CLOSE); +} + +static void child_close_pair(int fd[2]) +{ + child_close(fd[0]); + child_close(fd[1]); +} + /* * parent will make it look like the child spewed a fatal error and died * this is needed to prevent changes to t0061. @@ -277,6 +297,12 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr) error_errno("exec '%s': cd to '%s' failed", cmd->argv[0], cmd->dir); break; + case CHILD_ERR_DUP2: + error_errno("dup2() in child failed"); + break; + case CHILD_ERR_CLOSE: + error_errno("close() in child failed"); + break; case CHILD_ERR_ENOENT: error_errno("cannot run %s", cmd->argv[0]); break; @@ -527,35 +553,35 @@ fail_pipe: child_notifier = notify_pipe[1]; if (cmd->no_stdin) - dup2(null_fd, 0); + child_dup2(null_fd, 0); else if (need_in) { - dup2(fdin[0], 0); - close_pair(fdin); + child_dup2(fdin[0], 0); + child_close_pair(fdin); } else if (cmd->in) { - dup2(cmd->in, 0); - close(cmd->in); + child_dup2(cmd->in, 0); + child_close(cmd->in); } if (cmd->no_stderr) - dup2(null_fd, 2); + child_dup2(null_fd, 2); else if (need_err) { - dup2(fderr[1], 2); - close_pair(fderr); + child_dup2(fderr[1], 2); + child_close_pair(fderr); } else if (cmd->err > 1) { - dup2(cmd->err, 2); - close(cmd->err); + child_dup2(cmd->err, 2); + child_close(cmd->err); } if (cmd->no_stdout) - dup2(null_fd, 1); + child_dup2(null_fd, 1); else if (cmd->stdout_to_stderr) - dup2(2, 1); + child_dup2(2, 1); else if (need_out) { - dup2(fdout[1], 1); - close_pair(fdout); + child_dup2(fdout[1], 1); + child_close_pair(fdout); } else if (cmd->out > 1) { - dup2(cmd->out, 1); - close(cmd->out); + child_dup2(cmd->out, 1); + child_close(cmd->out); } if (cmd->dir && chdir(cmd->dir)) From e503cd6ed336d70d716e194ef6c5469330bea9da Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 19 Apr 2017 16:13:26 -0700 Subject: [PATCH 020/566] run-command: add note about forking and threading All non-Async-Signal-Safe functions (e.g. malloc and die) were removed between 'fork' and 'exec' in start_command in order to avoid potential deadlocking when forking while multiple threads are running. This deadlocking is possible when a thread (other than the one forking) has acquired a lock and didn't get around to releasing it before the fork. This leaves the lock in a locked state in the resulting process with no hope of it ever being released. Add a note describing this potential pitfall before the call to 'fork()' so people working in this section of the code know to only use Async-Signal-Safe functions in the child process. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/run-command.c b/run-command.c index 615b6e9c9c..df1edd963f 100644 --- a/run-command.c +++ b/run-command.c @@ -537,6 +537,15 @@ fail_pipe: prepare_cmd(&argv, cmd); childenv = prep_childenv(cmd->env); + /* + * NOTE: In order to prevent deadlocking when using threads special + * care should be taken with the function calls made in between the + * fork() and exec() calls. No calls should be made to functions which + * require acquiring a lock (e.g. malloc) as the lock could have been + * held by another thread at the time of forking, causing the lock to + * never be released in the child process. This means only + * Async-Signal-Safe functions are permitted in the child. + */ cmd->pid = fork(); failed_errno = errno; if (!cmd->pid) { From 45afb1ca9c28855096c94926e5b16dfbcde7381f Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 19 Apr 2017 16:13:27 -0700 Subject: [PATCH 021/566] run-command: block signals between fork and execve Signal handlers of the parent firing in the forked child may have unintended side effects. Rather than auditing every signal handler we have and will ever have, block signals while forking and restore default signal handlers in the child before execve. Restoring default signal handlers is required because execve does not unblock signals, it only restores default signal handlers. So we must restore them with sigprocmask before execve, leaving a window when signal handlers we control can fire in the child. Continue ignoring ignored signals, but reset the rest to defaults. Similarly, disable pthread cancellation to future-proof our code in case we start using cancellation; as cancellation is implemented with signals in glibc. Signed-off-by: Eric Wong Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- run-command.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/run-command.c b/run-command.c index df1edd963f..a97d7bf9f3 100644 --- a/run-command.c +++ b/run-command.c @@ -215,6 +215,7 @@ enum child_errcode { CHILD_ERR_CHDIR, CHILD_ERR_DUP2, CHILD_ERR_CLOSE, + CHILD_ERR_SIGPROCMASK, CHILD_ERR_ENOENT, CHILD_ERR_SILENT, CHILD_ERR_ERRNO @@ -303,6 +304,9 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr) case CHILD_ERR_CLOSE: error_errno("close() in child failed"); break; + case CHILD_ERR_SIGPROCMASK: + error_errno("sigprocmask failed restoring signals"); + break; case CHILD_ERR_ENOENT: error_errno("cannot run %s", cmd->argv[0]); break; @@ -398,7 +402,54 @@ static char **prep_childenv(const char *const *deltaenv) strbuf_release(&key); return childenv; } + +struct atfork_state { +#ifndef NO_PTHREADS + int cs; #endif + sigset_t old; +}; + +#ifndef NO_PTHREADS +static void bug_die(int err, const char *msg) +{ + if (err) { + errno = err; + die_errno("BUG: %s", msg); + } +} +#endif + +static void atfork_prepare(struct atfork_state *as) +{ + sigset_t all; + + if (sigfillset(&all)) + die_errno("sigfillset"); +#ifdef NO_PTHREADS + if (sigprocmask(SIG_SETMASK, &all, &as->old)) + die_errno("sigprocmask"); +#else + bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old), + "blocking all signals"); + bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs), + "disabling cancellation"); +#endif +} + +static void atfork_parent(struct atfork_state *as) +{ +#ifdef NO_PTHREADS + if (sigprocmask(SIG_SETMASK, &as->old, NULL)) + die_errno("sigprocmask"); +#else + bug_die(pthread_setcancelstate(as->cs, NULL), + "re-enabling cancellation"); + bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL), + "restoring signal mask"); +#endif +} +#endif /* GIT_WINDOWS_NATIVE */ static inline void set_cloexec(int fd) { @@ -523,6 +574,7 @@ fail_pipe: char **childenv; struct argv_array argv = ARGV_ARRAY_INIT; struct child_err cerr; + struct atfork_state as; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; @@ -536,6 +588,7 @@ fail_pipe: prepare_cmd(&argv, cmd); childenv = prep_childenv(cmd->env); + atfork_prepare(&as); /* * NOTE: In order to prevent deadlocking when using threads special @@ -549,6 +602,7 @@ fail_pipe: cmd->pid = fork(); failed_errno = errno; if (!cmd->pid) { + int sig; /* * Ensure the default die/error/warn routines do not get * called, they can take stdio locks and malloc. @@ -596,6 +650,19 @@ fail_pipe: if (cmd->dir && chdir(cmd->dir)) child_die(CHILD_ERR_CHDIR); + /* + * restore default signal handlers here, in case + * we catch a signal right before execve below + */ + for (sig = 1; sig < NSIG; sig++) { + /* ignored signals get reset to SIG_DFL on execve */ + if (signal(sig, SIG_DFL) == SIG_IGN) + signal(sig, SIG_IGN); + } + + if (sigprocmask(SIG_SETMASK, &as.old, NULL) != 0) + child_die(CHILD_ERR_SIGPROCMASK); + /* * Attempt to exec using the command and arguments starting at * argv.argv[1]. argv.argv[0] contains SHELL_PATH which will @@ -616,6 +683,7 @@ fail_pipe: child_die(CHILD_ERR_ERRNO); } } + atfork_parent(&as); if (cmd->pid < 0) error_errno("cannot fork() for %s", cmd->argv[0]); else if (cmd->clean_on_exit) From be4ca290570f9173db64ea1f925b5b3831c6efed Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 20 Apr 2017 16:41:18 -0400 Subject: [PATCH 022/566] Increase core.packedGitLimit When core.packedGitLimit is exceeded, git will close packs. If there is a repack operation going on in parallel with a fetch, the fetch might open a pack, and then be forced to close it due to packedGitLimit being hit. The repack could then delete the pack out from under the fetch, causing the fetch to fail. Increase core.packedGitLimit's default value to prevent this. On current 64-bit x86_64 machines, 48 bits of address space are available. It appears that 64-bit ARM machines have no standard amount of address space (that is, it varies by manufacturer), and IA64 and POWER machines have the full 64 bits. So 48 bits is the only limit that we can reasonably care about. We reserve a few bits of the 48-bit address space for the kernel's use (this is not strictly necessary, but it's better to be safe), and use up to the remaining 45. No git repository will be anywhere near this large any time soon, so this should prevent the failure. Helped-by: Jeff King Signed-off-by: David Turner Signed-off-by: Junio C Hamano --- git-compat-util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-compat-util.h b/git-compat-util.h index 8a4a3f85e7..1c5de153a5 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -616,7 +616,7 @@ extern int git_lstat(const char *, struct stat *); #endif #define DEFAULT_PACKED_GIT_LIMIT \ - ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? 8192 : 256)) + ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256)) #ifdef NO_PREAD #define pread git_pread From e467dc148d0375ed83300589281f749fcbdd690d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 20 Apr 2017 22:52:09 +0200 Subject: [PATCH 023/566] ref-filter: avoid using `unsigned long` for catch-all data type In its `atom_value` struct, the ref-filter source code wants to store different values in a field called `ul` (for `unsigned long`), e.g. timestamps. However, as we are about to switch the data type of timestamps away from `unsigned long` (because it may be 32-bit even when `time_t` is 64-bit), that data type is not large enough. Simply change that field to use `uintmax_t` instead. This patch is a bit larger than the mere change of the data type because the field's name was tied to its data type, which has been fixed at the same time. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- ref-filter.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ref-filter.c b/ref-filter.c index 3a640448fd..9287126600 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -351,7 +351,7 @@ struct ref_formatting_state { struct atom_value { const char *s; void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); - unsigned long ul; /* used for sorting when not FIELD_STR */ + uintmax_t value; /* used for sorting when not FIELD_STR */ struct used_atom *atom; }; @@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object if (!strcmp(name, "objecttype")) v->s = typename(obj->type); else if (!strcmp(name, "objectsize")) { - v->ul = sz; + v->value = sz; v->s = xstrfmt("%lu", sz); } else if (deref) @@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object v->s = xstrdup(oid_to_hex(&commit->tree->object.oid)); } else if (!strcmp(name, "numparent")) { - v->ul = commit_list_count(commit->parents); - v->s = xstrfmt("%lu", v->ul); + v->value = commit_list_count(commit->parents); + v->s = xstrfmt("%lu", (unsigned long)v->value); } else if (!strcmp(name, "parent")) { struct commit_list *parents; @@ -875,11 +875,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) goto bad; v->s = xstrdup(show_date(timestamp, tz, &date_mode)); - v->ul = timestamp; + v->value = timestamp; return; bad: v->s = ""; - v->ul = 0; + v->value = 0; } /* See grab_values */ @@ -1941,9 +1941,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru else if (cmp_type == FIELD_STR) cmp = cmp_fn(va->s, vb->s); else { - if (va->ul < vb->ul) + if (va->value < vb->value) cmp = -1; - else if (va->ul == vb->ul) + else if (va->value == vb->value) cmp = cmp_fn(a->refname, b->refname); else cmp = 1; From a07fb0507fdf745704e54d77aa19780580636f56 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 20 Apr 2017 22:52:13 +0200 Subject: [PATCH 024/566] t0006 & t5000: prepare for 64-bit timestamps Git's source code refers to timestamps as unsigned longs. On 32-bit platforms, as well as on Windows, unsigned long is not large enough to capture dates that are "absurdly far in the future". It is perfectly valid by the C standard, of course, for the `long` data type to refer to 32-bit integers. That is why the `time_t` data type exists: so that it can be 64-bit even if `long` is 32-bit. Git's source code simply uses an incorrect data type for timestamps, is all. The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test when unsigned long is not long enough, 2016-07-11) papered over this issue simply by skipping the respective test cases on platforms where they would fail due to the data type in use. This quick fix, however, tests for *long* to be 64-bit or not. What we need, though, is a test that says whether *whatever data type we use for timestamps* is 64-bit or not. The same quick fix was used to handle the similar problem where Git's source code uses `unsigned long` to represent size, instead of `size_t`, conflating the two issues. So let's just add another prerequisite to test specifically whether timestamps are represented by a 64-bit data type or not. Later, after we switch to a larger data type, we can flip that prerequisite to test `time_t` instead of `long`. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/helper/test-date.c | 5 ++++- t/t0006-date.sh | 4 ++-- t/t5000-tar-tree.sh | 6 +++--- t/test-lib.sh | 2 ++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/t/helper/test-date.c b/t/helper/test-date.c index 506054bcd5..4727bea255 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -4,7 +4,8 @@ static const char *usage_msg = "\n" " test-date relative [time_t]...\n" " test-date show: [time_t]...\n" " test-date parse [date]...\n" -" test-date approxidate [date]...\n"; +" test-date approxidate [date]...\n" +" test-date is64bit\n"; static void show_relative_dates(const char **argv, struct timeval *now) { @@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv) parse_dates(argv+1, &now); else if (!strcmp(*argv, "approxidate")) parse_approxidate(argv+1, &now); + else if (!strcmp(*argv, "is64bit")) + return sizeof(unsigned long) == 8 ? 0 : 1; else usage(usage_msg); return 0; diff --git a/t/t0006-date.sh b/t/t0006-date.sh index c0c910867d..9539b425ff 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000' # arbitrary time absurdly far in the future FUTURE="5758122296 -0400" -check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT -check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT +check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT +check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT check_parse() { echo "$1 -> $2" >expect diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 886b6953e4..997aa9dea2 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' ' test_cmp expect actual ' -test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' ' +test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' ' rm -f .git/index && echo content >file && git add file && @@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' ' git commit -m "tempori parendum" ' -test_expect_success LONG_IS_64BIT 'generate tar with future mtime' ' +test_expect_success TIME_IS_64BIT 'generate tar with future mtime' ' git archive HEAD >future.tar ' -test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' ' +test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' ' echo 4147 >expect && tar_info future.tar | cut -d" " -f2 >actual && test_cmp expect actual diff --git a/t/test-lib.sh b/t/test-lib.sh index 13b5696822..beee1d847f 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1164,3 +1164,5 @@ build_option () { test_lazy_prereq LONG_IS_64BIT ' test 8 -le "$(build_option sizeof-long)" ' + +test_lazy_prereq TIME_IS_64BIT 'test-date is64bit' From efac8ac84b8802d654d517468dbe822273b316df Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 20 Apr 2017 22:58:21 +0200 Subject: [PATCH 025/566] t0006 & t5000: skip "far in the future" test when time_t is too limited Git's source code refers to timestamps as unsigned long, which is ill-defined, as there is no guarantee about the number of bits that data type has. In preparation of switching to another data type that is large enough to hold "far in the future" dates, we need to prepare the t0006-date.sh script for the case where we *still* cannot format those dates if the system library uses 32-bit time_t. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/helper/test-date.c | 5 ++++- t/t0006-date.sh | 4 ++-- t/t5000-tar-tree.sh | 2 +- t/test-lib.sh | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/t/helper/test-date.c b/t/helper/test-date.c index 4727bea255..ac7c66c733 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -5,7 +5,8 @@ static const char *usage_msg = "\n" " test-date show: [time_t]...\n" " test-date parse [date]...\n" " test-date approxidate [date]...\n" -" test-date is64bit\n"; +" test-date is64bit\n" +" test-date time_t-is64bit\n"; static void show_relative_dates(const char **argv, struct timeval *now) { @@ -96,6 +97,8 @@ int cmd_main(int argc, const char **argv) parse_approxidate(argv+1, &now); else if (!strcmp(*argv, "is64bit")) return sizeof(unsigned long) == 8 ? 0 : 1; + else if (!strcmp(*argv, "time_t-is64bit")) + return sizeof(time_t) == 8 ? 0 : 1; else usage(usage_msg); return 0; diff --git a/t/t0006-date.sh b/t/t0006-date.sh index 9539b425ff..42d4ea61ef 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000' # arbitrary time absurdly far in the future FUTURE="5758122296 -0400" -check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT -check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT +check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT +check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT check_parse() { echo "$1 -> $2" >expect diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 997aa9dea2..fe2d4f15a7 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' ' git archive HEAD >future.tar ' -test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' ' +test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' ' echo 4147 >expect && tar_info future.tar | cut -d" " -f2 >actual && test_cmp expect actual diff --git a/t/test-lib.sh b/t/test-lib.sh index beee1d847f..8d25cb7c18 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT ' ' test_lazy_prereq TIME_IS_64BIT 'test-date is64bit' +test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit' From df4c0d1a7927e17e7944ec24fa94468eee979f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 20 Apr 2017 18:52:30 +0200 Subject: [PATCH 026/566] test-lib: abort when can't remove trash directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We had two similar bugs in the tests sporadically triggering error messages during the removal of the trash directory, see commits bb05510e5 (t5510: run auto-gc in the foreground, 2016-05-01) and ef09036cf (t6500: wait for detached auto gc at the end of the test script, 2017-04-13). The test script succeeded nonetheless, because these errors are ignored during housekeeping in 'test_done'. However, such an error is a sign that something is fishy in the test script. Print an error message and abort the test script when the trash directory can't be removed successfully or is already removed, because that's unexpected and we would prefer somebody notice and figure out why. Signed-off-by: SZEDER Gábor Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- t/test-lib.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index cde7fc7fcf..cb0766b9ee 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -760,9 +760,12 @@ test_done () { say "1..$test_count$skip_all" fi - test -d "$remove_trash" && + test -d "$remove_trash" || + error "Tests passed but trash directory already removed before test cleanup; aborting" + cd "$(dirname "$remove_trash")" && - rm -rf "$(basename "$remove_trash")" + rm -rf "$(basename "$remove_trash")" || + error "Tests passed but test cleanup failed; aborting" test_at_end_hook_ From 35b96d1de8e795aeac8ec281e88e59f2778335e7 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 21 Apr 2017 10:39:53 -0700 Subject: [PATCH 027/566] builtin/reset: add --recurse-submodules switch git-reset is yet another working tree manipulator, which should be taught about submodules. When a user uses git-reset and requests to recurse into submodules, this will reset the submodules to the object name as recorded in the superproject, detaching the HEADs. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/reset.c | 30 ++++++++++++++++++++++++++++++ t/t7112-reset-submodule.sh | 8 ++++++++ 2 files changed, 38 insertions(+) diff --git a/builtin/reset.c b/builtin/reset.c index fc3b906c47..5ce27fcaed 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -21,6 +21,27 @@ #include "parse-options.h" #include "unpack-trees.h" #include "cache-tree.h" +#include "submodule.h" +#include "submodule-config.h" + +static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; + +static int option_parse_recurse_submodules(const struct option *opt, + const char *arg, int unset) +{ + if (unset) { + recurse_submodules = RECURSE_SUBMODULES_OFF; + return 0; + } + if (arg) + recurse_submodules = + parse_update_recurse_submodules_arg(opt->long_name, + arg); + else + recurse_submodules = RECURSE_SUBMODULES_ON; + + return 0; +} static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] []"), @@ -283,6 +304,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix) N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), + { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, + "reset", "control recursive updating of submodules", + PARSE_OPT_OPTARG, option_parse_recurse_submodules }, OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), @@ -295,6 +319,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); + if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) { + gitmodules_config(); + git_config(submodule_config, NULL); + set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON); + } + unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh index 2eda6adeb1..f86ccdf215 100755 --- a/t/t7112-reset-submodule.sh +++ b/t/t7112-reset-submodule.sh @@ -5,6 +5,14 @@ test_description='reset can handle submodules' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh +KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 +KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 +KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1 + +test_submodule_switch_recursing "git reset --recurse-submodules --keep" + +test_submodule_forced_switch_recursing "git reset --hard --recurse-submodules" + test_submodule_switch "git reset --keep" test_submodule_switch "git reset --merge" From a96d3cc3f65285f7d51c30fb3f630202b5347a57 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 21 Apr 2017 14:46:17 -0400 Subject: [PATCH 028/566] cache-tree: reject entries with null sha1 We generally disallow null sha1s from entering the index, due to 4337b5856 (do not write null sha1s to on-disk index, 2012-07-28). However, we loosened that in 83bd7437c (write_index: optionally allow broken null sha1s, 2013-08-27) so that tools like filter-branch could be used to repair broken history. However, we should make sure that these broken entries do not get propagated into new trees. For most entries, we'd catch them with the missing-object check (since presumably the null sha1 does not exist in our object database). But gitlink entries do not need reachability, so we may blindly copy the entry into a bogus tree. This patch rejects all null sha1s (with the same "invalid entry" message that missing objects get) when building trees from the index. It does so even for non-gitlinks, and even when "write-tree" is given the --missing-ok flag. The null sha1 is a special sentinel value that is already rejected in trees by fsck; whether the object exists or not, it is an error to put it in a tree. Note that for this to work, we must also avoid reusing an existing cache-tree that contains the null sha1. This patch does so by just refusing to write out any cache tree when the index contains a null sha1. This is blunter than we need to be; we could just reject the subtree that contains the offending entry. But it's not worth the complexity. The behavior is unchanged unless you have a broken index entry, and even then we'd refuse the whole index write unless the emergency GIT_ALLOW_NULL_SHA1 is in use. And even then the end result is only a performance drop (any write-tree will have to generate the whole cache-tree from scratch). The tests bear some explanation. The existing test in t7009 doesn't catch this problem, because our index-filter runs "git rm --cached", which will try to rewrite the updated index and barf on the bogus entry. So we never even make it to write-tree. The new test there adds a noop index-filter, which does show the problem. The new tests in t1601 are slightly redundant with what filter-branch is doing under the hood in t7009. But as they're much more direct, they're easier to reason about. And should filter-branch ever change or go away, we'd want to make sure that these plumbing commands behave sanely. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache-tree.c | 4 +++- read-cache.c | 5 ++++- t/t1601-index-bogus.sh | 22 ++++++++++++++++++++++ t/t7009-filter-branch-null-sha1.sh | 6 ++++++ 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100755 t/t1601-index-bogus.sh diff --git a/cache-tree.c b/cache-tree.c index 345ea35963..34baa6d85a 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -354,7 +354,9 @@ static int update_one(struct cache_tree *it, entlen = pathlen - baselen; i++; } - if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) { + + if (is_null_sha1(sha1) || + (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))) { strbuf_release(&buffer); if (expected_missing) return -1; diff --git a/read-cache.c b/read-cache.c index 9054369dd0..2ca2cae617 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2021,6 +2021,7 @@ static int do_write_index(struct index_state *istate, int newfd, int entries = istate->cache_nr; struct stat st; struct strbuf previous_name_buf = STRBUF_INIT, *previous_name; + int drop_cache_tree = 0; for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) @@ -2071,6 +2072,8 @@ static int do_write_index(struct index_state *istate, int newfd, warning(msg, ce->name); else return error(msg, ce->name); + + drop_cache_tree = 1; } if (ce_write_entry(&c, newfd, ce, previous_name) < 0) return -1; @@ -2089,7 +2092,7 @@ static int do_write_index(struct index_state *istate, int newfd, if (err) return -1; } - if (!strip_extensions && istate->cache_tree) { + if (!strip_extensions && !drop_cache_tree && istate->cache_tree) { struct strbuf sb = STRBUF_INIT; cache_tree_write(&sb, istate->cache_tree); diff --git a/t/t1601-index-bogus.sh b/t/t1601-index-bogus.sh new file mode 100755 index 0000000000..73cc9323cd --- /dev/null +++ b/t/t1601-index-bogus.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +test_description='test handling of bogus index entries' +. ./test-lib.sh + +test_expect_success 'create tree with null sha1' ' + tree=$(printf "160000 commit $_z40\\tbroken\\n" | git mktree) +' + +test_expect_success 'read-tree refuses to read null sha1' ' + test_must_fail git read-tree $tree +' + +test_expect_success 'GIT_ALLOW_NULL_SHA1 overrides refusal' ' + GIT_ALLOW_NULL_SHA1=1 git read-tree $tree +' + +test_expect_success 'git write-tree refuses to write null sha1' ' + test_must_fail git write-tree +' + +test_done diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh index c27f90f285..a8d9ec4987 100755 --- a/t/t7009-filter-branch-null-sha1.sh +++ b/t/t7009-filter-branch-null-sha1.sh @@ -31,6 +31,12 @@ test_expect_success 'setup: bring HEAD and index in sync' ' git commit -a -m "back to normal" ' +test_expect_success 'noop filter-branch complains' ' + test_must_fail git filter-branch \ + --force --prune-empty \ + --index-filter "true" +' + test_expect_success 'filter commands are still checked' ' test_must_fail git filter-branch \ --force --prune-empty \ From 1aeb7e756c82d31e46712ec7557c4cbae37dccd9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 21 Apr 2017 12:45:44 +0200 Subject: [PATCH 029/566] parse_timestamp(): specify explicitly where we parse timestamps Currently, Git's source code represents all timestamps as `unsigned long`. In preparation for using a more appropriate data type, let's introduce a symbol `parse_timestamp` (currently being defined to `strtoul`) where appropriate, so that we can later easily switch to, say, use `strtoull()` instead. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/am.c | 2 +- builtin/receive-pack.c | 4 ++-- bundle.c | 2 +- commit.c | 6 +++--- date.c | 6 +++--- fsck.c | 2 +- git-compat-util.h | 2 ++ pretty.c | 2 +- ref-filter.c | 2 +- refs/files-backend.c | 2 +- t/helper/test-date.c | 2 +- tag.c | 4 ++-- upload-pack.c | 2 +- 13 files changed, 20 insertions(+), 18 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index f7a7a971fb..2c93adc69c 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -882,7 +882,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr) char *end; errno = 0; - timestamp = strtoul(str, &end, 10); + timestamp = parse_timestamp(str, &end, 10); if (errno) return error(_("invalid timestamp")); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 3cba3fd278..9a4c2a7ade 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len) retval = NONCE_BAD; goto leave; } - stamp = strtoul(nonce, &bohmac, 10); + stamp = parse_timestamp(nonce, &bohmac, 10); if (bohmac == nonce || bohmac[0] != '-') { retval = NONCE_BAD; goto leave; @@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len) * would mean it was issued by another server with its clock * skewed in the future. */ - ostamp = strtoul(push_cert_nonce, NULL, 10); + ostamp = parse_timestamp(push_cert_nonce, NULL, 10); nonce_stamp_slop = (long)ostamp - (long)stamp; if (nonce_stamp_slop_limit && diff --git a/bundle.c b/bundle.c index bbf4efa0a0..f43bfcf5ff 100644 --- a/bundle.c +++ b/bundle.c @@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs) line = memchr(line, '>', lineend ? lineend - line : buf + size - line); if (!line++) goto out; - date = strtoul(line, NULL, 10); + date = parse_timestamp(line, NULL, 10); result = (revs->max_age == -1 || revs->max_age < date) && (revs->min_age == -1 || revs->min_age > date); out: diff --git a/commit.c b/commit.c index 73c78c2b80..0d2d0fa198 100644 --- a/commit.c +++ b/commit.c @@ -89,8 +89,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail) /* nada */; if (buf >= tail) return 0; - /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */ - return strtoul(dateptr, NULL, 10); + /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */ + return parse_timestamp(dateptr, NULL, 10); } static struct commit_graft **commit_graft; @@ -607,7 +607,7 @@ static void record_author_date(struct author_date_slab *author_date, !ident.date_begin || !ident.date_end) goto fail_exit; /* malformed "author" line */ - date = strtoul(ident.date_begin, &date_end, 10); + date = parse_timestamp(ident.date_begin, &date_end, 10); if (date_end != ident.date_end) goto fail_exit; /* malformed date */ *(author_date_slab_at(author_date, commit)) = date; diff --git a/date.c b/date.c index a996331f5b..495c207c64 100644 --- a/date.c +++ b/date.c @@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt char *end; unsigned long num; - num = strtoul(date, &end, 10); + num = parse_timestamp(date, &end, 10); /* * Seconds since 1970? We trigger on that for any numbers with @@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp, if (*date < '0' || '9' < *date) return -1; - stamp = strtoul(date, &end, 10); + stamp = parse_timestamp(date, &end, 10); if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-')) return -1; date = end + 2; @@ -1066,7 +1066,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num, time_t now) { char *end; - unsigned long number = strtoul(date, &end, 10); + unsigned long number = parse_timestamp(date, &end, 10); switch (*end) { case ':': diff --git a/fsck.c b/fsck.c index e6152e4e6d..d589341cdd 100644 --- a/fsck.c +++ b/fsck.c @@ -691,7 +691,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option p++; if (*p == '0' && p[1] != ' ') return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date"); - if (date_overflows(strtoul(p, &end, 10))) + if (date_overflows(parse_timestamp(p, &end, 10))) return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow"); if ((end == p || *end != ' ')) return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date"); diff --git a/git-compat-util.h b/git-compat-util.h index 8a4a3f85e7..fc1b5fe1a6 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -319,6 +319,8 @@ extern char *gitdirname(char *); #define PRIo32 "o" #endif +#define parse_timestamp strtoul + #ifndef PATH_SEP #define PATH_SEP ':' #endif diff --git a/pretty.c b/pretty.c index d0f86f5d85..24fb0c7906 100644 --- a/pretty.c +++ b/pretty.c @@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident, long tz = 0; if (ident->date_begin && ident->date_end) - date = strtoul(ident->date_begin, NULL, 10); + date = parse_timestamp(ident->date_begin, NULL, 10); if (date_overflows(date)) date = 0; else { diff --git a/ref-filter.c b/ref-filter.c index 9287126600..c7836ae07b 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -868,7 +868,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam if (!eoemail) goto bad; - timestamp = strtoul(eoemail + 2, &zone, 10); + timestamp = parse_timestamp(eoemail + 2, &zone, 10); if (timestamp == ULONG_MAX) goto bad; tz = strtol(zone, NULL, 10); diff --git a/refs/files-backend.c b/refs/files-backend.c index 4d705b4037..dae0522673 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3247,7 +3247,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c parse_oid_hex(p, &noid, &p) || *p++ != ' ' || !(email_end = strchr(p, '>')) || email_end[1] != ' ' || - !(timestamp = strtoul(email_end + 2, &message, 10)) || + !(timestamp = parse_timestamp(email_end + 2, &message, 10)) || !message || message[0] != ' ' || (message[1] != '+' && message[1] != '-') || !isdigit(message[2]) || !isdigit(message[3]) || diff --git a/t/helper/test-date.c b/t/helper/test-date.c index ac7c66c733..52d1fc3445 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -34,7 +34,7 @@ static void show_dates(const char **argv, const char *format) * Do not use our normal timestamp parsing here, as the point * is to test the formatting code in isolation. */ - t = strtol(*argv, &arg, 10); + t = parse_timestamp(*argv, &arg, 10); while (*arg == ' ') arg++; tz = atoi(arg); diff --git a/tag.c b/tag.c index 243d1fdbbc..9b6725e02c 100644 --- a/tag.c +++ b/tag.c @@ -110,8 +110,8 @@ static unsigned long parse_tag_date(const char *buf, const char *tail) /* nada */; if (buf >= tail) return 0; - /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */ - return strtoul(dateptr, NULL, 10); + /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */ + return parse_timestamp(dateptr, NULL, 10); } int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) diff --git a/upload-pack.c b/upload-pack.c index ffb028d623..f17f4dd123 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -775,7 +775,7 @@ static void receive_needs(void) } if (skip_prefix(line, "deepen-since ", &arg)) { char *end = NULL; - deepen_since = strtoul(arg, &end, 0); + deepen_since = parse_timestamp(arg, &end, 0); if (!end || *end || !deepen_since || /* revisions.c's max_age -1 is special */ deepen_since == -1) From cb71f8bdb5a105cd5b66142b887989d9addc82d0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 21 Apr 2017 12:45:48 +0200 Subject: [PATCH 030/566] PRItime: introduce a new "printf format" for timestamps Currently, Git's source code treats all timestamps as if they were unsigned longs. Therefore, it is okay to write "%lu" when printing them. There is a substantial problem with that, though: at least on Windows, time_t is *larger* than unsigned long, and hence we will want to switch away from the ill-specified `unsigned long` data type. So let's introduce the pseudo format "PRItime" (currently simply being defined to "lu") to make it easier to change the data type used for timestamps. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/blame.c | 6 +++--- builtin/fsck.c | 2 +- builtin/log.c | 2 +- builtin/receive-pack.c | 4 ++-- builtin/rev-list.c | 2 +- builtin/rev-parse.c | 2 +- date.c | 26 +++++++++++++------------- fetch-pack.c | 2 +- git-compat-util.h | 1 + refs/files-backend.c | 2 +- t/helper/test-date.c | 2 +- t/helper/test-parse-options.c | 2 +- t/helper/test-ref-store.c | 2 +- upload-pack.c | 2 +- vcs-svn/fast_export.c | 4 ++-- 15 files changed, 31 insertions(+), 30 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 07506a3e45..e4b3c7b0eb 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1727,11 +1727,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat) get_commit_info(suspect->commit, &ci, 1); printf("author %s\n", ci.author.buf); printf("author-mail %s\n", ci.author_mail.buf); - printf("author-time %lu\n", ci.author_time); + printf("author-time %"PRItime"\n", ci.author_time); printf("author-tz %s\n", ci.author_tz.buf); printf("committer %s\n", ci.committer.buf); printf("committer-mail %s\n", ci.committer_mail.buf); - printf("committer-time %lu\n", ci.committer_time); + printf("committer-time %"PRItime"\n", ci.committer_time); printf("committer-tz %s\n", ci.committer_tz.buf); printf("summary %s\n", ci.summary.buf); if (suspect->commit->object.flags & UNINTERESTING) @@ -1844,7 +1844,7 @@ static const char *format_time(unsigned long time, const char *tz_str, strbuf_reset(&time_buf); if (show_raw_time) { - strbuf_addf(&time_buf, "%lu %s", time, tz_str); + strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str); } else { const char *time_str; diff --git a/builtin/fsck.c b/builtin/fsck.c index f76e4163ab..af7b985c6e 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, if (timestamp && name_objects) add_decoration(fsck_walk_options.object_names, obj, - xstrfmt("%s@{%ld}", refname, timestamp)); + xstrfmt("%s@{%"PRItime"}", refname, timestamp)); obj->used = 1; mark_object_reachable(obj); } else { diff --git a/builtin/log.c b/builtin/log.c index b3b10cc1ed..f93ef6c710 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -910,7 +910,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) static void gen_message_id(struct rev_info *info, char *base) { struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "%s.%lu.git.%s", base, + strbuf_addf(&buf, "%s.%"PRItime".git.%s", base, (unsigned long) time(NULL), git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT)); info->message_id = strbuf_detach(&buf, NULL); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 9a4c2a7ade..c49333c9b6 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -459,12 +459,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp) struct strbuf buf = STRBUF_INIT; unsigned char sha1[20]; - strbuf_addf(&buf, "%s:%lu", path, stamp); + strbuf_addf(&buf, "%s:%"PRItime, path, stamp); hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));; strbuf_release(&buf); /* RFC 2104 5. HMAC-SHA1-80 */ - strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1)); + strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1)); return strbuf_detach(&buf, NULL); } diff --git a/builtin/rev-list.c b/builtin/rev-list.c index bcf77f0b8a..3b292c99bd 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data) } if (info->show_timestamp) - printf("%lu ", commit->date); + printf("%"PRItime" ", commit->date); if (info->header_prefix) fputs(info->header_prefix, stdout); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 0513330910..b450900243 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -218,7 +218,7 @@ static void show_datestring(const char *flag, const char *datestr) /* date handling requires both flags and revs */ if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS)) return; - buffer = xstrfmt("%s%lu", flag, approxidate(datestr)); + buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr)); show(buffer); free(buffer); } diff --git a/date.c b/date.c index 495c207c64..5c33dfd8ee 100644 --- a/date.c +++ b/date.c @@ -100,41 +100,41 @@ void show_date_relative(unsigned long time, int tz, diff = now->tv_sec - time; if (diff < 90) { strbuf_addf(timebuf, - Q_("%lu second ago", "%lu seconds ago", diff), diff); + Q_("%"PRItime" second ago", "%"PRItime" seconds ago", diff), diff); return; } /* Turn it into minutes */ diff = (diff + 30) / 60; if (diff < 90) { strbuf_addf(timebuf, - Q_("%lu minute ago", "%lu minutes ago", diff), diff); + Q_("%"PRItime" minute ago", "%"PRItime" minutes ago", diff), diff); return; } /* Turn it into hours */ diff = (diff + 30) / 60; if (diff < 36) { strbuf_addf(timebuf, - Q_("%lu hour ago", "%lu hours ago", diff), diff); + Q_("%"PRItime" hour ago", "%"PRItime" hours ago", diff), diff); return; } /* We deal with number of days from here on */ diff = (diff + 12) / 24; if (diff < 14) { strbuf_addf(timebuf, - Q_("%lu day ago", "%lu days ago", diff), diff); + Q_("%"PRItime" day ago", "%"PRItime" days ago", diff), diff); return; } /* Say weeks for the past 10 weeks or so */ if (diff < 70) { strbuf_addf(timebuf, - Q_("%lu week ago", "%lu weeks ago", (diff + 3) / 7), + Q_("%"PRItime" week ago", "%"PRItime" weeks ago", (diff + 3) / 7), (diff + 3) / 7); return; } /* Say months for the past 12 months or so */ if (diff < 365) { strbuf_addf(timebuf, - Q_("%lu month ago", "%lu months ago", (diff + 15) / 30), + Q_("%"PRItime" month ago", "%"PRItime" months ago", (diff + 15) / 30), (diff + 15) / 30); return; } @@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz, unsigned long months = totalmonths % 12; if (months) { struct strbuf sb = STRBUF_INIT; - strbuf_addf(&sb, Q_("%lu year", "%lu years", years), years); + strbuf_addf(&sb, Q_("%"PRItime" year", "%"PRItime" years", years), years); strbuf_addf(timebuf, /* TRANSLATORS: "%s" is " years" */ - Q_("%s, %lu month ago", "%s, %lu months ago", months), + Q_("%s, %"PRItime" month ago", "%s, %"PRItime" months ago", months), sb.buf, months); strbuf_release(&sb); } else strbuf_addf(timebuf, - Q_("%lu year ago", "%lu years ago", years), years); + Q_("%"PRItime" year ago", "%"PRItime" years ago", years), years); return; } /* Otherwise, just years. Centuries is probably overkill. */ strbuf_addf(timebuf, - Q_("%lu year ago", "%lu years ago", (diff + 183) / 365), + Q_("%"PRItime" year ago", "%"PRItime" years ago", (diff + 183) / 365), (diff + 183) / 365); } @@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) if (mode->type == DATE_UNIX) { strbuf_reset(&timebuf); - strbuf_addf(&timebuf, "%lu", time); + strbuf_addf(&timebuf, "%"PRItime, time); return timebuf.buf; } @@ -188,7 +188,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) if (mode->type == DATE_RAW) { strbuf_reset(&timebuf); - strbuf_addf(&timebuf, "%lu %+05d", time, tz); + strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz); return timebuf.buf; } @@ -643,7 +643,7 @@ static void date_string(unsigned long date, int offset, struct strbuf *buf) offset = -offset; sign = '-'; } - strbuf_addf(buf, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60); + strbuf_addf(buf, "%"PRItime" %c%02d%02d", date, sign, offset / 60, offset % 60); } /* diff --git a/fetch-pack.c b/fetch-pack.c index 42969353d6..a1b8ea59e8 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -393,7 +393,7 @@ static int find_common(struct fetch_pack_args *args, packet_buf_write(&req_buf, "deepen %d", args->depth); if (args->deepen_since) { unsigned long max_age = approxidate(args->deepen_since); - packet_buf_write(&req_buf, "deepen-since %lu", max_age); + packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age); } if (args->deepen_not) { int i; diff --git a/git-compat-util.h b/git-compat-util.h index fc1b5fe1a6..cd522903ed 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -319,6 +319,7 @@ extern char *gitdirname(char *); #define PRIo32 "o" #endif +#define PRItime "lu" #define parse_timestamp strtoul #ifndef PATH_SEP diff --git a/refs/files-backend.c b/refs/files-backend.c index dae0522673..ecbb13dc4b 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -4131,7 +4131,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, printf("prune %s", message); } else { if (cb->newlog) { - fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s", + fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s", oid_to_hex(ooid), oid_to_hex(noid), email, timestamp, tz, message); oidcpy(&cb->last_kept_oid, noid); diff --git a/t/helper/test-date.c b/t/helper/test-date.c index 52d1fc3445..269040f028 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -53,7 +53,7 @@ static void parse_dates(const char **argv, struct timeval *now) strbuf_reset(&result); parse_date(*argv, &result); - if (sscanf(result.buf, "%lu %d", &t, &tz) == 2) + if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2) printf("%s -> %s\n", *argv, show_date(t, tz, DATE_MODE(ISO8601))); else diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index a01430c24b..7d93627e45 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv) show(&expect, &ret, "boolean: %d", boolean); show(&expect, &ret, "integer: %d", integer); show(&expect, &ret, "magnitude: %lu", magnitude); - show(&expect, &ret, "timestamp: %lu", timestamp); + show(&expect, &ret, "timestamp: %"PRItime, timestamp); show(&expect, &ret, "string: %s", string ? string : "(not set)"); show(&expect, &ret, "abbrev: %d", abbrev); show(&expect, &ret, "verbose: %d", verbose); diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index 2d84c45ffe..a436bfdb05 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -141,7 +141,7 @@ static int each_reflog(struct object_id *old_oid, struct object_id *new_oid, const char *committer, unsigned long timestamp, int tz, const char *msg, void *cb_data) { - printf("%s %s %s %lu %d %s\n", + printf("%s %s %s %"PRItime" %d %s\n", oid_to_hex(old_oid), oid_to_hex(new_oid), committer, timestamp, tz, msg); return 0; diff --git a/upload-pack.c b/upload-pack.c index f17f4dd123..4966f0b8a0 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -863,7 +863,7 @@ static void receive_needs(void) argv_array_push(&av, "rev-list"); if (deepen_since) - argv_array_pushf(&av, "--max-age=%lu", deepen_since); + argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since); if (deepen_not.nr) { argv_array_push(&av, "--not"); for (i = 0; i < deepen_not.nr; i++) { diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c index 97cba39cdf..6c9f2866d8 100644 --- a/vcs-svn/fast_export.c +++ b/vcs-svn/fast_export.c @@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author, static int firstnote = 1; size_t loglen = strlen(log); printf("commit %s\n", note_ref); - printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp); + printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp); printf("data %"PRIuMAX"\n", (uintmax_t)loglen); fwrite(log, loglen, 1, stdout); if (firstnote) { @@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author, } printf("commit %s\n", local_ref); printf("mark :%"PRIu32"\n", revision); - printf("committer %s <%s@%s> %lu +0000\n", + printf("committer %s <%s@%s> %"PRItime" +0000\n", *author ? author : "nobody", *author ? author : "nobody", *uuid ? uuid : "local", timestamp); From 0d8a814d8ac6b18834def72912781ce6fe45fa89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 24 Apr 2017 17:01:21 +0700 Subject: [PATCH 031/566] refs: add REFS_STORE_ALL_CAPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- refs.c | 6 +----- refs/refs-internal.h | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/refs.c b/refs.c index 96eba92475..6e6f0ec5be 100644 --- a/refs.c +++ b/refs.c @@ -1526,11 +1526,7 @@ struct ref_store *get_main_ref_store(void) if (main_ref_store) return main_ref_store; - main_ref_store = ref_store_init(get_git_dir(), - (REF_STORE_READ | - REF_STORE_WRITE | - REF_STORE_ODB | - REF_STORE_MAIN)); + main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS); return main_ref_store; } diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 690498698e..b26f7e41ce 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -467,6 +467,10 @@ struct ref_store; #define REF_STORE_WRITE (1 << 1) /* can perform update operations */ #define REF_STORE_ODB (1 << 2) /* has access to object database */ #define REF_STORE_MAIN (1 << 3) +#define REF_STORE_ALL_CAPS (REF_STORE_READ | \ + REF_STORE_WRITE | \ + REF_STORE_ODB | \ + REF_STORE_MAIN) /* * Initialize the ref_store for the specified gitdir. These functions From 17eff96b83be5c4c25e33a40e632d1b55c506d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 24 Apr 2017 17:01:22 +0700 Subject: [PATCH 032/566] refs: introduce get_worktree_ref_store() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit files-backend at this point is still aware of the per-repo/worktree separation in refs, so it can handle a linked worktree. Some refs operations are known not working when current files-backend is used in a linked worktree (e.g. reflog). Tests will be written when refs_* functions start to be called with worktree backend to verify that they work as expected. Note: accessing a worktree of a submodule remains unaddressed. Perhaps after get_worktrees() can access submodule (or rather a new function get_submodule_worktrees(), that lists worktrees of a submodule), we can update this function to work with submodules as well. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- refs.c | 30 ++++++++++++++++++++++++++++++ refs.h | 2 ++ 2 files changed, 32 insertions(+) diff --git a/refs.c b/refs.c index 6e6f0ec5be..7972720256 100644 --- a/refs.c +++ b/refs.c @@ -10,6 +10,7 @@ #include "object.h" #include "tag.h" #include "submodule.h" +#include "worktree.h" /* * List of all available backends @@ -1486,6 +1487,9 @@ static struct ref_store *main_ref_store; /* A hashmap of ref_stores, stored by submodule name: */ static struct hashmap submodule_ref_stores; +/* A hashmap of ref_stores, stored by worktree id: */ +static struct hashmap worktree_ref_stores; + /* * Look up a ref store by name. If that ref_store hasn't been * registered yet, return NULL. @@ -1586,6 +1590,32 @@ struct ref_store *get_submodule_ref_store(const char *submodule) return refs; } +struct ref_store *get_worktree_ref_store(const struct worktree *wt) +{ + struct ref_store *refs; + const char *id; + + if (wt->is_current) + return get_main_ref_store(); + + id = wt->id ? wt->id : "/"; + refs = lookup_ref_store_map(&worktree_ref_stores, id); + if (refs) + return refs; + + if (wt->id) + refs = ref_store_init(git_common_path("worktrees/%s", wt->id), + REF_STORE_ALL_CAPS); + else + refs = ref_store_init(get_git_common_dir(), + REF_STORE_ALL_CAPS); + + if (refs) + register_ref_store_map(&worktree_ref_stores, "worktree", + refs, id); + return refs; +} + void base_ref_store_init(struct ref_store *refs, const struct ref_storage_be *be) { diff --git a/refs.h b/refs.h index 49e97d7d5f..6df69a2adb 100644 --- a/refs.h +++ b/refs.h @@ -5,6 +5,7 @@ struct object_id; struct ref_store; struct strbuf; struct string_list; +struct worktree; /* * Resolve a reference, recursively following symbolic refererences. @@ -655,5 +656,6 @@ struct ref_store *get_main_ref_store(void); * submodule==NULL. */ struct ref_store *get_submodule_ref_store(const char *submodule); +struct ref_store *get_worktree_ref_store(const struct worktree *wt); #endif /* REFS_H */ From fa099d23227f88b5a1cd79c646551130d9b36e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 24 Apr 2017 17:01:23 +0700 Subject: [PATCH 033/566] worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The manual parsing code is replaced with a call to refs_resolve_ref_unsafe(). The manual parsing code must die because only refs/files-backend.c should do that. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- branch.c | 3 +- t/helper/test-ref-store.c | 18 ++++++ t/t1407-worktree-ref-store.sh | 40 +++++++++++++ worktree.c | 102 +++++++++------------------------- worktree.h | 2 +- 5 files changed, 88 insertions(+), 77 deletions(-) create mode 100755 t/t1407-worktree-ref-store.sh diff --git a/branch.c b/branch.c index 5c12036b02..0b949b7fb2 100644 --- a/branch.c +++ b/branch.c @@ -355,7 +355,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref, for (i = 0; worktrees[i]; i++) { if (worktrees[i]->is_detached) continue; - if (strcmp(oldref, worktrees[i]->head_ref)) + if (worktrees[i]->head_ref && + strcmp(oldref, worktrees[i]->head_ref)) continue; if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]), diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index 2d84c45ffe..4a487c014e 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -1,5 +1,6 @@ #include "cache.h" #include "refs.h" +#include "worktree.h" static const char *notnull(const char *arg, const char *name) { @@ -32,6 +33,23 @@ static const char **get_store(const char **argv, struct ref_store **refs) strbuf_release(&sb); *refs = get_submodule_ref_store(gitdir); + } else if (skip_prefix(argv[0], "worktree:", &gitdir)) { + struct worktree **p, **worktrees = get_worktrees(0); + + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + + if (!wt->id) { + /* special case for main worktree */ + if (!strcmp(gitdir, "main")) + break; + } else if (!strcmp(gitdir, wt->id)) + break; + } + if (!*p) + die("no such worktree: %s", gitdir); + + *refs = get_worktree_ref_store(*p); } else die("unknown backend %s", argv[0]); diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh new file mode 100755 index 0000000000..04d1e9d177 --- /dev/null +++ b/t/t1407-worktree-ref-store.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description='test worktree ref store api' + +. ./test-lib.sh + +RWT="test-ref-store worktree:wt" +RMAIN="test-ref-store worktree:main" + +test_expect_success 'setup' ' + test_commit first && + git worktree add -b wt-master wt && + ( + cd wt && + test_commit second + ) +' + +test_expect_success 'resolve_ref()' ' + SHA1=`git rev-parse master` && + echo "$SHA1 refs/heads/master 0x0" >expected && + $RWT resolve-ref refs/heads/master 0 >actual && + test_cmp expected actual && + $RMAIN resolve-ref refs/heads/master 0 >actual && + test_cmp expected actual +' + +test_expect_success 'resolve_ref()' ' + SHA1=`git -C wt rev-parse HEAD` && + echo "$SHA1 refs/heads/wt-master 0x1" >expected && + $RWT resolve-ref HEAD 0 >actual && + test_cmp expected actual && + + SHA1=`git rev-parse HEAD` && + echo "$SHA1 refs/heads/master 0x1" >expected && + $RMAIN resolve-ref HEAD 0 >actual && + test_cmp expected actual +' + +test_done diff --git a/worktree.c b/worktree.c index fa7bc67a50..c1ec334b06 100644 --- a/worktree.c +++ b/worktree.c @@ -19,54 +19,25 @@ void free_worktrees(struct worktree **worktrees) free (worktrees); } -/* - * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL, - * set is_detached to 1 (0) if the ref is detached (is not detached). - * - * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so - * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses - * git_path). Parse the ref ourselves. - * - * return -1 if the ref is not a proper ref, 0 otherwise (success) - */ -static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached) -{ - if (is_detached) - *is_detached = 0; - if (!strbuf_readlink(ref, path_to_ref, 0)) { - /* HEAD is symbolic link */ - if (!starts_with(ref->buf, "refs/") || - check_refname_format(ref->buf, 0)) - return -1; - } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) { - /* textual symref or detached */ - if (!starts_with(ref->buf, "ref:")) { - if (is_detached) - *is_detached = 1; - } else { - strbuf_remove(ref, 0, strlen("ref:")); - strbuf_trim(ref); - if (check_refname_format(ref->buf, 0)) - return -1; - } - } else - return -1; - return 0; -} - /** - * Add the head_sha1 and head_ref (if not detached) to the given worktree + * Update head_sha1, head_ref and is_detached of the given worktree */ -static void add_head_info(struct strbuf *head_ref, struct worktree *worktree) +static void add_head_info(struct worktree *wt) { - if (head_ref->len) { - if (worktree->is_detached) { - get_sha1_hex(head_ref->buf, worktree->head_sha1); - } else { - resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL); - worktree->head_ref = strbuf_detach(head_ref, NULL); - } - } + int flags; + const char *target; + + target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt), + "HEAD", + RESOLVE_REF_READING, + wt->head_sha1, &flags); + if (!target) + return; + + if (flags & REF_ISSYMREF) + wt->head_ref = xstrdup(target); + else + wt->is_detached = 1; } /** @@ -77,9 +48,7 @@ static struct worktree *get_main_worktree(void) struct worktree *worktree = NULL; struct strbuf path = STRBUF_INIT; struct strbuf worktree_path = STRBUF_INIT; - struct strbuf head_ref = STRBUF_INIT; int is_bare = 0; - int is_detached = 0; strbuf_add_absolute_path(&worktree_path, get_git_common_dir()); is_bare = !strbuf_strip_suffix(&worktree_path, "/.git"); @@ -91,13 +60,10 @@ static struct worktree *get_main_worktree(void) worktree = xcalloc(1, sizeof(*worktree)); worktree->path = strbuf_detach(&worktree_path, NULL); worktree->is_bare = is_bare; - worktree->is_detached = is_detached; - if (!parse_ref(path.buf, &head_ref, &is_detached)) - add_head_info(&head_ref, worktree); + add_head_info(worktree); strbuf_release(&path); strbuf_release(&worktree_path); - strbuf_release(&head_ref); return worktree; } @@ -106,8 +72,6 @@ static struct worktree *get_linked_worktree(const char *id) struct worktree *worktree = NULL; struct strbuf path = STRBUF_INIT; struct strbuf worktree_path = STRBUF_INIT; - struct strbuf head_ref = STRBUF_INIT; - int is_detached = 0; if (!id) die("Missing linked worktree name"); @@ -127,19 +91,14 @@ static struct worktree *get_linked_worktree(const char *id) strbuf_reset(&path); strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id); - if (parse_ref(path.buf, &head_ref, &is_detached) < 0) - goto done; - worktree = xcalloc(1, sizeof(*worktree)); worktree->path = strbuf_detach(&worktree_path, NULL); worktree->id = xstrdup(id); - worktree->is_detached = is_detached; - add_head_info(&head_ref, worktree); + add_head_info(worktree); done: strbuf_release(&path); strbuf_release(&worktree_path); - strbuf_release(&head_ref); return worktree; } @@ -334,8 +293,6 @@ const struct worktree *find_shared_symref(const char *symref, const char *target) { const struct worktree *existing = NULL; - struct strbuf path = STRBUF_INIT; - struct strbuf sb = STRBUF_INIT; static struct worktree **worktrees; int i = 0; @@ -345,6 +302,11 @@ const struct worktree *find_shared_symref(const char *symref, for (i = 0; worktrees[i]; i++) { struct worktree *wt = worktrees[i]; + const char *symref_target; + unsigned char sha1[20]; + struct ref_store *refs; + int flags; + if (wt->is_bare) continue; @@ -359,25 +321,15 @@ const struct worktree *find_shared_symref(const char *symref, } } - strbuf_reset(&path); - strbuf_reset(&sb); - strbuf_addf(&path, "%s/%s", - get_worktree_git_dir(wt), - symref); - - if (parse_ref(path.buf, &sb, NULL)) { - continue; - } - - if (!strcmp(sb.buf, target)) { + refs = get_worktree_ref_store(wt); + symref_target = refs_resolve_ref_unsafe(refs, symref, 0, + sha1, &flags); + if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) { existing = wt; break; } } - strbuf_release(&path); - strbuf_release(&sb); - return existing; } diff --git a/worktree.h b/worktree.h index 6bfb985203..5ea5e503fb 100644 --- a/worktree.h +++ b/worktree.h @@ -4,7 +4,7 @@ struct worktree { char *path; char *id; - char *head_ref; + char *head_ref; /* NULL if HEAD is broken or detached */ char *lock_reason; /* internal use */ unsigned char head_sha1[20]; int is_detached; From d026a25657cbe15ceb6bcb5d5047a36a0a70b33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 24 Apr 2017 17:01:24 +0700 Subject: [PATCH 034/566] refs: kill set_worktree_head_symref() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 70999e9cec (branch -m: update all per-worktree HEADs - 2016-03-27) added this function in order to update HEADs of all relevant worktrees, when a branch is renamed. It, as a public ref api, kind of breaks abstraction when it uses internal functions of files backend. With the introduction of refs_create_symref(), we can move back pretty close to the code before 70999e9cec, where create_symref() was used for updating HEAD. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- branch.c | 12 +++++----- refs.h | 10 -------- refs/files-backend.c | 44 ----------------------------------- t/t1407-worktree-ref-store.sh | 12 ++++++++++ 4 files changed, 18 insertions(+), 60 deletions(-) diff --git a/branch.c b/branch.c index 0b949b7fb2..69d5eea84b 100644 --- a/branch.c +++ b/branch.c @@ -353,18 +353,18 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref, int i; for (i = 0; worktrees[i]; i++) { + struct ref_store *refs; + if (worktrees[i]->is_detached) continue; if (worktrees[i]->head_ref && strcmp(oldref, worktrees[i]->head_ref)) continue; - if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]), - newref, logmsg)) { - ret = -1; - error(_("HEAD of working tree %s is not updated"), - worktrees[i]->path); - } + refs = get_worktree_ref_store(worktrees[i]); + if (refs_create_symref(refs, "HEAD", newref, logmsg)) + ret = error(_("HEAD of working tree %s is not updated"), + worktrees[i]->path); } free_worktrees(worktrees); diff --git a/refs.h b/refs.h index 6df69a2adb..447381d378 100644 --- a/refs.h +++ b/refs.h @@ -402,16 +402,6 @@ int refs_create_symref(struct ref_store *refs, const char *refname, const char *target, const char *logmsg); int create_symref(const char *refname, const char *target, const char *logmsg); -/* - * Update HEAD of the specified gitdir. - * Similar to create_symref("relative-git-dir/HEAD", target, NULL), but - * this can update the main working tree's HEAD regardless of where - * $GIT_DIR points to. - * Return 0 if successful, non-zero otherwise. - * */ -int set_worktree_head_symref(const char *gitdir, const char *target, - const char *logmsg); - enum action_on_err { UPDATE_REFS_MSG_ON_ERR, UPDATE_REFS_DIE_ON_ERR, diff --git a/refs/files-backend.c b/refs/files-backend.c index 4d705b4037..4149943a6e 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3160,50 +3160,6 @@ static int files_create_symref(struct ref_store *ref_store, return ret; } -int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg) -{ - /* - * FIXME: this obviously will not work well for future refs - * backends. This function needs to die. - */ - struct files_ref_store *refs = - files_downcast(get_main_ref_store(), - REF_STORE_WRITE, - "set_head_symref"); - - static struct lock_file head_lock; - struct ref_lock *lock; - struct strbuf head_path = STRBUF_INIT; - const char *head_rel; - int ret; - - strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir)); - if (hold_lock_file_for_update(&head_lock, head_path.buf, - LOCK_NO_DEREF) < 0) { - struct strbuf err = STRBUF_INIT; - unable_to_lock_message(head_path.buf, errno, &err); - error("%s", err.buf); - strbuf_release(&err); - strbuf_release(&head_path); - return -1; - } - - /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for - linked trees */ - head_rel = remove_leading_path(head_path.buf, - absolute_path(get_git_common_dir())); - /* to make use of create_symref_locked(), initialize ref_lock */ - lock = xcalloc(1, sizeof(struct ref_lock)); - lock->lk = &head_lock; - lock->ref_name = xstrdup(head_rel); - - ret = create_symref_locked(refs, lock, head_rel, target, logmsg); - - unlock_ref(lock); /* will free lock */ - strbuf_release(&head_path); - return ret; -} - static int files_reflog_exists(struct ref_store *ref_store, const char *refname) { diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh index 04d1e9d177..5df06f3556 100755 --- a/t/t1407-worktree-ref-store.sh +++ b/t/t1407-worktree-ref-store.sh @@ -37,4 +37,16 @@ test_expect_success 'resolve_ref()' ' test_cmp expected actual ' +test_expect_success 'create_symref(FOO, refs/heads/master)' ' + $RWT create-symref FOO refs/heads/master nothing && + echo refs/heads/master >expected && + git -C wt symbolic-ref FOO >actual && + test_cmp expected actual && + + $RMAIN create-symref FOO refs/heads/wt-master nothing && + echo refs/heads/wt-master >expected && + git symbolic-ref FOO >actual && + test_cmp expected actual +' + test_done From 758c1f9d1bcab5115f5d9e990c952602ea58a161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 24 Apr 2017 19:29:53 +0200 Subject: [PATCH 035/566] archive-zip: add tests for big ZIP archives Test the creation of ZIP archives bigger than 4GB and containing files bigger than 4GB. They are marked as EXPENSIVE because they take quite a while and because the first one needs a bit more than 4GB of disk space to store the resulting archive. The big archive in the first test is made up of a tree containing thousands of copies of a small file. Yet the test has to write out the full archive because unzip doesn't offer a way to read from stdin. The big file in the second test is provided as a zipped pack file to avoid writing another 4GB file to disk and then adding it. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- t/t5004-archive-corner-cases.sh | 45 ++++++++++++++++++++++++++++++++ t/t5004/big-pack.zip | Bin 0 -> 7373 bytes 2 files changed, 45 insertions(+) create mode 100644 t/t5004/big-pack.zip diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index cca23383c5..5c886fa823 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh @@ -155,4 +155,49 @@ test_expect_success ZIPINFO 'zip archive with many entries' ' test_cmp expect actual ' +test_expect_failure EXPENSIVE,UNZIP 'zip archive bigger than 4GB' ' + # build string containing 65536 characters + s=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef && + s=$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s && + s=$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s && + + # create blob with a length of 65536 + 1 bytes + blob=$(echo $s | git hash-object -w --stdin) && + + # create tree containing 65500 entries of that blob + for i in $(test_seq 1 65500) + do + echo "100644 blob $blob $i" + done >tree && + tree=$(git mktree big.lst && + grep $size big.lst +' + test_done diff --git a/t/t5004/big-pack.zip b/t/t5004/big-pack.zip new file mode 100644 index 0000000000000000000000000000000000000000..caaf614eeece6f818c525e433561e37560a75b05 GIT binary patch literal 7373 zcmWIWW@Zs#U}E54xLY#A%SmVLl4u471|Jp%215oJhJwW8Y+ZvC^HlQ`3(KT5gS6zd z#6$y=#3VyYlN6KGlw?bTG()50MB`LTb5p&{l#0+0P6p-9-y6#!_6|EcGQyxED zyZY^&^YwM2r?Wic_gf3wz0#?R{8hB^$gt{iHiUEJN{iALjZ~|R(VzA zOp{_@IDE*S!H8sEfc%Wl8*lHd?-DJPX@5BLD~n)>se}uQ{sHa{9CBhhi*hb3*-*jg zc5o4&4%_XW4Bab?Enk9jiu`<7qbA8y|-Ck>2+hMBYN-pgCcFt>ev-5*|-&jb`JE>Hh`B*~cT z&t2=?C44}E8GDaU*PD0ekK%{l7w@(f14RzJnfvea+fQlS75nRoU$s?(g=&R(fOLb% zK_JPHAqeJ(fjJ$>oKcz4&>2l3kc=^!7e@2KXkHl23k(gTVK5p7z>;7z9gKznQp0() zeK6WS7;PVn){Ud}!f4$%I)=h9I*!CJ8V10UU^E?!h5@KqG@1@Z!(cQWK$^#7<%OS{ z_HQoT!K!n;di%NDON?i(ygj-((dOTWgOj)4mXFhko42#<@9S6BTc6*5|Cc?$n~_P5 z8P`mn1SleafRRC^5k!+Qug40R*F&4rL$?-n>J8c2VFDo0!BTPW}9!Q@6 I&6hC%0B76lcmMzZ literal 0 HcmV?d00001 From c061a149709195e0ec914a5d6ba173042e4197f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 24 Apr 2017 19:30:49 +0200 Subject: [PATCH 036/566] archive-zip: use strbuf for ZIP directory Keep the ZIP central directory, which is written after all archive entries, in a strbuf instead of a custom-managed buffer. It contains binary data, so we can't (and don't want to) use the full range of strbuf functions and we don't need the terminating NUL, but the result is shorter and simpler code. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index b429a8d974..a6fac59602 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -11,16 +11,14 @@ static int zip_date; static int zip_time; -static unsigned char *zip_dir; -static unsigned int zip_dir_size; +/* We only care about the "buf" part here. */ +static struct strbuf zip_dir; static unsigned int zip_offset; -static unsigned int zip_dir_offset; static uint64_t zip_dir_entries; static unsigned int max_creator_version; -#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024) #define ZIP_STREAM (1 << 3) #define ZIP_UTF8 (1 << 11) @@ -268,7 +266,6 @@ static int write_zip_entry(struct archiver_args *args, unsigned long attr2; unsigned long compressed_size; unsigned long crc; - unsigned long direntsize; int method; unsigned char *out; void *deflated = NULL; @@ -356,13 +353,6 @@ static int write_zip_entry(struct archiver_args *args, extra.flags[0] = 1; /* just mtime */ copy_le32(extra.mtime, args->time); - /* make sure we have enough free space in the dictionary */ - direntsize = ZIP_DIR_HEADER_SIZE + pathlen + ZIP_EXTRA_MTIME_SIZE; - while (zip_dir_size < zip_dir_offset + direntsize) { - zip_dir_size += ZIP_DIRECTORY_MIN_SIZE; - zip_dir = xrealloc(zip_dir, zip_dir_size); - } - copy_le32(dirent.magic, 0x02014b50); copy_le16(dirent.creator_version, creator_version); copy_le16(dirent.version, 10); @@ -486,12 +476,9 @@ static int write_zip_entry(struct archiver_args *args, copy_le16(dirent.attr1, !is_binary); - memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE); - zip_dir_offset += ZIP_DIR_HEADER_SIZE; - memcpy(zip_dir + zip_dir_offset, path, pathlen); - zip_dir_offset += pathlen; - memcpy(zip_dir + zip_dir_offset, &extra, ZIP_EXTRA_MTIME_SIZE); - zip_dir_offset += ZIP_EXTRA_MTIME_SIZE; + strbuf_add(&zip_dir, &dirent, ZIP_DIR_HEADER_SIZE); + strbuf_add(&zip_dir, path, pathlen); + strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE); zip_dir_entries++; return 0; @@ -510,12 +497,12 @@ static void write_zip64_trailer(void) copy_le32(trailer64.directory_start_disk, 0); copy_le64(trailer64.entries_on_this_disk, zip_dir_entries); copy_le64(trailer64.entries, zip_dir_entries); - copy_le64(trailer64.size, zip_dir_offset); + copy_le64(trailer64.size, zip_dir.len); copy_le64(trailer64.offset, zip_offset); copy_le32(locator64.magic, 0x07064b50); copy_le32(locator64.disk, 0); - copy_le64(locator64.offset, zip_offset + zip_dir_offset); + copy_le64(locator64.offset, zip_offset + zip_dir.len); copy_le32(locator64.number_of_disks, 1); write_or_die(1, &trailer64, ZIP64_DIR_TRAILER_SIZE); @@ -533,11 +520,11 @@ static void write_zip_trailer(const unsigned char *sha1) copy_le16_clamp(trailer.entries_on_this_disk, zip_dir_entries, &clamped); copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped); - copy_le32(trailer.size, zip_dir_offset); + copy_le32(trailer.size, zip_dir.len); copy_le32(trailer.offset, zip_offset); copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0); - write_or_die(1, zip_dir, zip_dir_offset); + write_or_die(1, zip_dir.buf, zip_dir.len); if (clamped) write_zip64_trailer(); write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE); @@ -568,14 +555,13 @@ static int write_zip_archive(const struct archiver *ar, dos_time(&args->time, &zip_date, &zip_time); - zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); - zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; + strbuf_init(&zip_dir, 0); err = write_archive_entries(args, write_zip_entry); if (!err) write_zip_trailer(args->commit_sha1); - free(zip_dir); + strbuf_release(&zip_dir); return err; } From 3c78fd808d8ed8848fe09735c7755ddd35cfe83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 24 Apr 2017 19:31:44 +0200 Subject: [PATCH 037/566] archive-zip: write ZIP dir entry directly to strbuf Write all fields of the ZIP directory record for an archive entry in the right order directly into the strbuf instead of taking a detour through a struct. Do that at end, when we have all necessary data like checksum and compressed size. The fields are documented just as well, the code becomes shorter and we save an extra copy. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 81 +++++++++++++++++---------------------------------- 1 file changed, 27 insertions(+), 54 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index a6fac59602..2d52bb3ade 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -45,27 +45,6 @@ struct zip_data_desc { unsigned char _end[1]; }; -struct zip_dir_header { - unsigned char magic[4]; - unsigned char creator_version[2]; - unsigned char version[2]; - unsigned char flags[2]; - unsigned char compression_method[2]; - unsigned char mtime[2]; - unsigned char mdate[2]; - unsigned char crc32[4]; - unsigned char compressed_size[4]; - unsigned char size[4]; - unsigned char filename_length[2]; - unsigned char extra_length[2]; - unsigned char comment_length[2]; - unsigned char disk[2]; - unsigned char attr1[2]; - unsigned char attr2[4]; - unsigned char offset[4]; - unsigned char _end[1]; -}; - struct zip_dir_trailer { unsigned char magic[4]; unsigned char disk[2]; @@ -166,6 +145,15 @@ static void copy_le16_clamp(unsigned char *dest, uint64_t n, int *clamped) copy_le16(dest, clamp_max(n, 0xffff, clamped)); } +static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n) +{ + while (size-- > 0) { + strbuf_addch(sb, n & 0xff); + n >>= 8; + } + return -!!n; +} + static void *zlib_deflate_raw(void *data, unsigned long size, int compression_level, unsigned long *compressed_size) @@ -212,16 +200,6 @@ static void write_zip_data_desc(unsigned long size, write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE); } -static void set_zip_dir_data_desc(struct zip_dir_header *header, - unsigned long size, - unsigned long compressed_size, - unsigned long crc) -{ - copy_le32(header->crc32, crc); - copy_le32(header->compressed_size, compressed_size); - copy_le32(header->size, size); -} - static void set_zip_header_data_desc(struct zip_local_header *header, unsigned long size, unsigned long compressed_size, @@ -261,7 +239,7 @@ static int write_zip_entry(struct archiver_args *args, unsigned int mode) { struct zip_local_header header; - struct zip_dir_header dirent; + uintmax_t offset = zip_offset; struct zip_extra_mtime extra; unsigned long attr2; unsigned long compressed_size; @@ -353,21 +331,6 @@ static int write_zip_entry(struct archiver_args *args, extra.flags[0] = 1; /* just mtime */ copy_le32(extra.mtime, args->time); - copy_le32(dirent.magic, 0x02014b50); - copy_le16(dirent.creator_version, creator_version); - copy_le16(dirent.version, 10); - copy_le16(dirent.flags, flags); - copy_le16(dirent.compression_method, method); - copy_le16(dirent.mtime, zip_time); - copy_le16(dirent.mdate, zip_date); - set_zip_dir_data_desc(&dirent, size, compressed_size, crc); - copy_le16(dirent.filename_length, pathlen); - copy_le16(dirent.extra_length, ZIP_EXTRA_MTIME_SIZE); - copy_le16(dirent.comment_length, 0); - copy_le16(dirent.disk, 0); - copy_le32(dirent.attr2, attr2); - copy_le32(dirent.offset, zip_offset); - copy_le32(header.magic, 0x04034b50); copy_le16(header.version, 10); copy_le16(header.flags, flags); @@ -406,8 +369,6 @@ static int write_zip_entry(struct archiver_args *args, write_zip_data_desc(size, compressed_size, crc); zip_offset += ZIP_DATA_DESC_SIZE; - - set_zip_dir_data_desc(&dirent, size, compressed_size, crc); } else if (stream && method == 8) { unsigned char buf[STREAM_BUFFER_SIZE]; ssize_t readlen; @@ -464,8 +425,6 @@ static int write_zip_entry(struct archiver_args *args, write_zip_data_desc(size, compressed_size, crc); zip_offset += ZIP_DATA_DESC_SIZE; - - set_zip_dir_data_desc(&dirent, size, compressed_size, crc); } else if (compressed_size > 0) { write_or_die(1, out, compressed_size); zip_offset += compressed_size; @@ -474,9 +433,23 @@ static int write_zip_entry(struct archiver_args *args, free(deflated); free(buffer); - copy_le16(dirent.attr1, !is_binary); - - strbuf_add(&zip_dir, &dirent, ZIP_DIR_HEADER_SIZE); + strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */ + strbuf_add_le(&zip_dir, 2, creator_version); + strbuf_add_le(&zip_dir, 2, 10); /* version */ + strbuf_add_le(&zip_dir, 2, flags); + strbuf_add_le(&zip_dir, 2, method); + strbuf_add_le(&zip_dir, 2, zip_time); + strbuf_add_le(&zip_dir, 2, zip_date); + strbuf_add_le(&zip_dir, 4, crc); + strbuf_add_le(&zip_dir, 4, compressed_size); + strbuf_add_le(&zip_dir, 4, size); + strbuf_add_le(&zip_dir, 2, pathlen); + strbuf_add_le(&zip_dir, 2, ZIP_EXTRA_MTIME_SIZE); + strbuf_add_le(&zip_dir, 2, 0); /* comment length */ + strbuf_add_le(&zip_dir, 2, 0); /* disk */ + strbuf_add_le(&zip_dir, 2, !is_binary); + strbuf_add_le(&zip_dir, 4, attr2); + strbuf_add_le(&zip_dir, 4, offset); strbuf_add(&zip_dir, path, pathlen); strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE); zip_dir_entries++; From af95749f9b06467e7536da24432b00989a7c8c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 24 Apr 2017 19:32:36 +0200 Subject: [PATCH 038/566] archive-zip: support archives bigger than 4GB Add a zip64 extended information extra field to the central directory and emit the zip64 end of central directory records as well as locator if the offset of an entry within the archive exceeds 4GB. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 32 ++++++++++++++++++++++++++++---- t/t5004-archive-corner-cases.sh | 2 +- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index 2d52bb3ade..7d6f2a85d0 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -14,7 +14,7 @@ static int zip_time; /* We only care about the "buf" part here. */ static struct strbuf zip_dir; -static unsigned int zip_offset; +static uintmax_t zip_offset; static uint64_t zip_dir_entries; static unsigned int max_creator_version; @@ -145,6 +145,11 @@ static void copy_le16_clamp(unsigned char *dest, uint64_t n, int *clamped) copy_le16(dest, clamp_max(n, 0xffff, clamped)); } +static void copy_le32_clamp(unsigned char *dest, uint64_t n, int *clamped) +{ + copy_le32(dest, clamp_max(n, 0xffffffff, clamped)); +} + static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n) { while (size-- > 0) { @@ -154,6 +159,12 @@ static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n) return -!!n; } +static uint32_t clamp32(uintmax_t n) +{ + const uintmax_t max = 0xffffffff; + return (n < max) ? n : max; +} + static void *zlib_deflate_raw(void *data, unsigned long size, int compression_level, unsigned long *compressed_size) @@ -254,6 +265,8 @@ static int write_zip_entry(struct archiver_args *args, int is_binary = -1; const char *path_without_prefix = path + args->baselen; unsigned int creator_version = 0; + size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE; + size_t zip64_dir_extra_payload_size = 0; crc = crc32(0, NULL, 0); @@ -433,6 +446,11 @@ static int write_zip_entry(struct archiver_args *args, free(deflated); free(buffer); + if (offset > 0xffffffff) { + zip64_dir_extra_payload_size += 8; + zip_dir_extra_size += 2 + 2 + zip64_dir_extra_payload_size; + } + strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */ strbuf_add_le(&zip_dir, 2, creator_version); strbuf_add_le(&zip_dir, 2, 10); /* version */ @@ -444,14 +462,20 @@ static int write_zip_entry(struct archiver_args *args, strbuf_add_le(&zip_dir, 4, compressed_size); strbuf_add_le(&zip_dir, 4, size); strbuf_add_le(&zip_dir, 2, pathlen); - strbuf_add_le(&zip_dir, 2, ZIP_EXTRA_MTIME_SIZE); + strbuf_add_le(&zip_dir, 2, zip_dir_extra_size); strbuf_add_le(&zip_dir, 2, 0); /* comment length */ strbuf_add_le(&zip_dir, 2, 0); /* disk */ strbuf_add_le(&zip_dir, 2, !is_binary); strbuf_add_le(&zip_dir, 4, attr2); - strbuf_add_le(&zip_dir, 4, offset); + strbuf_add_le(&zip_dir, 4, clamp32(offset)); strbuf_add(&zip_dir, path, pathlen); strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE); + if (zip64_dir_extra_payload_size) { + strbuf_add_le(&zip_dir, 2, 0x0001); /* magic */ + strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size); + if (offset >= 0xffffffff) + strbuf_add_le(&zip_dir, 8, offset); + } zip_dir_entries++; return 0; @@ -494,7 +518,7 @@ static void write_zip_trailer(const unsigned char *sha1) &clamped); copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped); copy_le32(trailer.size, zip_dir.len); - copy_le32(trailer.offset, zip_offset); + copy_le32_clamp(trailer.offset, zip_offset, &clamped); copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0); write_or_die(1, zip_dir.buf, zip_dir.len); diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index 5c886fa823..41183ea2cf 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh @@ -155,7 +155,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' ' test_cmp expect actual ' -test_expect_failure EXPENSIVE,UNZIP 'zip archive bigger than 4GB' ' +test_expect_success EXPENSIVE,UNZIP 'zip archive bigger than 4GB' ' # build string containing 65536 characters s=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef && s=$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s && From 4cdf3f9d84568da72f1dcade812de7a42ecb6d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 24 Apr 2017 19:33:34 +0200 Subject: [PATCH 039/566] archive-zip: support files bigger than 4GB Write a zip64 extended information extra field for big files as part of their local headers and as part of their central directory headers. Also write a zip64 version of the data descriptor in that case. If we're streaming then we don't know the compressed size at the time we write the header. Deflate can end up making a file bigger instead of smaller if we're unlucky. Write a local zip64 header already for files with a size of 2GB or more in this case to be on the safe side. Both sizes need to be included in the local zip64 header, but the extra field for the directory must only contain 64-bit equivalents for 32-bit values of 0xffffffff. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 90 +++++++++++++++++++++++++++------ t/t5004-archive-corner-cases.sh | 2 +- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index 7d6f2a85d0..44ed78f163 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -45,6 +45,14 @@ struct zip_data_desc { unsigned char _end[1]; }; +struct zip64_data_desc { + unsigned char magic[4]; + unsigned char crc32[4]; + unsigned char compressed_size[8]; + unsigned char size[8]; + unsigned char _end[1]; +}; + struct zip_dir_trailer { unsigned char magic[4]; unsigned char disk[2]; @@ -65,6 +73,14 @@ struct zip_extra_mtime { unsigned char _end[1]; }; +struct zip64_extra { + unsigned char magic[2]; + unsigned char extra_size[2]; + unsigned char size[8]; + unsigned char compressed_size[8]; + unsigned char _end[1]; +}; + struct zip64_dir_trailer { unsigned char magic[4]; unsigned char record_size[8]; @@ -94,11 +110,15 @@ struct zip64_dir_trailer_locator { */ #define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end) #define ZIP_DATA_DESC_SIZE offsetof(struct zip_data_desc, _end) +#define ZIP64_DATA_DESC_SIZE offsetof(struct zip64_data_desc, _end) #define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end) #define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end) #define ZIP_EXTRA_MTIME_SIZE offsetof(struct zip_extra_mtime, _end) #define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \ (ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags)) +#define ZIP64_EXTRA_SIZE offsetof(struct zip64_extra, _end) +#define ZIP64_EXTRA_PAYLOAD_SIZE \ + (ZIP64_EXTRA_SIZE - offsetof(struct zip64_extra, size)) #define ZIP64_DIR_TRAILER_SIZE offsetof(struct zip64_dir_trailer, _end) #define ZIP64_DIR_TRAILER_RECORD_SIZE \ (ZIP64_DIR_TRAILER_SIZE - \ @@ -202,13 +222,23 @@ static void write_zip_data_desc(unsigned long size, unsigned long compressed_size, unsigned long crc) { - struct zip_data_desc trailer; - - copy_le32(trailer.magic, 0x08074b50); - copy_le32(trailer.crc32, crc); - copy_le32(trailer.compressed_size, compressed_size); - copy_le32(trailer.size, size); - write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE); + if (size >= 0xffffffff || compressed_size >= 0xffffffff) { + struct zip64_data_desc trailer; + copy_le32(trailer.magic, 0x08074b50); + copy_le32(trailer.crc32, crc); + copy_le64(trailer.compressed_size, compressed_size); + copy_le64(trailer.size, size); + write_or_die(1, &trailer, ZIP64_DATA_DESC_SIZE); + zip_offset += ZIP64_DATA_DESC_SIZE; + } else { + struct zip_data_desc trailer; + copy_le32(trailer.magic, 0x08074b50); + copy_le32(trailer.crc32, crc); + copy_le32(trailer.compressed_size, compressed_size); + copy_le32(trailer.size, size); + write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE); + zip_offset += ZIP_DATA_DESC_SIZE; + } } static void set_zip_header_data_desc(struct zip_local_header *header, @@ -252,6 +282,9 @@ static int write_zip_entry(struct archiver_args *args, struct zip_local_header header; uintmax_t offset = zip_offset; struct zip_extra_mtime extra; + struct zip64_extra extra64; + size_t header_extra_size = ZIP_EXTRA_MTIME_SIZE; + int need_zip64_extra = 0; unsigned long attr2; unsigned long compressed_size; unsigned long crc; @@ -344,21 +377,40 @@ static int write_zip_entry(struct archiver_args *args, extra.flags[0] = 1; /* just mtime */ copy_le32(extra.mtime, args->time); + if (size > 0xffffffff || compressed_size > 0xffffffff) + need_zip64_extra = 1; + if (stream && size > 0x7fffffff) + need_zip64_extra = 1; + copy_le32(header.magic, 0x04034b50); copy_le16(header.version, 10); copy_le16(header.flags, flags); copy_le16(header.compression_method, method); copy_le16(header.mtime, zip_time); copy_le16(header.mdate, zip_date); - set_zip_header_data_desc(&header, size, compressed_size, crc); + if (need_zip64_extra) { + set_zip_header_data_desc(&header, 0xffffffff, 0xffffffff, crc); + header_extra_size += ZIP64_EXTRA_SIZE; + } else { + set_zip_header_data_desc(&header, size, compressed_size, crc); + } copy_le16(header.filename_length, pathlen); - copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE); + copy_le16(header.extra_length, header_extra_size); write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE); zip_offset += ZIP_LOCAL_HEADER_SIZE; write_or_die(1, path, pathlen); zip_offset += pathlen; write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE); zip_offset += ZIP_EXTRA_MTIME_SIZE; + if (need_zip64_extra) { + copy_le16(extra64.magic, 0x0001); + copy_le16(extra64.extra_size, ZIP64_EXTRA_PAYLOAD_SIZE); + copy_le64(extra64.size, size); + copy_le64(extra64.compressed_size, compressed_size); + write_or_die(1, &extra64, ZIP64_EXTRA_SIZE); + zip_offset += ZIP64_EXTRA_SIZE; + } + if (stream && method == 0) { unsigned char buf[STREAM_BUFFER_SIZE]; ssize_t readlen; @@ -381,7 +433,6 @@ static int write_zip_entry(struct archiver_args *args, zip_offset += compressed_size; write_zip_data_desc(size, compressed_size, crc); - zip_offset += ZIP_DATA_DESC_SIZE; } else if (stream && method == 8) { unsigned char buf[STREAM_BUFFER_SIZE]; ssize_t readlen; @@ -437,7 +488,6 @@ static int write_zip_entry(struct archiver_args *args, zip_offset += compressed_size; write_zip_data_desc(size, compressed_size, crc); - zip_offset += ZIP_DATA_DESC_SIZE; } else if (compressed_size > 0) { write_or_die(1, out, compressed_size); zip_offset += compressed_size; @@ -446,8 +496,14 @@ static int write_zip_entry(struct archiver_args *args, free(deflated); free(buffer); - if (offset > 0xffffffff) { - zip64_dir_extra_payload_size += 8; + if (compressed_size > 0xffffffff || size > 0xffffffff || + offset > 0xffffffff) { + if (compressed_size >= 0xffffffff) + zip64_dir_extra_payload_size += 8; + if (size >= 0xffffffff) + zip64_dir_extra_payload_size += 8; + if (offset >= 0xffffffff) + zip64_dir_extra_payload_size += 8; zip_dir_extra_size += 2 + 2 + zip64_dir_extra_payload_size; } @@ -459,8 +515,8 @@ static int write_zip_entry(struct archiver_args *args, strbuf_add_le(&zip_dir, 2, zip_time); strbuf_add_le(&zip_dir, 2, zip_date); strbuf_add_le(&zip_dir, 4, crc); - strbuf_add_le(&zip_dir, 4, compressed_size); - strbuf_add_le(&zip_dir, 4, size); + strbuf_add_le(&zip_dir, 4, clamp32(compressed_size)); + strbuf_add_le(&zip_dir, 4, clamp32(size)); strbuf_add_le(&zip_dir, 2, pathlen); strbuf_add_le(&zip_dir, 2, zip_dir_extra_size); strbuf_add_le(&zip_dir, 2, 0); /* comment length */ @@ -473,6 +529,10 @@ static int write_zip_entry(struct archiver_args *args, if (zip64_dir_extra_payload_size) { strbuf_add_le(&zip_dir, 2, 0x0001); /* magic */ strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size); + if (size >= 0xffffffff) + strbuf_add_le(&zip_dir, 8, size); + if (compressed_size >= 0xffffffff) + strbuf_add_le(&zip_dir, 8, compressed_size); if (offset >= 0xffffffff) strbuf_add_le(&zip_dir, 8, offset); } diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index 41183ea2cf..9106c53c4c 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh @@ -178,7 +178,7 @@ test_expect_success EXPENSIVE,UNZIP 'zip archive bigger than 4GB' ' "$GIT_UNZIP" -t many-big.zip ' -test_expect_failure EXPENSIVE,UNZIP,ZIPINFO 'zip archive with files bigger than 4GB' ' +test_expect_success EXPENSIVE,UNZIP,ZIPINFO 'zip archive with files bigger than 4GB' ' # Pack created with: # dd if=/dev/zero of=file bs=1M count=4100 && git hash-object -w file mkdir -p .git/objects/pack && From 4d0912a206a32e9763424363617e8425f049f344 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 24 Apr 2017 23:39:47 -0700 Subject: [PATCH 040/566] test-lib.sh: do not barf under --debug at the end of the test The original did "does $remove_trash exist? Then go one level above and remove it". There was no problem under "--debug", where the variable is left empty, as the first "test -d $remove_trash" would have said "No, it doesn't". With the check implemented in the previous step, we'd always get an error under "--debug". Noticed-by: Jeff King Signed-off-by: Junio C Hamano --- t/test-lib.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index cb0766b9ee..d2f9ad5ae5 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -760,13 +760,16 @@ test_done () { say "1..$test_count$skip_all" fi - test -d "$remove_trash" || - error "Tests passed but trash directory already removed before test cleanup; aborting" + if test -n "$remove_trash" + then + test -d "$remove_trash" || + error "Tests passed but trash directory already removed before test cleanup; aborting" - cd "$(dirname "$remove_trash")" && - rm -rf "$(basename "$remove_trash")" || - error "Tests passed but test cleanup failed; aborting" + cd "$(dirname "$remove_trash")" && + rm -rf "$(basename "$remove_trash")" || + error "Tests passed but test cleanup failed; aborting" + fi test_at_end_hook_ exit 0 ;; From 06478dab4c9fb57dc6b7299eafd18df1bea9ca22 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 23 Apr 2017 17:15:09 -0700 Subject: [PATCH 041/566] test-lib: retire $remove_trash variable The convention "$remove_trash is set to the trash directory that is used during the test, so that it will be removed at the end, but under --debug option we set the varilable to empty string to preserve the directory" made sense back when it was introduced, as there was no $TRASH_DIRECTORY variable. These days, since no tests looks at the variable, it is obscure and even risks that by mistake the variable gets used for something else (e.g. remove_trash=yes) and cause us misbehave. Worse yet, remove_trash was not initialized to an empty string at the beginning, so a stray environment variable the user has could have affected the logic when "--debug" is in use. Rewrite the clean-up sequence in test_done helper to explicitly check the $debug condition and remove the trash directory using the $TRASH_DIRECTORY variable. Note that "go to the directory one level above the trash and then remove it" is kept and this is deliverate; test_at_end_hook_ will keep running from the expected location, and also some platforms may not like a directory that is serving as the $cwd of a still-active process removed. Signed-off-by: Junio C Hamano --- t/test-lib.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index d2f9ad5ae5..51b59c69cb 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -760,15 +760,14 @@ test_done () { say "1..$test_count$skip_all" fi - if test -n "$remove_trash" + if test -z "$debug" then - test -d "$remove_trash" || + test -d "$TRASH_DIRECTORY" || error "Tests passed but trash directory already removed before test cleanup; aborting" - cd "$(dirname "$remove_trash")" && - rm -rf "$(basename "$remove_trash")" || + cd "$TRASH_DIRECTORY/.." && + rm -fr "$TRASH_DIRECTORY" || error "Tests passed but test cleanup failed; aborting" - fi test_at_end_hook_ @@ -924,7 +923,6 @@ case "$TRASH_DIRECTORY" in /*) ;; # absolute path is good *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;; esac -test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$TRASH_DIRECTORY" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" From 38124a40e480c1717326b7bc27bcbca758de908e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 25 Apr 2017 16:46:59 -0700 Subject: [PATCH 042/566] run-command: expose is_executable function Move the logic for 'is_executable()' from help.c to run_command.c and expose it so that callers from outside help.c can access the function. This is to enable run-command to be able to query if a file is executable in a future patch. Signed-off-by: Brandon Williams Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- help.c | 43 +------------------------------------------ run-command.c | 42 ++++++++++++++++++++++++++++++++++++++++++ run-command.h | 1 + 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/help.c b/help.c index bc6cd19cf3..0c65a2d21c 100644 --- a/help.c +++ b/help.c @@ -1,6 +1,7 @@ #include "cache.h" #include "builtin.h" #include "exec_cmd.h" +#include "run-command.h" #include "levenshtein.h" #include "help.h" #include "common-cmds.h" @@ -96,48 +97,6 @@ static void pretty_print_cmdnames(struct cmdnames *cmds, unsigned int colopts) string_list_clear(&list, 0); } -static int is_executable(const char *name) -{ - struct stat st; - - if (stat(name, &st) || /* stat, not lstat */ - !S_ISREG(st.st_mode)) - return 0; - -#if defined(GIT_WINDOWS_NATIVE) - /* - * On Windows there is no executable bit. The file extension - * indicates whether it can be run as an executable, and Git - * has special-handling to detect scripts and launch them - * through the indicated script interpreter. We test for the - * file extension first because virus scanners may make - * it quite expensive to open many files. - */ - if (ends_with(name, ".exe")) - return S_IXUSR; - -{ - /* - * Now that we know it does not have an executable extension, - * peek into the file instead. - */ - char buf[3] = { 0 }; - int n; - int fd = open(name, O_RDONLY); - st.st_mode &= ~S_IXUSR; - if (fd >= 0) { - n = read(fd, buf, 2); - if (n == 2) - /* look for a she-bang */ - if (!strcmp(buf, "#!")) - st.st_mode |= S_IXUSR; - close(fd); - } -} -#endif - return st.st_mode & S_IXUSR; -} - static void list_commands_in_dir(struct cmdnames *cmds, const char *path, const char *prefix) diff --git a/run-command.c b/run-command.c index a97d7bf9f3..2ffbd7e67b 100644 --- a/run-command.c +++ b/run-command.c @@ -117,6 +117,48 @@ static inline void close_pair(int fd[2]) close(fd[1]); } +int is_executable(const char *name) +{ + struct stat st; + + if (stat(name, &st) || /* stat, not lstat */ + !S_ISREG(st.st_mode)) + return 0; + +#if defined(GIT_WINDOWS_NATIVE) + /* + * On Windows there is no executable bit. The file extension + * indicates whether it can be run as an executable, and Git + * has special-handling to detect scripts and launch them + * through the indicated script interpreter. We test for the + * file extension first because virus scanners may make + * it quite expensive to open many files. + */ + if (ends_with(name, ".exe")) + return S_IXUSR; + +{ + /* + * Now that we know it does not have an executable extension, + * peek into the file instead. + */ + char buf[3] = { 0 }; + int n; + int fd = open(name, O_RDONLY); + st.st_mode &= ~S_IXUSR; + if (fd >= 0) { + n = read(fd, buf, 2); + if (n == 2) + /* look for a she-bang */ + if (!strcmp(buf, "#!")) + st.st_mode |= S_IXUSR; + close(fd); + } +} +#endif + return st.st_mode & S_IXUSR; +} + static char *locate_in_PATH(const char *file) { const char *p = getenv("PATH"); diff --git a/run-command.h b/run-command.h index 4fa8f65adb..3932420ec8 100644 --- a/run-command.h +++ b/run-command.h @@ -51,6 +51,7 @@ struct child_process { #define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT } void child_process_init(struct child_process *); void child_process_clear(struct child_process *); +extern int is_executable(const char *name); int start_command(struct child_process *); int finish_command(struct child_process *); From 940283101ce87250cf31a592730386f5061e1286 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 25 Apr 2017 16:47:00 -0700 Subject: [PATCH 043/566] run-command: restrict PATH search to executable files In some situations run-command will incorrectly try (and fail) to execute a directory instead of an executable file. This was observed by having a directory called "ssh" in $PATH before the real ssh and trying to use ssh protoccol, reslting in the following: $ git ls-remote ssh://url fatal: cannot exec 'ssh': Permission denied It ends up being worse and run-command will even try to execute a non-executable file if it preceeds the executable version of a file on the PATH. For example, if PATH=~/bin1:~/bin2:~/bin3 and there exists a directory 'git-hello' in 'bin1', a non-executable file 'git-hello' in bin2 and an executable file 'git-hello' (which prints "Hello World!") in bin3 the following will occur: $ git hello fatal: cannot exec 'git-hello': Permission denied This is due to only checking 'access()' when locating an executable in PATH, which doesn't distinguish between files and directories. Instead use 'is_executable()' which check that the path is to a regular, executable file. Now run-command won't try to execute the directory or non-executable file 'git-hello': $ git hello Hello World! which matches what execvp(3) would have done when asked to execute git-hello with such a $PATH. Reported-by: Brian Hatfield Signed-off-by: Brandon Williams Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- run-command.c | 19 ++++++++++++++++++- t/t0061-run-command.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index 2ffbd7e67b..9e36151bf9 100644 --- a/run-command.c +++ b/run-command.c @@ -159,6 +159,23 @@ int is_executable(const char *name) return st.st_mode & S_IXUSR; } +/* + * Search $PATH for a command. This emulates the path search that + * execvp would perform, without actually executing the command so it + * can be used before fork() to prepare to run a command using + * execve() or after execvp() to diagnose why it failed. + * + * The caller should ensure that file contains no directory + * separators. + * + * Returns the path to the command, as found in $PATH or NULL if the + * command could not be found. The caller inherits ownership of the memory + * used to store the resultant path. + * + * This should not be used on Windows, where the $PATH search rules + * are more complicated (e.g., a search for "foo" should find + * "foo.exe"). + */ static char *locate_in_PATH(const char *file) { const char *p = getenv("PATH"); @@ -179,7 +196,7 @@ static char *locate_in_PATH(const char *file) } strbuf_addstr(&buf, file); - if (!access(buf.buf, F_OK)) + if (is_executable(buf.buf)) return strbuf_detach(&buf, NULL); if (!*end) diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 98c09dd982..e4739170aa 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -37,6 +37,36 @@ test_expect_success !MINGW 'run_command can run a script without a #! line' ' test_cmp empty err ' +test_expect_success 'run_command does not try to execute a directory' ' + test_when_finished "rm -rf bin1 bin2" && + mkdir -p bin1/greet bin2 && + write_script bin2/greet <<-\EOF && + cat bin2/greet + EOF + + PATH=$PWD/bin1:$PWD/bin2:$PATH \ + test-run-command run-command greet >actual 2>err && + test_cmp bin2/greet actual && + test_cmp empty err +' + +test_expect_success POSIXPERM 'run_command passes over non-executable file' ' + test_when_finished "rm -rf bin1 bin2" && + mkdir -p bin1 bin2 && + write_script bin1/greet <<-\EOF && + cat bin1/greet + EOF + chmod -x bin1/greet && + write_script bin2/greet <<-\EOF && + cat bin2/greet + EOF + + PATH=$PWD/bin1:$PWD/bin2:$PATH \ + test-run-command run-command greet >actual 2>err && + test_cmp bin2/greet actual && + test_cmp empty err +' + test_expect_success POSIXPERM 'run_command reports EACCES' ' cat hello-script >hello.sh && chmod -x hello.sh && From 40bcf3188a6f02acba94587593124ccca5a37e2b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2017 08:09:25 +0900 Subject: [PATCH 044/566] repack: accept --threads= and pass it down to pack-objects We already do so for --window= and --depth=; this will help when the user wants to force --threads=1 for reproducible testing without getting affected by racing multiple threads. Signed-off-by: Junio C Hamano --- Documentation/git-repack.txt | 5 ++++- builtin/repack.c | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 26afe6ed54..ae750e9e11 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository SYNOPSIS -------- [verse] -'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=] [--depth=] +'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=] [--depth=] [--threads=] DESCRIPTION ----------- @@ -92,6 +92,9 @@ other objects in that pack they already have locally. to be applied that many times to get to the necessary object. The default value for --window is 10 and --depth is 50. +--threads=:: + This option is passed through to `git pack-objects`. + --window-memory=:: This option provides an additional limit on top of `--window`; the window size will dynamically scale down so as to not take diff --git a/builtin/repack.c b/builtin/repack.c index 677bc7c81a..38ba4ef825 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -155,6 +155,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) int keep_unreachable = 0; const char *window = NULL, *window_memory = NULL; const char *depth = NULL; + const char *threads = NULL; const char *max_pack_size = NULL; int no_reuse_delta = 0, no_reuse_object = 0; int no_update_server_info = 0; @@ -190,6 +191,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("same as the above, but limit memory size instead of entries count")), OPT_STRING(0, "depth", &depth, N_("n"), N_("limits the maximum delta depth")), + OPT_STRING(0, "threads", &threads, N_("n"), + N_("limits the maximum number of threads")), OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"), N_("maximum size of each packfile")), OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, @@ -234,6 +237,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_pushf(&cmd.args, "--window-memory=%s", window_memory); if (depth) argv_array_pushf(&cmd.args, "--depth=%s", depth); + if (threads) + argv_array_pushf(&cmd.args, "--threads=%s", threads); if (max_pack_size) argv_array_pushf(&cmd.args, "--max-pack-size=%s", max_pack_size); if (no_reuse_delta) From 44dc738a39f2c4dba41c0ddacae280d0f88dc71f Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Wed, 26 Apr 2017 13:50:03 -0700 Subject: [PATCH 045/566] sequencer: add newline before adding footers When encountering a commit message that does not end in a newline, sequencer does not complete the line before determining if a blank line should be added. This causes the "(cherry picked..." and sign-off lines to sometimes appear on the same line as the last line of the commit message. This behavior was introduced by commit 967dfd4 ("sequencer: use trailer's trailer layout", 2016-11-29). However, a revert of that commit would not resolve this issue completely: prior to that commit, a conforming footer was deemed to be non-conforming by has_conforming_footer() if there was no terminating newline, resulting in both conforming and non-conforming footers being treated the same when they should not be. Resolve this issue, both for conforming and non-conforming footers, and in both do_pick_commit() and append_signoff(), by always adding a newline to the commit message if it does not end in one before checking the footer for conformity. Reported-by: Brian Norris Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano --- sequencer.c | 11 ++++------ t/t3511-cherry-pick-x.sh | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/sequencer.c b/sequencer.c index d64c97397b..5df080dada 100644 --- a/sequencer.c +++ b/sequencer.c @@ -698,6 +698,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, strbuf_addstr(&msgbuf, skip_blank_lines(p + 2)); if (opts->record_origin) { + strbuf_complete_line(&msgbuf); if (!has_conforming_footer(&msgbuf, NULL, 0)) strbuf_addch(&msgbuf, '\n'); strbuf_addstr(&msgbuf, cherry_picked_prefix); @@ -1362,6 +1363,9 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag) getenv("GIT_COMMITTER_EMAIL"))); strbuf_addch(&sob, '\n'); + if (!ignore_footer) + strbuf_complete_line(msgbuf); + /* * If the whole message buffer is equal to the sob, pretend that we * found a conforming footer with a matching sob @@ -1382,13 +1386,6 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag) * the title and body to be filled in by the user. */ append_newlines = "\n\n"; - } else if (msgbuf->buf[len - 1] != '\n') { - /* - * Incomplete line. Complete the line and add a - * blank one so that there is an empty line between - * the message body and the sob. - */ - append_newlines = "\n\n"; } else if (len == 1) { /* * Buffer contains a single newline. Add another diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh index bf0a5c9887..9888bf34b9 100755 --- a/t/t3511-cherry-pick-x.sh +++ b/t/t3511-cherry-pick-x.sh @@ -208,6 +208,50 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo test_cmp expect actual ' +test_expect_success 'cherry-pick -x handles commits with no NL at end of message' ' + pristine_detach initial && + printf "title\n\nSigned-off-by: A " >msg && + sha1=$(git commit-tree -p initial mesg-with-footer^{tree} actual && + + printf "\n(cherry picked from commit %s)\n" $sha1 >>msg && + test_cmp msg actual +' + +test_expect_success 'cherry-pick -x handles commits with no footer and no NL at end of message' ' + pristine_detach initial && + printf "title\n\nnot a footer" >msg && + sha1=$(git commit-tree -p initial mesg-with-footer^{tree} actual && + + printf "\n\n(cherry picked from commit %s)\n" $sha1 >>msg && + test_cmp msg actual +' + +test_expect_success 'cherry-pick -s handles commits with no NL at end of message' ' + pristine_detach initial && + printf "title\n\nSigned-off-by: A " >msg && + sha1=$(git commit-tree -p initial mesg-with-footer^{tree} actual && + + printf "\nSigned-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n" >>msg && + test_cmp msg actual +' + +test_expect_success 'cherry-pick -s handles commits with no footer and no NL at end of message' ' + pristine_detach initial && + printf "title\n\nnot a footer" >msg && + sha1=$(git commit-tree -p initial mesg-with-footer^{tree} actual && + + printf "\n\nSigned-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n" >>msg && + test_cmp msg actual +' + test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as part of footer' ' pristine_detach initial && sha1=$(git rev-parse mesg-with-cherry-footer^0) && From 505ad91304e1793ee588ccdcc2413c2e0af6f667 Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Wed, 26 Apr 2017 21:15:44 +0200 Subject: [PATCH 046/566] travis-ci: check AsciiDoc/AsciiDoctor stderr output `make` does not necessarily fail with an error code if Asciidoc/AsciiDoctor encounters problems. Anything written to stderr might be a better indicator for problems. Ensure that nothing is written to stderr during a documentation build. The redirects do not work in `sh`, therefore the script uses `bash`. This shouldn't be a problem as the script is only executed on TravisCI. Signed-off-by: Lars Schneider Signed-off-by: Junio C Hamano --- ci/test-documentation.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh index 58962d668a..6214e6acb4 100755 --- a/ci/test-documentation.sh +++ b/ci/test-documentation.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # # Perform sanity checks on documentation and build it. # @@ -9,7 +9,8 @@ make check-builtins make check-docs # Build docs with AsciiDoc -make --jobs=2 doc +make --jobs=2 doc > >(tee stdout.log) 2> >(tee stderr.log >&2) +! test -s stderr.log test -s Documentation/git.html test -s Documentation/git.xml test -s Documentation/git.1 @@ -17,6 +18,8 @@ grep 'tm_mday + (t->tm_mon + 1) * 32 + (t->tm_year + 1900 - 1980) * 512; diff --git a/archive.h b/archive.h index 415e0152e2..62d1d82c1a 100644 --- a/archive.h +++ b/archive.h @@ -9,7 +9,7 @@ struct archiver_args { struct tree *tree; const unsigned char *commit_sha1; const struct commit *commit; - time_t time; + timestamp_t time; struct pathspec pathspec; unsigned int verbose : 1; unsigned int worktree_attributes : 1; diff --git a/builtin/am.c b/builtin/am.c index 2c93adc69c..89914ed875 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -877,7 +877,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr) if (skip_prefix(sb.buf, "# User ", &str)) fprintf(out, "From: %s\n", str); else if (skip_prefix(sb.buf, "# Date ", &str)) { - unsigned long timestamp; + timestamp_t timestamp; long tz, tz2; char *end; diff --git a/builtin/blame.c b/builtin/blame.c index e4b3c7b0eb..f00eda1637 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1561,13 +1561,13 @@ finish: struct commit_info { struct strbuf author; struct strbuf author_mail; - unsigned long author_time; + timestamp_t author_time; struct strbuf author_tz; /* filled only when asked for details */ struct strbuf committer; struct strbuf committer_mail; - unsigned long committer_time; + timestamp_t committer_time; struct strbuf committer_tz; struct strbuf summary; @@ -1578,7 +1578,7 @@ struct commit_info { */ static void get_ac_line(const char *inbuf, const char *what, struct strbuf *name, struct strbuf *mail, - unsigned long *time, struct strbuf *tz) + timestamp_t *time, struct strbuf *tz) { struct ident_split ident; size_t len, maillen, namelen; @@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt) stop_progress(&pi.progress); } -static const char *format_time(unsigned long time, const char *tz_str, +static const char *format_time(timestamp_t time, const char *tz_str, int show_raw_time) { static struct strbuf time_buf = STRBUF_INIT; diff --git a/builtin/fsck.c b/builtin/fsck.c index af7b985c6e..ea3a9f8a70 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type, static int default_refs; static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, - unsigned long timestamp) + timestamp_t timestamp) { struct object *obj; @@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, } static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { const char *refname = cb_data; diff --git a/builtin/gc.c b/builtin/gc.c index 2daede7820..cb1e20aae4 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -33,7 +33,7 @@ static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; static int detach_auto = 1; -static unsigned long gc_log_expire_time; +static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; diff --git a/builtin/log.c b/builtin/log.c index f93ef6c710..fd3d10ec21 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -911,7 +911,7 @@ static void gen_message_id(struct rev_info *info, char *base) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s.%"PRItime".git.%s", base, - (unsigned long) time(NULL), + (timestamp_t) time(NULL), git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT)); info->message_id = strbuf_detach(&buf, NULL); } diff --git a/builtin/merge-base.c b/builtin/merge-base.c index cfe2a796f8..8ed96391c1 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs) } static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *ident, unsigned long timestamp, + const char *ident, timestamp_t timestamp, int tz, const char *message, void *cbdata) { struct rev_collect *revs = cbdata; diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 92a5d8a5d2..4437475017 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -10,7 +10,7 @@ typedef struct rev_name { const char *tip_name; - unsigned long taggerdate; + timestamp_t taggerdate; int generation; int distance; } rev_name; @@ -21,7 +21,7 @@ static long cutoff = LONG_MAX; #define MERGE_TRAVERSAL_WEIGHT 65535 static void name_rev(struct commit *commit, - const char *tip_name, unsigned long taggerdate, + const char *tip_name, timestamp_t taggerdate, int generation, int distance, int deref) { @@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo struct name_ref_data *data = cb_data; int can_abbreviate_output = data->tags_only && data->name_only; int deref = 0; - unsigned long taggerdate = ULONG_MAX; + timestamp_t taggerdate = TIME_MAX; if (data->tags_only && !starts_with(path, "refs/tags/")) return 0; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0fe35d1b5a..9b4ba8a80d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written; static int non_empty; static int reuse_delta = 1, reuse_object = 1; static int keep_unreachable, unpack_unreachable, include_tag; -static unsigned long unpack_unreachable_expiration; +static timestamp_t unpack_unreachable_expiration; static int pack_loose_unreachable; static int local; static int have_non_local_packs; @@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1) static struct oid_array recent_objects; static int loosened_object_can_be_discarded(const struct object_id *oid, - unsigned long mtime) + timestamp_t mtime) { if (!unpack_unreachable_expiration) return 0; diff --git a/builtin/prune.c b/builtin/prune.c index 42633e0c6e..8dcfecde0f 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -13,7 +13,7 @@ static const char * const prune_usage[] = { }; static int show_only; static int verbose; -static unsigned long expire; +static timestamp_t expire; static int show_progress = -1; static int prune_tmp_file(const char *fullpath) @@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) }; char *s; - expire = ULONG_MAX; + expire = TIME_MAX; save_commit_buffer = 0; check_replace_refs = 0; ref_paranoia = 1; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c49333c9b6..b4e22f4449 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK"; static const char *NONCE_SLOP = "SLOP"; static const char *nonce_status; static long nonce_stamp_slop; -static unsigned long nonce_stamp_slop_limit; +static timestamp_t nonce_stamp_slop_limit; static struct ref_transaction *transaction; static enum { @@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out, git_SHA1_Final(out, &ctx); } -static char *prepare_push_cert_nonce(const char *path, unsigned long stamp) +static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) { struct strbuf buf = STRBUF_INIT; unsigned char sha1[20]; @@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key) static const char *check_nonce(const char *buf, size_t len) { char *nonce = find_header(buf, len, "nonce"); - unsigned long stamp, ostamp; + timestamp_t stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; diff --git a/builtin/reflog.c b/builtin/reflog.c index 7472775778..4228d9ff4d 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -16,14 +16,14 @@ static const char reflog_delete_usage[] = static const char reflog_exists_usage[] = "git reflog exists "; -static unsigned long default_reflog_expire; -static unsigned long default_reflog_expire_unreachable; +static timestamp_t default_reflog_expire; +static timestamp_t default_reflog_expire_unreachable; struct cmd_reflog_expire_cb { struct rev_info revs; int stalefix; - unsigned long expire_total; - unsigned long expire_unreachable; + timestamp_t expire_total; + timestamp_t expire_unreachable; int recno; }; @@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1) static void mark_reachable(struct expire_reflog_policy_cb *cb) { struct commit_list *pending; - unsigned long expire_limit = cb->mark_limit; + timestamp_t expire_limit = cb->mark_limit; struct commit_list *leftover = NULL; for (pending = cb->mark_list; pending; pending = pending->next) @@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit * Return true iff the specified reflog entry should be expired. */ static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct expire_reflog_policy_cb *cb = cb_data; @@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus static struct reflog_expire_cfg { struct reflog_expire_cfg *next; - unsigned long expire_total; - unsigned long expire_unreachable; + timestamp_t expire_total; + timestamp_t expire_unreachable; char pattern[FLEX_ARRAY]; } *reflog_expire_cfg, **reflog_expire_cfg_tail; @@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) return ent; } -static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire) +static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire) { if (!value) return config_error_nonbool(var); @@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb) { const char *pattern, *key; int pattern_len; - unsigned long expire; + timestamp_t expire; int slot; struct reflog_expire_cfg *ent; @@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) { struct expire_reflog_policy_cb cb; - unsigned long now = time(NULL); + timestamp_t now = time(NULL); int i, status, do_all; int explicit_expiry = 0; unsigned int flags = 0; @@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) } static int count_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct expire_reflog_policy_cb *cb = cb_data; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 19756595d5..8860f429b0 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) base = strtoul(reflog_base, &ep, 10); if (*ep) { /* Ah, that is a date spec... */ - unsigned long at; + timestamp_t at; at = approxidate(reflog_base); read_ref_at(ref, flags, at, -1, oid.hash, NULL, NULL, NULL, &base); @@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) char *logmsg; char *nth_desc; const char *msg; - unsigned long timestamp; + timestamp_t timestamp; int tz; if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg, diff --git a/builtin/worktree.c b/builtin/worktree.c index 9993ded41a..74f9b18d40 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -30,7 +30,7 @@ struct add_opts { static int show_only; static int verbose; -static unsigned long expire; +static timestamp_t expire; static int prune_worktree(const char *id, struct strbuf *reason) { @@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix) OPT_END() }; - expire = ULONG_MAX; + expire = TIME_MAX; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac) usage_with_options(worktree_usage, options); diff --git a/bundle.c b/bundle.c index f43bfcf5ff..05e014fc5a 100644 --- a/bundle.c +++ b/bundle.c @@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs) unsigned long size; enum object_type type; char *buf = NULL, *line, *lineend; - unsigned long date; + timestamp_t date; int result = 1; if (revs->max_age == -1 && revs->min_age == -1) diff --git a/cache.h b/cache.h index ba27595d54..f3a02993b2 100644 --- a/cache.h +++ b/cache.h @@ -1476,18 +1476,18 @@ struct date_mode { #define DATE_MODE(t) date_mode_from_type(DATE_##t) struct date_mode *date_mode_from_type(enum date_mode_type type); -const char *show_date(unsigned long time, int timezone, const struct date_mode *mode); -void show_date_relative(unsigned long time, int tz, const struct timeval *now, +const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode); +void show_date_relative(timestamp_t time, int tz, const struct timeval *now, struct strbuf *timebuf); int parse_date(const char *date, struct strbuf *out); -int parse_date_basic(const char *date, unsigned long *timestamp, int *offset); -int parse_expiry_date(const char *date, unsigned long *timestamp); +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset); +int parse_expiry_date(const char *date, timestamp_t *timestamp); void datestamp(struct strbuf *out); #define approxidate(s) approxidate_careful((s), NULL) -unsigned long approxidate_careful(const char *, int *); -unsigned long approxidate_relative(const char *date, const struct timeval *now); +timestamp_t approxidate_careful(const char *, int *); +timestamp_t approxidate_relative(const char *date, const struct timeval *now); void parse_date_format(const char *format, struct date_mode *mode); -int date_overflows(unsigned long date); +int date_overflows(timestamp_t date); #define IDENT_STRICT 1 #define IDENT_NO_DATE 2 diff --git a/commit.c b/commit.c index 0d2d0fa198..99a62b90ee 100644 --- a/commit.c +++ b/commit.c @@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name) return commit; } -static unsigned long parse_commit_date(const char *buf, const char *tail) +static timestamp_t parse_commit_date(const char *buf, const char *tail) { const char *dateptr; @@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm static int commit_list_compare_by_date(const void *a, const void *b) { - unsigned long a_date = ((const struct commit_list *)a)->item->date; - unsigned long b_date = ((const struct commit_list *)b)->item->date; + timestamp_t a_date = ((const struct commit_list *)a)->item->date; + timestamp_t b_date = ((const struct commit_list *)b)->item->date; if (a_date < b_date) return 1; if (a_date > b_date) @@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date, const char *ident_line; size_t ident_len; char *date_end; - unsigned long date; + timestamp_t date; ident_line = find_commit_header(buffer, "author", &ident_len); if (!ident_line) @@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_, { const struct commit *a = a_, *b = b_; struct author_date_slab *author_date = cb_data; - unsigned long a_date = *(author_date_slab_at(author_date, a)); - unsigned long b_date = *(author_date_slab_at(author_date, b)); + timestamp_t a_date = *(author_date_slab_at(author_date, a)); + timestamp_t b_date = *(author_date_slab_at(author_date, b)); /* newer commits with larger date first */ if (a_date < b_date) diff --git a/commit.h b/commit.h index 7b1986d5c8..c9d887b5e5 100644 --- a/commit.h +++ b/commit.h @@ -17,7 +17,7 @@ struct commit { struct object object; void *util; unsigned int index; - unsigned long date; + timestamp_t date; struct commit_list *parents; struct tree *tree; }; diff --git a/config.c b/config.c index 1a4d85537b..3247bfaa02 100644 --- a/config.c +++ b/config.c @@ -1926,7 +1926,7 @@ int git_config_get_expiry(const char *key, const char **output) if (ret) return ret; if (strcmp(*output, "now")) { - unsigned long now = approxidate("now"); + timestamp_t now = approxidate("now"); if (approxidate(*output) >= now) git_die_config(key, _("Invalid %s: '%s'"), key, *output); } diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c index 46c5937526..f3814cc47a 100644 --- a/credential-cache--daemon.c +++ b/credential-cache--daemon.c @@ -8,7 +8,7 @@ static struct tempfile socket_file; struct credential_cache_entry { struct credential item; - unsigned long expiration; + timestamp_t expiration; }; static struct credential_cache_entry *entries; static int entries_nr; @@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c) e->expiration = 0; } -static int check_expirations(void) +static timestamp_t check_expirations(void) { - static unsigned long wait_for_entry_until; + static timestamp_t wait_for_entry_until; int i = 0; - unsigned long now = time(NULL); - unsigned long next = (unsigned long)-1; + timestamp_t now = time(NULL); + timestamp_t next = TIME_MAX; /* * Initially give the client 30 seconds to actually contact us @@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out) static int serve_cache_loop(int fd) { struct pollfd pfd; - unsigned long wakeup; + timestamp_t wakeup; wakeup = check_expirations(); if (!wakeup) diff --git a/date.c b/date.c index 5c33dfd8ee..92ab31aa44 100644 --- a/date.c +++ b/date.c @@ -39,7 +39,7 @@ static const char *weekday_names[] = { "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays" }; -static time_t gm_time_t(unsigned long time, int tz) +static time_t gm_time_t(timestamp_t time, int tz) { int minutes; @@ -54,7 +54,7 @@ static time_t gm_time_t(unsigned long time, int tz) * thing, which means that tz -0100 is passed in as the integer -100, * even though it means "sixty minutes off" */ -static struct tm *time_to_tm(unsigned long time, int tz) +static struct tm *time_to_tm(timestamp_t time, int tz) { time_t t = gm_time_t(time, tz); return gmtime(&t); @@ -64,7 +64,7 @@ static struct tm *time_to_tm(unsigned long time, int tz) * What value of "tz" was in effect back then at "time" in the * local timezone? */ -static int local_tzoffset(unsigned long time) +static int local_tzoffset(timestamp_t time) { time_t t, t_local; struct tm tm; @@ -88,11 +88,11 @@ static int local_tzoffset(unsigned long time) return offset * eastwest; } -void show_date_relative(unsigned long time, int tz, +void show_date_relative(timestamp_t time, int tz, const struct timeval *now, struct strbuf *timebuf) { - unsigned long diff; + timestamp_t diff; if (now->tv_sec < time) { strbuf_addstr(timebuf, _("in the future")); return; @@ -140,9 +140,9 @@ void show_date_relative(unsigned long time, int tz, } /* Give years and months for 5 years or so */ if (diff < 1825) { - unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2); - unsigned long years = totalmonths / 12; - unsigned long months = totalmonths % 12; + timestamp_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2); + timestamp_t years = totalmonths / 12; + timestamp_t months = totalmonths % 12; if (months) { struct strbuf sb = STRBUF_INIT; strbuf_addf(&sb, Q_("%"PRItime" year", "%"PRItime" years", years), years); @@ -172,7 +172,7 @@ struct date_mode *date_mode_from_type(enum date_mode_type type) return &mode; } -const char *show_date(unsigned long time, int tz, const struct date_mode *mode) +const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) { struct tm *tm; static struct strbuf timebuf = STRBUF_INIT; @@ -425,7 +425,7 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, return 0; } -static int match_multi_number(unsigned long num, char c, const char *date, +static int match_multi_number(timestamp_t num, char c, const char *date, char *end, struct tm *tm, time_t now) { struct tm now_tm; @@ -508,7 +508,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt { int n; char *end; - unsigned long num; + timestamp_t num; num = parse_timestamp(date, &end, 10); @@ -635,7 +635,7 @@ static int match_tz(const char *date, int *offp) return end - date; } -static void date_string(unsigned long date, int offset, struct strbuf *buf) +static void date_string(timestamp_t date, int offset, struct strbuf *buf) { int sign = '+'; @@ -650,16 +650,16 @@ static void date_string(unsigned long date, int offset, struct strbuf *buf) * Parse a string like "0 +0000" as ancient timestamp near epoch, but * only when it appears not as part of any other string. */ -static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset) +static int match_object_header_date(const char *date, timestamp_t *timestamp, int *offset) { char *end; - unsigned long stamp; + timestamp_t stamp; int ofs; if (*date < '0' || '9' < *date) return -1; stamp = parse_timestamp(date, &end, 10); - if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-')) + if (*end != ' ' || stamp == TIME_MAX || (end[1] != '+' && end[1] != '-')) return -1; date = end + 2; ofs = strtol(date, &end, 10); @@ -675,11 +675,11 @@ static int match_object_header_date(const char *date, unsigned long *timestamp, /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822 (i.e. English) day/month names, and it doesn't work correctly with %z. */ -int parse_date_basic(const char *date, unsigned long *timestamp, int *offset) +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset) { struct tm tm; int tm_gmt; - unsigned long dummy_timestamp; + timestamp_t dummy_timestamp; int dummy_offset; if (!timestamp) @@ -747,7 +747,7 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset) return 0; /* success */ } -int parse_expiry_date(const char *date, unsigned long *timestamp) +int parse_expiry_date(const char *date, timestamp_t *timestamp) { int errors = 0; @@ -762,7 +762,7 @@ int parse_expiry_date(const char *date, unsigned long *timestamp) * of the past, and there is nothing from the future * to be kept. */ - *timestamp = ULONG_MAX; + *timestamp = TIME_MAX; else *timestamp = approxidate_careful(date, &errors); @@ -771,7 +771,7 @@ int parse_expiry_date(const char *date, unsigned long *timestamp) int parse_date(const char *date, struct strbuf *result) { - unsigned long timestamp; + timestamp_t timestamp; int offset; if (parse_date_basic(date, ×tamp, &offset)) return -1; @@ -845,7 +845,7 @@ void datestamp(struct strbuf *out) * Relative time update (eg "2 days ago"). If we haven't set the time * yet, we need to set it from current time. */ -static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec) +static time_t update_tm(struct tm *tm, struct tm *now, time_t sec) { time_t n; @@ -1066,7 +1066,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num, time_t now) { char *end; - unsigned long number = parse_timestamp(date, &end, 10); + timestamp_t number = parse_timestamp(date, &end, 10); switch (*end) { case ':': @@ -1114,9 +1114,9 @@ static void pending_number(struct tm *tm, int *num) } } -static unsigned long approxidate_str(const char *date, - const struct timeval *tv, - int *error_ret) +static timestamp_t approxidate_str(const char *date, + const struct timeval *tv, + int *error_ret) { int number = 0; int touched = 0; @@ -1148,12 +1148,12 @@ static unsigned long approxidate_str(const char *date, pending_number(&tm, &number); if (!touched) *error_ret = 1; - return update_tm(&tm, &now, 0); + return (timestamp_t)update_tm(&tm, &now, 0); } -unsigned long approxidate_relative(const char *date, const struct timeval *tv) +timestamp_t approxidate_relative(const char *date, const struct timeval *tv) { - unsigned long timestamp; + timestamp_t timestamp; int offset; int errors = 0; @@ -1162,10 +1162,10 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv) return approxidate_str(date, tv, &errors); } -unsigned long approxidate_careful(const char *date, int *error_ret) +timestamp_t approxidate_careful(const char *date, int *error_ret) { struct timeval tv; - unsigned long timestamp; + timestamp_t timestamp; int offset; int dummy = 0; if (!error_ret) @@ -1180,12 +1180,12 @@ unsigned long approxidate_careful(const char *date, int *error_ret) return approxidate_str(date, &tv, error_ret); } -int date_overflows(unsigned long t) +int date_overflows(timestamp_t t) { time_t sys; - /* If we overflowed our unsigned long, that's bad... */ - if (t == ULONG_MAX) + /* If we overflowed our timestamp data type, that's bad... */ + if ((uintmax_t)t >= TIME_MAX) return 1; /* diff --git a/fetch-pack.c b/fetch-pack.c index a1b8ea59e8..fbe52f0234 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -392,7 +392,7 @@ static int find_common(struct fetch_pack_args *args, if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); if (args->deepen_since) { - unsigned long max_age = approxidate(args->deepen_since); + timestamp_t max_age = approxidate(args->deepen_since); packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age); } if (args->deepen_not) { @@ -581,7 +581,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid, } static void mark_recent_complete_commits(struct fetch_pack_args *args, - unsigned long cutoff) + timestamp_t cutoff) { while (complete && cutoff <= complete->item->date) { print_verbose(args, _("Marking %s as complete"), @@ -668,7 +668,7 @@ static int everything_local(struct fetch_pack_args *args, { struct ref *ref; int retval; - unsigned long cutoff = 0; + timestamp_t cutoff = 0; save_commit_buffer = 0; diff --git a/git-compat-util.h b/git-compat-util.h index cd522903ed..72c12173a1 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -319,8 +319,10 @@ extern char *gitdirname(char *); #define PRIo32 "o" #endif +typedef unsigned long timestamp_t; #define PRItime "lu" #define parse_timestamp strtoul +#define TIME_MAX ULONG_MAX #ifndef PATH_SEP #define PATH_SEP ':' diff --git a/http-backend.c b/http-backend.c index eef0a361f4..d6ea607533 100644 --- a/http-backend.c +++ b/http-backend.c @@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value) strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value); } -static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when) +static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when) { const char *value = show_date(when, 0, DATE_MODE(RFC2822)); hdr_str(hdr, name, value); @@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr) static void hdr_cache_forever(struct strbuf *hdr) { - unsigned long now = time(NULL); + timestamp_t now = time(NULL); hdr_date(hdr, "Date", now); hdr_date(hdr, "Expires", now + 31536000); hdr_str(hdr, "Cache-Control", "public, max-age=31536000"); diff --git a/parse-options-cb.c b/parse-options-cb.c index 7419780a9b..a6810f295c 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset) int parse_opt_approxidate_cb(const struct option *opt, const char *arg, int unset) { - *(unsigned long *)(opt->value) = approxidate(arg); + *(timestamp_t *)(opt->value) = approxidate(arg); return 0; } int parse_opt_expiry_date_cb(const struct option *opt, const char *arg, int unset) { - return parse_expiry_date(arg, (unsigned long *)opt->value); + return parse_expiry_date(arg, (timestamp_t *)opt->value); } int parse_opt_color_flag_cb(const struct option *opt, const char *arg, diff --git a/pretty.c b/pretty.c index 24fb0c7906..587d48371b 100644 --- a/pretty.c +++ b/pretty.c @@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len, const char *show_ident_date(const struct ident_split *ident, const struct date_mode *mode) { - unsigned long date = 0; + timestamp_t date = 0; long tz = 0; if (ident->date_begin && ident->date_end) diff --git a/reachable.c b/reachable.c index a8a979bd4f..682418f5d2 100644 --- a/reachable.c +++ b/reachable.c @@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data) struct recent_data { struct rev_info *revs; - unsigned long timestamp; + timestamp_t timestamp; }; static void add_recent_object(const struct object_id *oid, - unsigned long mtime, + timestamp_t mtime, struct recent_data *data) { struct object *obj; @@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid, } int add_unseen_recent_objects_to_traversal(struct rev_info *revs, - unsigned long timestamp) + timestamp_t timestamp) { struct recent_data data; int r; @@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs, } void mark_reachable_objects(struct rev_info *revs, int mark_reflog, - unsigned long mark_recent, - struct progress *progress) + timestamp_t mark_recent, struct progress *progress) { struct connectivity_progress cp; diff --git a/reachable.h b/reachable.h index d23efc36ec..3c00fa0526 100644 --- a/reachable.h +++ b/reachable.h @@ -3,8 +3,8 @@ struct progress; extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs, - unsigned long timestamp); + timestamp_t timestamp); extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, - unsigned long mark_recent, struct progress *); + timestamp_t mark_recent, struct progress *); #endif diff --git a/ref-filter.c b/ref-filter.c index c7836ae07b..1fc5e9970d 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam { const char *eoemail = strstr(buf, "> "); char *zone; - unsigned long timestamp; + timestamp_t timestamp; long tz; struct date_mode date_mode = { DATE_NORMAL }; const char *formatp; @@ -869,7 +869,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam if (!eoemail) goto bad; timestamp = parse_timestamp(eoemail + 2, &zone, 10); - if (timestamp == ULONG_MAX) + if (timestamp == TIME_MAX) goto bad; tz = strtol(zone, NULL, 10); if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) diff --git a/reflog-walk.c b/reflog-walk.c index 99679f5825..3ca5ed8415 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -12,7 +12,7 @@ struct complete_reflogs { struct reflog_info { struct object_id ooid, noid; char *email; - unsigned long timestamp; + timestamp_t timestamp; int tz; char *message; } *items; @@ -20,7 +20,7 @@ struct complete_reflogs { }; static int read_one_reflog(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct complete_reflogs *array = cb_data; @@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref) } static int get_reflog_recno_by_time(struct complete_reflogs *array, - unsigned long timestamp) + timestamp_t timestamp) { int i; for (i = array->nr - 1; i >= 0; i--) @@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info) int add_reflog_for_walk(struct reflog_walk_info *info, struct commit *commit, const char *name) { - unsigned long timestamp = 0; + timestamp_t timestamp = 0; int recno = -1; struct string_list_item *item; struct complete_reflogs *reflogs; diff --git a/refs.c b/refs.c index d1e1b4399b..6841bd9f71 100644 --- a/refs.c +++ b/refs.c @@ -712,7 +712,7 @@ int is_branch(const char *refname) struct read_ref_at_cb { const char *refname; - unsigned long at_time; + timestamp_t at_time; int cnt; int reccnt; unsigned char *sha1; @@ -721,15 +721,15 @@ struct read_ref_at_cb { unsigned char osha1[20]; unsigned char nsha1[20]; int tz; - unsigned long date; + timestamp_t date; char **msg; - unsigned long *cutoff_time; + timestamp_t *cutoff_time; int *cutoff_tz; int *cutoff_cnt; }; static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; @@ -776,7 +776,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, } static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; @@ -796,9 +796,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid return 1; } -int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt, +int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt, unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) + timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { struct read_ref_at_cb cb; diff --git a/refs.h b/refs.h index 49e97d7d5f..2b80d37fd3 100644 --- a/refs.h +++ b/refs.h @@ -317,9 +317,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err /** Reads log for the value of ref during at_time. **/ int read_ref_at(const char *refname, unsigned int flags, - unsigned long at_time, int cnt, + timestamp_t at_time, int cnt, unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); + timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt); /** Check if a particular reflog exists */ int refs_reflog_exists(struct ref_store *refs, const char *refname); @@ -356,7 +356,7 @@ int delete_reflog(const char *refname); /* iterate over reflog entries */ typedef int each_reflog_ent_fn( struct object_id *old_oid, struct object_id *new_oid, - const char *committer, unsigned long timestamp, + const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data); int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname, @@ -616,7 +616,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname, typedef int reflog_expiry_should_prune_fn(unsigned char *osha1, unsigned char *nsha1, const char *email, - unsigned long timestamp, int tz, + timestamp_t timestamp, int tz, const char *message, void *cb_data); typedef void reflog_expiry_cleanup_fn(void *cb_data); diff --git a/refs/files-backend.c b/refs/files-backend.c index ecbb13dc4b..6646dcfc83 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3237,7 +3237,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c { struct object_id ooid, noid; char *email_end, *message; - unsigned long timestamp; + timestamp_t timestamp; int tz; const char *p = sb->buf; @@ -4114,7 +4114,7 @@ struct expire_reflog_cb { }; static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct expire_reflog_cb *cb = cb_data; diff --git a/revision.c b/revision.c index 7ff61ff5f7..8a8c1789c7 100644 --- a/revision.c +++ b/revision.c @@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) /* How many extra uninteresting commits we want to see.. */ #define SLOP 5 -static int still_interesting(struct commit_list *src, unsigned long date, int slop, +static int still_interesting(struct commit_list *src, timestamp_t date, int slop, struct commit **interesting_cache) { /* @@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs) static int limit_list(struct rev_info *revs) { int slop = SLOP; - unsigned long date = ~0ul; + timestamp_t date = TIME_MAX; struct commit_list *list = revs->commits; struct commit_list *newlist = NULL; struct commit_list **p = &newlist; @@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data) } static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { handle_one_reflog_commit(ooid, cb_data); diff --git a/revision.h b/revision.h index 14886ec92b..0d9e68b36e 100644 --- a/revision.h +++ b/revision.h @@ -181,8 +181,8 @@ struct rev_info { /* special limits */ int skip_count; int max_count; - unsigned long max_age; - unsigned long min_age; + timestamp_t max_age; + timestamp_t min_age; int min_parents; int max_parents; int (*include_check)(struct commit *, void *); diff --git a/sha1_name.c b/sha1_name.c index 8eec9f7c1b..35c1e2a9e3 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -660,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1, if (reflog_len) { int nth, i; - unsigned long at_time; - unsigned long co_time; + timestamp_t at_time; + timestamp_t co_time; int co_tz, co_cnt; /* Is it asking for N-th entry, or approxidate? */ @@ -1054,7 +1054,7 @@ struct grab_nth_branch_switch_cbdata { }; static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct grab_nth_branch_switch_cbdata *cb = cb_data; diff --git a/t/helper/test-date.c b/t/helper/test-date.c index 269040f028..f414a3ac67 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -27,7 +27,7 @@ static void show_dates(const char **argv, const char *format) parse_date_format(format, &mode); for (; *argv; argv++) { char *arg; - time_t t; + timestamp_t t; int tz; /* @@ -48,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now) struct strbuf result = STRBUF_INIT; for (; *argv; argv++) { - unsigned long t; + timestamp_t t; int tz; strbuf_reset(&result); @@ -65,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now) static void parse_approxidate(const char **argv, struct timeval *now) { for (; *argv; argv++) { - time_t t; + timestamp_t t; t = approxidate_relative(*argv, now); printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601))); } @@ -96,7 +96,7 @@ int cmd_main(int argc, const char **argv) else if (!strcmp(*argv, "approxidate")) parse_approxidate(argv+1, &now); else if (!strcmp(*argv, "is64bit")) - return sizeof(unsigned long) == 8 ? 0 : 1; + return sizeof(timestamp_t) == 8 ? 0 : 1; else if (!strcmp(*argv, "time_t-is64bit")) return sizeof(time_t) == 8 ? 0 : 1; else diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 7d93627e45..75fe883aac 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -5,7 +5,7 @@ static int boolean = 0; static int integer = 0; static unsigned long magnitude = 0; -static unsigned long timestamp; +static timestamp_t timestamp; static int abbrev = 7; static int verbose = -1; /* unspecified */ static int dry_run = 0, quiet = 0; diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index a436bfdb05..9077ec2c33 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -138,7 +138,7 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv) } static int each_reflog(struct object_id *old_oid, struct object_id *new_oid, - const char *committer, unsigned long timestamp, + const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { printf("%s %s %s %"PRItime" %d %s\n", diff --git a/tag.c b/tag.c index 9b6725e02c..d71b67e8d8 100644 --- a/tag.c +++ b/tag.c @@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1) return object_as_type(obj, OBJ_TAG, 0); } -static unsigned long parse_tag_date(const char *buf, const char *tail) +static timestamp_t parse_tag_date(const char *buf, const char *tail) { const char *dateptr; diff --git a/tag.h b/tag.h index a5721b6731..2abb3726fb 100644 --- a/tag.h +++ b/tag.h @@ -9,7 +9,7 @@ struct tag { struct object object; struct object *tagged; char *tag; - unsigned long date; + timestamp_t date; }; extern struct tag *lookup_tag(const unsigned char *sha1); diff --git a/upload-pack.c b/upload-pack.c index 4966f0b8a0..97da13e6a5 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = { #define CLIENT_SHALLOW (1u << 18) #define HIDDEN_REF (1u << 19) -static unsigned long oldest_have; +static timestamp_t oldest_have; static int deepen_relative; static int multi_ack; @@ -735,7 +735,7 @@ static void receive_needs(void) struct string_list deepen_not = STRING_LIST_INIT_DUP; int depth = 0; int has_non_tip = 0; - unsigned long deepen_since = 0; + timestamp_t deepen_since = 0; int deepen_rev_list = 0; shallow_nr = 0; diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c index 6c9f2866d8..5a89db30e3 100644 --- a/vcs-svn/fast_export.c +++ b/vcs-svn/fast_export.c @@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref) } void fast_export_begin_note(uint32_t revision, const char *author, - const char *log, unsigned long timestamp, const char *note_ref) + const char *log, timestamp_t timestamp, const char *note_ref) { static int firstnote = 1; size_t loglen = strlen(log); @@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN]; void fast_export_begin_commit(uint32_t revision, const char *author, const struct strbuf *log, const char *uuid, const char *url, - unsigned long timestamp, const char *local_ref) + timestamp_t timestamp, const char *local_ref) { static const struct strbuf empty = STRBUF_INIT; if (!log) diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h index c8b5adb811..b9a3b71c99 100644 --- a/vcs-svn/fast_export.h +++ b/vcs-svn/fast_export.h @@ -11,10 +11,10 @@ void fast_export_delete(const char *path); void fast_export_modify(const char *path, uint32_t mode, const char *dataref); void fast_export_note(const char *committish, const char *dataref); void fast_export_begin_note(uint32_t revision, const char *author, - const char *log, unsigned long timestamp, const char *note_ref); + const char *log, timestamp_t timestamp, const char *note_ref); void fast_export_begin_commit(uint32_t revision, const char *author, const struct strbuf *log, const char *uuid,const char *url, - unsigned long timestamp, const char *local_ref); + timestamp_t timestamp, const char *local_ref); void fast_export_end_commit(uint32_t revision); void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input); void fast_export_buf_to_data(const struct strbuf *data); diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c index e4b395963b..1846685a21 100644 --- a/vcs-svn/svndump.c +++ b/vcs-svn/svndump.c @@ -47,7 +47,7 @@ static struct { static struct { uint32_t revision; - unsigned long timestamp; + timestamp_t timestamp; struct strbuf log, author, note; } rev_ctx; diff --git a/wt-status.c b/wt-status.c index 0375484962..1b1d644b85 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1387,7 +1387,7 @@ struct grab_1st_switch_cbdata { }; static int grab_1st_switch(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct grab_1st_switch_cbdata *cb = cb_data; From 1e65a982da0e9dd4eac440e82392a8b7c72b3def Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 26 Apr 2017 21:29:36 +0200 Subject: [PATCH 048/566] date.c: abort if the system time cannot handle one of our timestamps We are about to switch to a new data type for time stamps that is definitely not smaller or equal, but larger or equal to time_t. So before using the system functions to process or format timestamps, let's make extra certain that they can handle what we feed them. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- date.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/date.c b/date.c index 92ab31aa44..63fa99685e 100644 --- a/date.c +++ b/date.c @@ -46,7 +46,17 @@ static time_t gm_time_t(timestamp_t time, int tz) minutes = tz < 0 ? -tz : tz; minutes = (minutes / 100)*60 + (minutes % 100); minutes = tz < 0 ? -minutes : minutes; - return time + minutes * 60; + + if (minutes > 0) { + if (unsigned_add_overflows(time, minutes * 60)) + die("Timestamp+tz too large: %"PRItime" +%04d", + time, tz); + } else if (time < -minutes * 60) + die("Timestamp before Unix epoch: %"PRItime" %04d", time, tz); + time += minutes * 60; + if (date_overflows(time)) + die("Timestamp too large for this system: %"PRItime, time); + return (time_t)time; } /* @@ -70,7 +80,10 @@ static int local_tzoffset(timestamp_t time) struct tm tm; int offset, eastwest; - t = time; + if (date_overflows(time)) + die("Timestamp too large for this system: %"PRItime, time); + + t = (time_t)time; localtime_r(&t, &tm); t_local = tm_to_time_t(&tm); From 28f4aee3fb1a45ebcda8ff84f94d6d2d0053f887 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 26 Apr 2017 21:29:42 +0200 Subject: [PATCH 049/566] use uintmax_t for timestamps Previously, we used `unsigned long` for timestamps. This was only a good choice on Linux, where we know implicitly that `unsigned long` is what is used for `time_t`. However, we want to use a different data type for timestamps for two reasons: - there is nothing that says that `unsigned long` should be the same data type as `time_t`, and indeed, on 64-bit Windows for example, it is not: `unsigned long` is 32-bit but `time_t` is 64-bit. - even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is 32-bit, we *want* to be able to encode timestamps in Git that are currently absurdly far in the future, *even if* the system library is not able to format those timestamps into date strings. So let's just switch to the maximal integer type available, which should be at least 64-bit for all practical purposes these days. It certainly cannot be worse than `unsigned long`, so... Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-compat-util.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/git-compat-util.h b/git-compat-util.h index 72c12173a1..f8349a03bd 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -319,10 +319,10 @@ extern char *gitdirname(char *); #define PRIo32 "o" #endif -typedef unsigned long timestamp_t; -#define PRItime "lu" -#define parse_timestamp strtoul -#define TIME_MAX ULONG_MAX +typedef uintmax_t timestamp_t; +#define PRItime PRIuMAX +#define parse_timestamp strtoumax +#define TIME_MAX UINTMAX_MAX #ifndef PATH_SEP #define PATH_SEP ':' From f131db9e3166c528d3b0352b653eb0d9deca5a65 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 26 Apr 2017 23:25:55 -0400 Subject: [PATCH 050/566] am: fix commit buffer leak in get_commit_info() Calling logmsg_reencode() may allocate a buffer for the commit message (because we need to load it from disk, or because it needs re-encoded). We must "unuse" it afterwards to free it. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/am.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/am.c b/builtin/am.c index 826f18ba12..8dfe8f84fa 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1417,6 +1417,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit) die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid)); state->msg = xstrdup(msg + 2); state->msg_len = strlen(state->msg); + unuse_commit_buffer(commit, buffer); } /** From 2e2bbb9624e10537560c3ac45ff6820ff773b3d6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 26 Apr 2017 23:27:17 -0400 Subject: [PATCH 051/566] am: simplify allocations in get_commit_info() After we call split_ident_line(), we have several begin/end pairs for various parts of the ident. We then copy each into a strbuf to create a single string, and then detach that string. We can instead skip the strbuf entirely and just duplicate the strings directly. This is shorter, and it makes it more obvious that we are not leaking the strbuf (we were not before, because every code path either died or hit a strbuf_detach). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/am.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 8dfe8f84fa..17f394c825 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1376,40 +1376,35 @@ static int get_mail_commit_oid(struct object_id *commit_id, const char *mail) */ static void get_commit_info(struct am_state *state, struct commit *commit) { - const char *buffer, *ident_line, *author_date, *msg; + const char *buffer, *ident_line, *msg; size_t ident_len; struct ident_split ident_split; - struct strbuf sb = STRBUF_INIT; buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); ident_line = find_commit_header(buffer, "author", &ident_len); - if (split_ident_line(&ident_split, ident_line, ident_len) < 0) { - strbuf_add(&sb, ident_line, ident_len); - die(_("invalid ident line: %s"), sb.buf); - } + if (split_ident_line(&ident_split, ident_line, ident_len) < 0) + die(_("invalid ident line: %.*s"), (int)ident_len, ident_line); assert(!state->author_name); if (ident_split.name_begin) { - strbuf_add(&sb, ident_split.name_begin, - ident_split.name_end - ident_split.name_begin); - state->author_name = strbuf_detach(&sb, NULL); + state->author_name = + xmemdupz(ident_split.name_begin, + ident_split.name_end - ident_split.name_begin); } else state->author_name = xstrdup(""); assert(!state->author_email); if (ident_split.mail_begin) { - strbuf_add(&sb, ident_split.mail_begin, - ident_split.mail_end - ident_split.mail_begin); - state->author_email = strbuf_detach(&sb, NULL); + state->author_email = + xmemdupz(ident_split.mail_begin, + ident_split.mail_end - ident_split.mail_begin); } else state->author_email = xstrdup(""); - author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL)); - strbuf_addstr(&sb, author_date); assert(!state->author_date); - state->author_date = strbuf_detach(&sb, NULL); + state->author_date = xstrdup(show_ident_date(&ident_split, DATE_MODE(NORMAL))); assert(!state->msg); msg = strstr(buffer, "\n\n"); From 721f5f1e355648f6b15d79799a3fdc6b4a28352f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 26 Apr 2017 23:28:31 -0400 Subject: [PATCH 052/566] am: shorten ident_split variable name in get_commit_info() The local ident_split variable is often mentioned three times per line when dealing with its begin/end pointer pairs. Let's use a shorter name which lets us get rid of some long lines. Since this is a short self-contained function, readability doesn't suffer. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/am.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 17f394c825..5c747faa05 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1378,33 +1378,31 @@ static void get_commit_info(struct am_state *state, struct commit *commit) { const char *buffer, *ident_line, *msg; size_t ident_len; - struct ident_split ident_split; + struct ident_split id; buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); ident_line = find_commit_header(buffer, "author", &ident_len); - if (split_ident_line(&ident_split, ident_line, ident_len) < 0) + if (split_ident_line(&id, ident_line, ident_len) < 0) die(_("invalid ident line: %.*s"), (int)ident_len, ident_line); assert(!state->author_name); - if (ident_split.name_begin) { + if (id.name_begin) state->author_name = - xmemdupz(ident_split.name_begin, - ident_split.name_end - ident_split.name_begin); - } else + xmemdupz(id.name_begin, id.name_end - id.name_begin); + else state->author_name = xstrdup(""); assert(!state->author_email); - if (ident_split.mail_begin) { + if (id.mail_begin) state->author_email = - xmemdupz(ident_split.mail_begin, - ident_split.mail_end - ident_split.mail_begin); - } else + xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); + else state->author_email = xstrdup(""); assert(!state->author_date); - state->author_date = xstrdup(show_ident_date(&ident_split, DATE_MODE(NORMAL))); + state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL))); assert(!state->msg); msg = strstr(buffer, "\n\n"); From ebdfa294c9e280ee14de27cf9d4ffb2cf82d2f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Thu, 27 Apr 2017 22:25:45 +0200 Subject: [PATCH 053/566] archive-zip: set version field for big files correctly Signal that extractors need to implement spec version 4.5 (or higher) for files with sizes of 4GB and more. Older unzippers might produce truncated results otherwise; they should rather refuse to extract. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index 44ed78f163..e81c5ac15a 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -298,6 +298,7 @@ static int write_zip_entry(struct archiver_args *args, int is_binary = -1; const char *path_without_prefix = path + args->baselen; unsigned int creator_version = 0; + unsigned int version_needed = 10; size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE; size_t zip64_dir_extra_payload_size = 0; @@ -382,8 +383,11 @@ static int write_zip_entry(struct archiver_args *args, if (stream && size > 0x7fffffff) need_zip64_extra = 1; + if (need_zip64_extra) + version_needed = 45; + copy_le32(header.magic, 0x04034b50); - copy_le16(header.version, 10); + copy_le16(header.version, version_needed); copy_le16(header.flags, flags); copy_le16(header.compression_method, method); copy_le16(header.mtime, zip_time); @@ -509,7 +513,7 @@ static int write_zip_entry(struct archiver_args *args, strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */ strbuf_add_le(&zip_dir, 2, creator_version); - strbuf_add_le(&zip_dir, 2, 10); /* version */ + strbuf_add_le(&zip_dir, 2, version_needed); strbuf_add_le(&zip_dir, 2, flags); strbuf_add_le(&zip_dir, 2, method); strbuf_add_le(&zip_dir, 2, zip_time); From 75177c8591aee23374c2ead9c9505ce989a8eec2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 27 Apr 2017 05:01:05 -0400 Subject: [PATCH 054/566] status: fix missing newline when comment chars are disabled When git-status shows tracking data for the current branch in the long format, we try to end the stanza with a blank line. When status.displayCommentPrefix is true, we call color_fprintf_ln() to do so. But when it's false, we call the enigmatic: fputs("", s->fp); which does nothing at all! This is a bug from 7d7d68022 (silence a bunch of format-zero-length warnings, 2014-05-04). Prior to that, we called fprintf_ln() with an empty string. Switching to fputs() meant we needed to include the "newline in the string, but we didn't. So you see: On branch jk/status-tracking-newline Your branch is ahead of 'origin/master' by 1 commit. Changes not staged for commit: modified: foo Untracked files: bar whereas there should be a blank line before the "Changes not staged" line. The fix itself is a one-liner. But we never noticed this bug because t7508 doesn't exercise the ahead/behind code at all. So let's configure an upstream during the initial setup, which means that the code will be exercised as part of all of the various invocations in that script. This makes the diff rather noisy, but should give us good coverage. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t7508-status.sh | 115 ++++++++++++++++++++++++++++++++++++++++++++-- wt-status.c | 2 +- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/t/t7508-status.sh b/t/t7508-status.sh index fb00e6d9b0..a000ed4e7f 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -32,6 +32,17 @@ test_expect_success 'commit -h in broken repository' ' test_i18ngrep "[Uu]sage" broken/usage ' +test_expect_success 'create upstream branch' ' + git checkout -b upstream && + test_commit upstream1 && + test_commit upstream2 && + # leave the first commit on master as root because several + # tests depend on this case; for our upstream we only + # care about commit counts anyway, so a totally divergent + # history is OK + git checkout --orphan master +' + test_expect_success 'setup' ' : >tracked && : >modified && @@ -53,7 +64,9 @@ test_expect_success 'setup' ' echo 1 >dir1/modified && echo 2 >dir2/modified && echo 3 >dir2/added && - git add dir2/added + git add dir2/added && + + git branch --set-upstream-to=upstream ' test_expect_success 'status (1)' ' @@ -75,6 +88,10 @@ EOF test_expect_success 'status --column' ' cat >expect <<\EOF && # On branch master +# Your branch and '\''upstream'\'' have diverged, +# and have 1 and 2 different commits each, respectively. +# (use "git pull" to merge the remote branch into yours) +# # Changes to be committed: # (use "git reset HEAD ..." to unstage) # @@ -105,6 +122,10 @@ test_expect_success 'status --column status.displayCommentPrefix=false' ' cat >expect <<\EOF # On branch master +# Your branch and 'upstream' have diverged, +# and have 1 and 2 different commits each, respectively. +# (use "git pull" to merge the remote branch into yours) +# # Changes to be committed: # (use "git reset HEAD ..." to unstage) # @@ -178,6 +199,9 @@ test_expect_success 'commit ignores status.displayCommentPrefix=false in COMMIT_ cat >expect <<\EOF On branch master +Your branch and 'upstream' have diverged, +and have 1 and 2 different commits each, respectively. + Changes to be committed: new file: dir2/added @@ -248,6 +272,10 @@ test_expect_success 'status with gitignore' ' cat >expect <<\EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 1 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -313,6 +341,10 @@ test_expect_success 'status with gitignore (nothing untracked)' ' cat >expect <<\EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 1 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -348,7 +380,7 @@ output* EOF cat >expect <<\EOF -## master +## master...upstream [ahead 1, behind 2] M dir1/modified A dir2/added ?? dir1/untracked @@ -382,6 +414,10 @@ test_expect_success 'setup dir3' ' test_expect_success 'status -uno' ' cat >expect <..." to unstage) @@ -408,6 +444,9 @@ test_expect_success 'status (status.showUntrackedFiles no)' ' test_expect_success 'status -uno (advice.statusHints false)' ' cat >expect <expect <..." to unstage) @@ -493,6 +536,10 @@ test_expect_success 'status -s (status.showUntrackedFiles normal)' ' test_expect_success 'status -uall' ' cat >expect <..." to unstage) @@ -552,6 +599,10 @@ test_expect_success 'status -s (status.showUntrackedFiles all)' ' test_expect_success 'status with relative paths' ' cat >expect <<\EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 1 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -617,6 +668,10 @@ test_expect_success 'setup unique colors' ' test_expect_success 'status with color.ui' ' cat >expect <<\EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 1 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -675,7 +730,7 @@ test_expect_success 'status -s with color.status' ' ' cat >expect <<\EOF -## master +## master...upstream [ahead 1, behind 2] M dir1/modified A dir2/added ?? dir1/untracked @@ -726,7 +781,7 @@ test_expect_success 'status --porcelain respects -b' ' git status --porcelain -b >output && { - echo "## master" && + echo "## master...upstream [ahead 1, behind 2]" && cat expect } >tmp && mv tmp expect && @@ -739,6 +794,10 @@ test_expect_success 'status --porcelain respects -b' ' test_expect_success 'status without relative paths' ' cat >expect <<\EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 1 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -785,6 +844,10 @@ test_expect_success 'status -s without relative paths' ' test_expect_success 'dry-run of partial commit excluding new file in index' ' cat >expect <..." to unstage) @@ -825,6 +888,10 @@ test_expect_success 'setup status submodule summary' ' test_expect_success 'status submodule summary is disabled by default' ' cat >expect <..." to unstage) @@ -881,6 +948,10 @@ head=$(cd sm && git rev-parse --short=7 --verify HEAD) test_expect_success 'status submodule summary' ' cat >expect <..." to unstage) @@ -939,6 +1010,10 @@ test_expect_success 'status -s submodule summary' ' test_expect_success 'status submodule summary (clean submodule): commit' ' cat >expect <..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) @@ -985,6 +1060,10 @@ test_expect_success 'status -z implies porcelain' ' test_expect_success 'commit --dry-run submodule summary (--amend)' ' cat >expect <..." to unstage) @@ -1038,6 +1117,10 @@ touch .gitmodules test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' ' cat > expect << EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 2 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -1146,6 +1229,10 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with modifie test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" ' cat > expect << EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 2 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -1202,6 +1289,10 @@ head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 -- test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" ' cat > expect << EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 2 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -1282,6 +1373,10 @@ test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary cat > expect << EOF ; On branch master +; Your branch and 'upstream' have diverged, +; and have 2 and 2 different commits each, respectively. +; (use "git pull" to merge the remote branch into yours) +; ; Changes to be committed: ; (use "git reset HEAD ..." to unstage) ; @@ -1329,6 +1424,10 @@ test_expect_success "status (core.commentchar with two chars with submodule summ test_expect_success "--ignore-submodules=all suppresses submodule summary" ' cat > expect << EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 2 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) @@ -1353,6 +1452,10 @@ EOF test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' ' cat > expect << EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 2 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) @@ -1472,6 +1575,10 @@ test_expect_success 'git commit --dry-run will show a staged but ignored submodu git add sm && cat >expect << EOF && On branch master +Your branch and '\''upstream'\'' have diverged, +and have 2 and 2 different commits each, respectively. + (use "git pull" to merge the remote branch into yours) + Changes to be committed: (use "git reset HEAD ..." to unstage) diff --git a/wt-status.c b/wt-status.c index 0375484962..b7ade902fb 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1002,7 +1002,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s) color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c", comment_line_char); else - fputs("", s->fp); + fputs("\n", s->fp); } static int has_unmerged(struct wt_status *s) From 93fdf301de0daa57fd6a694ad4637ec7a3a1041c Mon Sep 17 00:00:00 2001 From: Stephen Kent Date: Fri, 21 Apr 2017 22:42:02 -0700 Subject: [PATCH 055/566] status: add color config slots for branch info in "--short --branch" Add color config slots to be used in the status short-format when displaying local and remote tracking branch information. [jc: rebased on top of Peff's fix to 'git status' and tweaked the test to check both local and remote-tracking branch output] Signed-off-by: Stephen Kent Signed-off-by: Junio C Hamano --- Documentation/config.txt | 5 ++++- builtin/commit.c | 4 ++++ contrib/completion/git-completion.bash | 2 ++ t/t7508-status.sh | 6 ++++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 475e874d51..96e9cf8b73 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1137,7 +1137,10 @@ color.status.:: `untracked` (files which are not tracked by Git), `branch` (the current branch), `nobranch` (the color the 'no branch' warning is shown in, defaulting - to red), or + to red), + `localBranch` or `remoteBranch` (the local and remote branch names, + respectively, when branch and tracking information is displayed in the + status short-format), or `unmerged` (files which have unmerged changes). color.ui:: diff --git a/builtin/commit.c b/builtin/commit.c index 1d805f5da8..9028bfacf8 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1263,6 +1263,10 @@ static int parse_status_slot(const char *slot) return WT_STATUS_NOBRANCH; if (!strcasecmp(slot, "unmerged")) return WT_STATUS_UNMERGED; + if (!strcasecmp(slot, "localBranch")) + return WT_STATUS_LOCAL_BRANCH; + if (!strcasecmp(slot, "remoteBranch")) + return WT_STATUS_REMOTE_BRANCH; return -1; } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b617019075..72c6d58965 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2378,7 +2378,9 @@ _git_config () color.status.added color.status.changed color.status.header + color.status.localBranch color.status.nobranch + color.status.remoteBranch color.status.unmerged color.status.untracked color.status.updated diff --git a/t/t7508-status.sh b/t/t7508-status.sh index a000ed4e7f..567c4d4bab 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -661,7 +661,9 @@ test_expect_success 'status --porcelain ignores relative paths setting' ' test_expect_success 'setup unique colors' ' git config status.color.untracked blue && - git config status.color.branch green + git config status.color.branch green && + git config status.color.localBranch yellow && + git config status.color.remoteBranch cyan ' @@ -730,7 +732,7 @@ test_expect_success 'status -s with color.status' ' ' cat >expect <<\EOF -## master...upstream [ahead 1, behind 2] +## master...upstream [ahead 1, behind 2] M dir1/modified A dir2/added ?? dir1/untracked From 9f41c7a6b3e3cc3ac4a739364400393fa2492880 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Wed, 26 Apr 2017 22:05:23 +0200 Subject: [PATCH 056/566] read-cache: close index.lock in do_write_index Teach do_write_index() to close the index.lock file before getting the mtime and updating the istate.timestamp fields. On Windows, a file's mtime is not updated until the file is closed. On Linux, the mtime is set after the last flush. Signed-off-by: Jeff Hostetler Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- read-cache.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/read-cache.c b/read-cache.c index 9054369dd0..c02b6bef60 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2011,9 +2011,10 @@ void update_index_if_able(struct index_state *istate, struct lock_file *lockfile rollback_lock_file(lockfile); } -static int do_write_index(struct index_state *istate, int newfd, +static int do_write_index(struct index_state *istate, struct tempfile *tempfile, int strip_extensions) { + int newfd = tempfile->fd; git_SHA_CTX c; struct cache_header hdr; int i, err, removed, extended, hdr_version; @@ -2122,7 +2123,11 @@ static int do_write_index(struct index_state *istate, int newfd, return -1; } - if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st)) + if (ce_flush(&c, newfd, istate->sha1)) + return -1; + if (close_tempfile(tempfile)) + return error(_("could not close '%s'"), tempfile->filename.buf); + if (stat(tempfile->filename.buf, &st)) return -1; istate->timestamp.sec = (unsigned int)st.st_mtime; istate->timestamp.nsec = ST_MTIME_NSEC(st); @@ -2145,7 +2150,7 @@ static int commit_locked_index(struct lock_file *lk) static int do_write_locked_index(struct index_state *istate, struct lock_file *lock, unsigned flags) { - int ret = do_write_index(istate, get_lock_file_fd(lock), 0); + int ret = do_write_index(istate, &lock->tempfile, 0); if (ret) return ret; assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) != @@ -2183,7 +2188,7 @@ static int write_shared_index(struct index_state *istate, return do_write_locked_index(istate, lock, flags); } move_cache_to_base_index(istate); - ret = do_write_index(si->base, fd, 1); + ret = do_write_index(si->base, &temporary_sharedindex, 1); if (ret) { delete_tempfile(&temporary_sharedindex); return ret; From 867e40ff3a731d9cf4ab1900a83ca21a9c2c8fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 30 Apr 2017 09:53:52 +0200 Subject: [PATCH 057/566] t5004: require 64-bit support for big ZIP tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check if unzip supports the ZIP64 format and skip the tests that create big archives otherwise. Also skip the test that archives a big file on 32-bit platforms because the git object systems can't unpack files bigger than 4GB there. Reported-by: Torsten Bögershausen Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- t/t5004-archive-corner-cases.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index 9106c53c4c..f6207f42b5 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh @@ -27,6 +27,9 @@ check_dir() { test_cmp expect actual } +test_lazy_prereq UNZIP_ZIP64_SUPPORT ' + "$GIT_UNZIP" -v | grep ZIP64_SUPPORT +' # bsdtar/libarchive versions before 3.1.3 consider a tar file with a # global pax header that is not followed by a file record as corrupt. @@ -155,7 +158,8 @@ test_expect_success ZIPINFO 'zip archive with many entries' ' test_cmp expect actual ' -test_expect_success EXPENSIVE,UNZIP 'zip archive bigger than 4GB' ' +test_expect_success EXPENSIVE,UNZIP,UNZIP_ZIP64_SUPPORT \ + 'zip archive bigger than 4GB' ' # build string containing 65536 characters s=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef && s=$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s && @@ -178,7 +182,8 @@ test_expect_success EXPENSIVE,UNZIP 'zip archive bigger than 4GB' ' "$GIT_UNZIP" -t many-big.zip ' -test_expect_success EXPENSIVE,UNZIP,ZIPINFO 'zip archive with files bigger than 4GB' ' +test_expect_success EXPENSIVE,LONG_IS_64BIT,UNZIP,UNZIP_ZIP64_SUPPORT,ZIPINFO \ + 'zip archive with files bigger than 4GB' ' # Pack created with: # dd if=/dev/zero of=file bs=1M count=4100 && git hash-object -w file mkdir -p .git/objects/pack && From 7f1b225153c9e75b805a262feb6796e3bf23dc3d Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Sun, 30 Apr 2017 18:29:30 +0100 Subject: [PATCH 058/566] t7400: add !CYGWIN prerequisite to 'add with \\ in path' Commit cf9e55f494 ("submodule: prevent backslash expantion in submodule names", 07-04-2017) added a test which creates a git repository with some backslash characters in the name. On windows, where the backslash character is a directory separator, it is not possible to create a repository with the name 'sub\with\backslash'. (The NTFS filesystem would probably allow it, but the win32 api does not). The MinGW and Git for Windows versions of git actually create a repository called 'backslash' in the sub-directory 'sub/with'. On cygwin, however, due to the slightly schizophrenic treatment of the backslash character by cygwin-git, this test fails at the 'git init' stage. The git-init command does not recognise the directory separators in the input path (eg. is_dir_sep('\\') is false), so it does not attempt to create the leading directories 'sub/with'. (The call to mkdir('sub\\with\\backslash') actually does recognise the directory separators, but fails because the 'sub/with' directory doesn't exist). In order to suppress the test failure (for now), add the !CYGWIN test prerequisite. Signed-off-by: Ramsay Jones Signed-off-by: Junio C Hamano --- t/t7400-submodule-basic.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index c2706fe472..d0357f1d8a 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -273,7 +273,7 @@ test_expect_success 'submodule add with ./, /.. and // in path' ' test_cmp empty untracked ' -test_expect_success 'submodule add with \\ in path' ' +test_expect_success !CYGWIN 'submodule add with \\ in path' ' test_when_finished "rm -rf parent sub\\with\\backslash" && # Initialize a repo with a backslash in its name From 28d67d9a262c7600f5c17bb8e2072fd224260ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Wed, 26 Apr 2017 23:12:32 +0000 Subject: [PATCH 059/566] tests: change "cd ... && git fetch" to "cd &&\n\tgit fetch" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change occurrences "cd" followed by "fetch" on a single line to be on two lines. This is purely a stylistic change pointed out in code review for an unrelated patch. Change the these tests use so new tests added later using the more common style don't look out of place. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5612-clone-refspec.sh | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh index 7ace2535c8..97c847fab6 100755 --- a/t/t5612-clone-refspec.sh +++ b/t/t5612-clone-refspec.sh @@ -59,7 +59,8 @@ test_expect_success 'setup' ' test_expect_success 'by default all branches will be kept updated' ' ( - cd dir_all && git fetch && + cd dir_all && + git fetch && git for-each-ref refs/remotes/origin | sed -e "/HEAD$/d" \ -e "s|/remotes/origin/|/heads/|" >../actual @@ -71,7 +72,8 @@ test_expect_success 'by default all branches will be kept updated' ' test_expect_success 'by default no tags will be kept updated' ' ( - cd dir_all && git fetch && + cd dir_all && + git fetch && git for-each-ref refs/tags >../actual ) && git for-each-ref refs/tags >expect && @@ -80,7 +82,8 @@ test_expect_success 'by default no tags will be kept updated' ' test_expect_success '--single-branch while HEAD pointing at master' ' ( - cd dir_master && git fetch && + cd dir_master && + git fetch && git for-each-ref refs/remotes/origin | sed -e "/HEAD$/d" \ -e "s|/remotes/origin/|/heads/|" >../actual @@ -92,7 +95,8 @@ test_expect_success '--single-branch while HEAD pointing at master' ' test_expect_success '--single-branch while HEAD pointing at side' ' ( - cd dir_side && git fetch && + cd dir_side && + git fetch && git for-each-ref refs/remotes/origin | sed -e "/HEAD$/d" \ -e "s|/remotes/origin/|/heads/|" >../actual @@ -104,7 +108,8 @@ test_expect_success '--single-branch while HEAD pointing at side' ' test_expect_success '--single-branch with explicit --branch side' ' ( - cd dir_side2 && git fetch && + cd dir_side2 && + git fetch && git for-each-ref refs/remotes/origin | sed -e "/HEAD$/d" \ -e "s|/remotes/origin/|/heads/|" >../actual @@ -116,7 +121,8 @@ test_expect_success '--single-branch with explicit --branch side' ' test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' ' ( - cd dir_tag && git fetch && + cd dir_tag && + git fetch && git for-each-ref refs/tags >../actual ) && git for-each-ref refs/tags >expect && @@ -125,7 +131,8 @@ test_expect_success '--single-branch with explicit --branch with tag fetches upd test_expect_success '--single-branch with --mirror' ' ( - cd dir_mirror && git fetch && + cd dir_mirror && + git fetch && git for-each-ref refs > ../actual ) && git for-each-ref refs >expect && @@ -134,7 +141,8 @@ test_expect_success '--single-branch with --mirror' ' test_expect_success '--single-branch with explicit --branch and --mirror' ' ( - cd dir_mirror_side && git fetch && + cd dir_mirror_side && + git fetch && git for-each-ref refs > ../actual ) && git for-each-ref refs >expect && @@ -143,7 +151,8 @@ test_expect_success '--single-branch with explicit --branch and --mirror' ' test_expect_success '--single-branch with detached' ' ( - cd dir_detached && git fetch && + cd dir_detached && + git fetch && git for-each-ref refs/remotes/origin | sed -e "/HEAD$/d" \ -e "s|/remotes/origin/|/heads/|" >../actual From 0dab2468ee5bbfaa854a22eb17c70647fc8b6b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Wed, 26 Apr 2017 23:12:33 +0000 Subject: [PATCH 060/566] clone: add a --no-tags option to clone without tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a --no-tags option to clone without fetching any tags. Without this change there's no easy way to clone a repository without also fetching its tags. When supplying --single-branch the primary remote branch will be cloned, but in addition tags will be followed & retrieved. Now --no-tags can be added --single-branch to clone a repository without tags, and which only tracks a single upstream branch. This option works without --single-branch as well, and will do a normal clone but not fetch any tags. Many git commands pay some fixed overhead as a function of the number of references. E.g. creating ~40k tags in linux.git will cause a command like `git log -1 >/dev/null` to run in over a second instead of in a matter of milliseconds, in addition numerous other things will slow down, e.g. "git log " with the bash completion will slowly show ~40k references instead of 1. The user might want to avoid all of that overhead to simply use a repository like that to browse the "master" branch, or something like a CI tool might want to keep that one branch up-to-date without caring about any other references. Without this change the only way of accomplishing this was either by manually tweaking the config in a fresh repository: git init git && cat >git/.git/config < Signed-off-by: Junio C Hamano --- Documentation/git-clone.txt | 14 ++++- builtin/clone.c | 13 ++++- contrib/completion/git-completion.bash | 1 + t/t5612-clone-refspec.sh | 76 +++++++++++++++++++++++++- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 30052cce49..83c8e9b394 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -13,7 +13,7 @@ SYNOPSIS [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] [-o ] [-b ] [-u ] [--reference ] [--dissociate] [--separate-git-dir ] - [--depth ] [--[no-]single-branch] + [--depth ] [--[no-]single-branch] [--no-tags] [--recurse-submodules] [--[no-]shallow-submodules] [--jobs ] [--] [] @@ -215,6 +215,18 @@ objects from the source repository into a pack in the cloned repository. branch when `--single-branch` clone was made, no remote-tracking branch is created. +--no-tags:: + Don't clone any tags, and set + `remote..tagOpt=--no-tags` in the config, ensuring + that future `git pull` and `git fetch` operations won't follow + any tags. Subsequent explicit tag fetches will still work, + (see linkgit:git-fetch[1]). ++ +Can be used in conjunction with `--single-branch` to clone and +maintain a branch with no references other than a single cloned +branch. This is useful e.g. to maintain minimal clones of the default +branch of some repository for search indexing. + --recurse-submodules[=file && git commit -a -m four && git checkout master && + git tag five && # default clone git clone . dir_all && + # default clone --no-tags + git clone --no-tags . dir_all_no_tags && + # default --single that follows HEAD=master git clone --single-branch . dir_master && + # default --single that follows HEAD=master with no tags + git clone --single-branch --no-tags . dir_master_no_tags && + # default --single that follows HEAD=side git checkout side && git clone --single-branch . dir_side && @@ -45,6 +52,9 @@ test_expect_success 'setup' ' # explicit --single with tag git clone --single-branch --branch two . dir_tag && + # explicit --single with tag and --no-tags + git clone --single-branch --no-tags --branch two . dir_tag_no_tags && + # advance both "master" and "side" branches git checkout side && echo five >file && @@ -77,7 +87,18 @@ test_expect_success 'by default no tags will be kept updated' ' git for-each-ref refs/tags >../actual ) && git for-each-ref refs/tags >expect && - test_must_fail test_cmp expect actual + test_must_fail test_cmp expect actual && + test_line_count = 2 actual +' + +test_expect_success 'clone with --no-tags' ' + ( + cd dir_all_no_tags && + git fetch && + git for-each-ref refs/tags >../actual + ) && + >expect && + test_cmp expect actual ' test_expect_success '--single-branch while HEAD pointing at master' ' @@ -90,7 +111,47 @@ test_expect_success '--single-branch while HEAD pointing at master' ' ) && # only follow master git for-each-ref refs/heads/master >expect && - test_cmp expect actual + # get & check latest tags + test_cmp expect actual && + ( + cd dir_master && + git fetch --tags && + git for-each-ref refs/tags >../actual + ) && + git for-each-ref refs/tags >expect && + test_cmp expect actual && + test_line_count = 2 actual +' + +test_expect_success '--single-branch while HEAD pointing at master and --no-tags' ' + ( + cd dir_master_no_tags && + git fetch && + git for-each-ref refs/remotes/origin | + sed -e "/HEAD$/d" \ + -e "s|/remotes/origin/|/heads/|" >../actual + ) && + # only follow master + git for-each-ref refs/heads/master >expect && + test_cmp expect actual && + # get tags (noop) + ( + cd dir_master_no_tags && + git fetch && + git for-each-ref refs/tags >../actual + ) && + >expect && + test_cmp expect actual && + test_line_count = 0 actual && + # get tags with --tags overrides tagOpt + ( + cd dir_master_no_tags && + git fetch --tags && + git for-each-ref refs/tags >../actual + ) && + git for-each-ref refs/tags >expect && + test_cmp expect actual && + test_line_count = 2 actual ' test_expect_success '--single-branch while HEAD pointing at side' ' @@ -129,6 +190,17 @@ test_expect_success '--single-branch with explicit --branch with tag fetches upd test_cmp expect actual ' +test_expect_success '--single-branch with explicit --branch with tag fetches updated tag despite --no-tags' ' + ( + cd dir_tag_no_tags && + git fetch && + git for-each-ref refs/tags >../actual + ) && + git for-each-ref refs/tags/two >expect && + test_cmp expect actual && + test_line_count = 1 actual +' + test_expect_success '--single-branch with --mirror' ' ( cd dir_mirror && From 1524ccdc1873029e4eafa5bffae2aa66d02369d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Wed, 26 Apr 2017 23:12:34 +0000 Subject: [PATCH 061/566] tests: rename a test having to do with shallow submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the t5614-clone-submodules.sh test to t5614-clone-submodules-shallow.sh. It's not a general test of submodules, but of shallow cloning in relation to submodules. Move it to create another similar t56*-clone-submodules-*.sh test. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- ...5614-clone-submodules.sh => t5614-clone-submodules-shallow.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename t/{t5614-clone-submodules.sh => t5614-clone-submodules-shallow.sh} (100%) diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules-shallow.sh similarity index 100% rename from t/t5614-clone-submodules.sh rename to t/t5614-clone-submodules-shallow.sh From 419fd7866c7636e4f2e3606888273b38d8cdacd4 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 28 Apr 2017 16:53:57 -0700 Subject: [PATCH 062/566] submodule: rename add_sha1_to_array() Rename 'add_sha1_to_array()' to 'append_oid_to_array()' to more accurately describe what the function does, since it handles 'struct object_id' and not sha1 character arrays. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- submodule.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/submodule.c b/submodule.c index d3299e29c0..be0f5d8478 100644 --- a/submodule.c +++ b/submodule.c @@ -951,17 +951,18 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q, } } -static int add_sha1_to_array(const char *ref, const struct object_id *oid, - int flags, void *data) +static int append_oid_to_array(const char *ref, const struct object_id *oid, + int flags, void *data) { - oid_array_append(data, oid); + struct oid_array *array = data; + oid_array_append(array, oid); return 0; } void check_for_new_submodule_commits(struct object_id *oid) { if (!initialized_fetch_ref_tips) { - for_each_ref(add_sha1_to_array, &ref_tips_before_fetch); + for_each_ref(append_oid_to_array, &ref_tips_before_fetch); initialized_fetch_ref_tips = 1; } From 610b233704638372834777ebf6ed3ab8829ec259 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 28 Apr 2017 16:53:58 -0700 Subject: [PATCH 063/566] submodule: rename free_submodules_sha1s() Rename 'free_submodules_sha1s()' to 'free_submodules_oids()' since the function frees a 'struct string_list' which has a 'struct oid_array' stored in the 'util' field. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- submodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/submodule.c b/submodule.c index be0f5d8478..46abd52b14 100644 --- a/submodule.c +++ b/submodule.c @@ -738,7 +738,7 @@ static void find_unpushed_submodule_commits(struct commit *commit, diff_tree_combined_merge(commit, 1, &rev); } -static void free_submodules_sha1s(struct string_list *submodules) +static void free_submodules_oids(struct string_list *submodules) { struct string_list_item *item; for_each_string_list_item(item, submodules) @@ -779,7 +779,8 @@ int find_unpushed_submodules(struct oid_array *commits, if (submodule_needs_pushing(submodule->string, commits)) string_list_insert(needs_pushing, submodule->string); } - free_submodules_sha1s(&submodules); + + free_submodules_oids(&submodules); return needs_pushing->nr; } From d1a8460caac1a19457e352a6b610b7bc89cd56d9 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 28 Apr 2017 16:53:59 -0700 Subject: [PATCH 064/566] submodule: remove add_oid_to_argv() The function 'add_oid_to_argv()' provides the same functionality as 'append_oid_to_argv()'. Remove this duplicate function and instead use 'append_oid_to_argv()' where 'add_oid_to_argv()' was previously used. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- submodule.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/submodule.c b/submodule.c index 46abd52b14..7baa28ae05 100644 --- a/submodule.c +++ b/submodule.c @@ -970,12 +970,6 @@ void check_for_new_submodule_commits(struct object_id *oid) oid_array_append(&ref_tips_after_fetch, oid); } -static int add_oid_to_argv(const struct object_id *oid, void *data) -{ - argv_array_push(data, oid_to_hex(oid)); - return 0; -} - static void calculate_changed_submodule_paths(void) { struct rev_info rev; @@ -989,10 +983,10 @@ static void calculate_changed_submodule_paths(void) init_revisions(&rev, NULL); argv_array_push(&argv, "--"); /* argv[0] program name */ oid_array_for_each_unique(&ref_tips_after_fetch, - add_oid_to_argv, &argv); + append_oid_to_argv, &argv); argv_array_push(&argv, "--not"); oid_array_for_each_unique(&ref_tips_before_fetch, - add_oid_to_argv, &argv); + append_oid_to_argv, &argv); setup_revisions(argv.argc, argv.argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); From a6bb78c3b19697133a01122c13993986b66ef28b Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 28 Apr 2017 16:54:00 -0700 Subject: [PATCH 065/566] submodule: change string_list changed_submodule_paths Eliminate a call to 'xstrdup()' by changing the string_list 'changed_submodule_paths' to duplicated strings added to it. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- submodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodule.c b/submodule.c index 7baa28ae05..3bcf44521b 100644 --- a/submodule.c +++ b/submodule.c @@ -20,7 +20,7 @@ static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int parallel_jobs = 1; -static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP; +static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP; static int initialized_fetch_ref_tips; static struct oid_array ref_tips_before_fetch; static struct oid_array ref_tips_after_fetch; @@ -939,7 +939,7 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q, struct string_list_item *path; path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path); if (!path && !is_submodule_commit_present(p->two->path, p->two->oid.hash)) - string_list_append(&changed_submodule_paths, xstrdup(p->two->path)); + string_list_append(&changed_submodule_paths, p->two->path); } else { /* Submodule is new or was moved here */ /* NEEDSWORK: When the .git directories of submodules From 7c8d2b00f2516a0b30aaa3f59dceaf4fe83f8729 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 1 May 2017 18:02:38 -0700 Subject: [PATCH 066/566] submodule: improve submodule_has_commits() Teach 'submodule_has_commits()' to ensure that if a commit exists in a submodule, that it is also reachable from a ref. This is a preparatory step prior to merging the logic which checks for changed submodules when fetching or pushing. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- submodule.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/submodule.c b/submodule.c index 3bcf44521b..057695e645 100644 --- a/submodule.c +++ b/submodule.c @@ -644,10 +644,44 @@ static int submodule_has_commits(const char *path, struct oid_array *commits) { int has_commit = 1; + /* + * Perform a cheap, but incorrect check for the existance of 'commits'. + * This is done by adding the submodule's object store to the in-core + * object store, and then querying for each commit's existance. If we + * do not have the commit object anywhere, there is no chance we have + * it in the object store of the correct submodule and have it + * reachable from a ref, so we can fail early without spawning rev-list + * which is expensive. + */ if (add_submodule_odb(path)) return 0; oid_array_for_each_unique(commits, check_has_commit, &has_commit); + + if (has_commit) { + /* + * Even if the submodule is checked out and the commit is + * present, make sure it exists in the submodule's object store + * and that it is reachable from a ref. + */ + struct child_process cp = CHILD_PROCESS_INIT; + struct strbuf out = STRBUF_INIT; + + argv_array_pushl(&cp.args, "rev-list", "-n", "1", NULL); + oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args); + argv_array_pushl(&cp.args, "--not", "--all", NULL); + + prepare_submodule_repo_env(&cp.env_array); + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; + + if (capture_command(&cp, &out, GIT_MAX_HEXSZ + 1) || out.len) + has_commit = 0; + + strbuf_release(&out); + } + return has_commit; } From aacc5c1a81c004efccff8075edb78acdf5f15264 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 1 May 2017 18:02:39 -0700 Subject: [PATCH 067/566] submodule: refactor logic to determine changed submodules There are currently two instances (fetch and push) where we want to determine if submodules have changed given some revision specification. These two instances don't use the same logic to generate a list of changed submodules and as a result there is a fair amount of code duplication. This patch refactors these two code paths such that they both use the same logic to generate a list of changed submodules. This also makes it easier for future callers to be able to reuse this logic as they only need to create an argv_array with the revision specification to be using during the revision walk. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- submodule.c | 247 ++++++++++++++++++++++------------------------------ 1 file changed, 105 insertions(+), 142 deletions(-) diff --git a/submodule.c b/submodule.c index 057695e645..7eaa3d384e 100644 --- a/submodule.c +++ b/submodule.c @@ -617,6 +617,94 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce) return submodule_from_path(null_sha1, ce->name); } +static struct oid_array *submodule_commits(struct string_list *submodules, + const char *path) +{ + struct string_list_item *item; + + item = string_list_insert(submodules, path); + if (item->util) + return (struct oid_array *) item->util; + + /* NEEDSWORK: should we have oid_array_init()? */ + item->util = xcalloc(1, sizeof(struct oid_array)); + return (struct oid_array *) item->util; +} + +static void collect_changed_submodules_cb(struct diff_queue_struct *q, + struct diff_options *options, + void *data) +{ + int i; + struct string_list *changed = data; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + struct oid_array *commits; + if (!S_ISGITLINK(p->two->mode)) + continue; + + if (S_ISGITLINK(p->one->mode)) { + /* + * NEEDSWORK: We should honor the name configured in + * the .gitmodules file of the commit we are examining + * here to be able to correctly follow submodules + * being moved around. + */ + commits = submodule_commits(changed, p->two->path); + oid_array_append(commits, &p->two->oid); + } else { + /* Submodule is new or was moved here */ + /* + * NEEDSWORK: When the .git directories of submodules + * live inside the superprojects .git directory some + * day we should fetch new submodules directly into + * that location too when config or options request + * that so they can be checked out from there. + */ + continue; + } + } +} + +/* + * Collect the paths of submodules in 'changed' which have changed based on + * the revisions as specified in 'argv'. Each entry in 'changed' will also + * have a corresponding 'struct oid_array' (in the 'util' field) which lists + * what the submodule pointers were updated to during the change. + */ +static void collect_changed_submodules(struct string_list *changed, + struct argv_array *argv) +{ + struct rev_info rev; + const struct commit *commit; + + init_revisions(&rev, NULL); + setup_revisions(argv->argc, argv->argv, &rev, NULL); + if (prepare_revision_walk(&rev)) + die("revision walk setup failed"); + + while ((commit = get_revision(&rev))) { + struct rev_info diff_rev; + + init_revisions(&diff_rev, NULL); + diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; + diff_rev.diffopt.format_callback = collect_changed_submodules_cb; + diff_rev.diffopt.format_callback_data = changed; + diff_tree_combined_merge(commit, 1, &diff_rev); + } + + reset_revision_walk(); +} + +static void free_submodules_oids(struct string_list *submodules) +{ + struct string_list_item *item; + for_each_string_list_item(item, submodules) + oid_array_clear((struct oid_array *) item->util); + string_list_clear(submodules, 1); +} + static int has_remote(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -729,92 +817,31 @@ static int submodule_needs_pushing(const char *path, struct oid_array *commits) return 0; } -static struct oid_array *submodule_commits(struct string_list *submodules, - const char *path) -{ - struct string_list_item *item; - - item = string_list_insert(submodules, path); - if (item->util) - return (struct oid_array *) item->util; - - /* NEEDSWORK: should we have oid_array_init()? */ - item->util = xcalloc(1, sizeof(struct oid_array)); - return (struct oid_array *) item->util; -} - -static void collect_submodules_from_diff(struct diff_queue_struct *q, - struct diff_options *options, - void *data) -{ - int i; - struct string_list *submodules = data; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - struct oid_array *commits; - if (!S_ISGITLINK(p->two->mode)) - continue; - commits = submodule_commits(submodules, p->two->path); - oid_array_append(commits, &p->two->oid); - } -} - -static void find_unpushed_submodule_commits(struct commit *commit, - struct string_list *needs_pushing) -{ - struct rev_info rev; - - init_revisions(&rev, NULL); - rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = collect_submodules_from_diff; - rev.diffopt.format_callback_data = needs_pushing; - diff_tree_combined_merge(commit, 1, &rev); -} - -static void free_submodules_oids(struct string_list *submodules) -{ - struct string_list_item *item; - for_each_string_list_item(item, submodules) - oid_array_clear((struct oid_array *) item->util); - string_list_clear(submodules, 1); -} - int find_unpushed_submodules(struct oid_array *commits, const char *remotes_name, struct string_list *needs_pushing) { - struct rev_info rev; - struct commit *commit; struct string_list submodules = STRING_LIST_INIT_DUP; struct string_list_item *submodule; struct argv_array argv = ARGV_ARRAY_INIT; - init_revisions(&rev, NULL); - /* argv.argv[0] will be ignored by setup_revisions */ argv_array_push(&argv, "find_unpushed_submodules"); oid_array_for_each_unique(commits, append_oid_to_argv, &argv); argv_array_push(&argv, "--not"); argv_array_pushf(&argv, "--remotes=%s", remotes_name); - setup_revisions(argv.argc, argv.argv, &rev, NULL); - if (prepare_revision_walk(&rev)) - die("revision walk setup failed"); - - while ((commit = get_revision(&rev)) != NULL) - find_unpushed_submodule_commits(commit, &submodules); - - reset_revision_walk(); - argv_array_clear(&argv); + collect_changed_submodules(&submodules, &argv); for_each_string_list_item(submodule, &submodules) { - struct oid_array *commits = (struct oid_array *) submodule->util; + struct oid_array *commits = submodule->util; + const char *path = submodule->string; - if (submodule_needs_pushing(submodule->string, commits)) - string_list_insert(needs_pushing, submodule->string); + if (submodule_needs_pushing(path, commits)) + string_list_insert(needs_pushing, path); } free_submodules_oids(&submodules); + argv_array_clear(&argv); return needs_pushing->nr; } @@ -931,61 +958,6 @@ int push_unpushed_submodules(struct oid_array *commits, return ret; } -static int is_submodule_commit_present(const char *path, unsigned char sha1[20]) -{ - int is_present = 0; - if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) { - /* Even if the submodule is checked out and the commit is - * present, make sure it is reachable from a ref. */ - struct child_process cp = CHILD_PROCESS_INIT; - const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL}; - struct strbuf buf = STRBUF_INIT; - - argv[3] = sha1_to_hex(sha1); - cp.argv = argv; - prepare_submodule_repo_env(&cp.env_array); - cp.git_cmd = 1; - cp.no_stdin = 1; - cp.dir = path; - if (!capture_command(&cp, &buf, 1024) && !buf.len) - is_present = 1; - - strbuf_release(&buf); - } - return is_present; -} - -static void submodule_collect_changed_cb(struct diff_queue_struct *q, - struct diff_options *options, - void *data) -{ - int i; - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (!S_ISGITLINK(p->two->mode)) - continue; - - if (S_ISGITLINK(p->one->mode)) { - /* NEEDSWORK: We should honor the name configured in - * the .gitmodules file of the commit we are examining - * here to be able to correctly follow submodules - * being moved around. */ - struct string_list_item *path; - path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path); - if (!path && !is_submodule_commit_present(p->two->path, p->two->oid.hash)) - string_list_append(&changed_submodule_paths, p->two->path); - } else { - /* Submodule is new or was moved here */ - /* NEEDSWORK: When the .git directories of submodules - * live inside the superprojects .git directory some - * day we should fetch new submodules directly into - * that location too when config or options request - * that so they can be checked out from there. */ - continue; - } - } -} - static int append_oid_to_array(const char *ref, const struct object_id *oid, int flags, void *data) { @@ -1006,45 +978,36 @@ void check_for_new_submodule_commits(struct object_id *oid) static void calculate_changed_submodule_paths(void) { - struct rev_info rev; - struct commit *commit; struct argv_array argv = ARGV_ARRAY_INIT; + struct string_list changed_submodules = STRING_LIST_INIT_DUP; + const struct string_list_item *item; /* No need to check if there are no submodules configured */ if (!submodule_from_path(NULL, NULL)) return; - init_revisions(&rev, NULL); argv_array_push(&argv, "--"); /* argv[0] program name */ oid_array_for_each_unique(&ref_tips_after_fetch, append_oid_to_argv, &argv); argv_array_push(&argv, "--not"); oid_array_for_each_unique(&ref_tips_before_fetch, append_oid_to_argv, &argv); - setup_revisions(argv.argc, argv.argv, &rev, NULL); - if (prepare_revision_walk(&rev)) - die("revision walk setup failed"); /* * Collect all submodules (whether checked out or not) for which new * commits have been recorded upstream in "changed_submodule_paths". */ - while ((commit = get_revision(&rev))) { - struct commit_list *parent = commit->parents; - while (parent) { - struct diff_options diff_opts; - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.output_format |= DIFF_FORMAT_CALLBACK; - diff_opts.format_callback = submodule_collect_changed_cb; - diff_setup_done(&diff_opts); - diff_tree_sha1(parent->item->object.oid.hash, commit->object.oid.hash, "", &diff_opts); - diffcore_std(&diff_opts); - diff_flush(&diff_opts); - parent = parent->next; - } + collect_changed_submodules(&changed_submodules, &argv); + + for_each_string_list_item(item, &changed_submodules) { + struct oid_array *commits = item->util; + const char *path = item->string; + + if (!submodule_has_commits(path, commits)) + string_list_append(&changed_submodule_paths, path); } + free_submodules_oids(&changed_submodules); argv_array_clear(&argv); oid_array_clear(&ref_tips_before_fetch); oid_array_clear(&ref_tips_after_fetch); From 1b283377b18bc1e363005729b025a88b36084c4e Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:28:54 +0000 Subject: [PATCH 068/566] fetch-pack: convert to struct object_id Convert all uses of unsigned char [20] to struct object_id. Switch one use of get_sha1_hex to parse_oid_hex to avoid the need for a constant. This change is necessary in order to convert parse_object. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- fetch-pack.c | 89 ++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index afb8b05024..b42d01f422 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -118,9 +118,9 @@ static void rev_list_push(struct commit *commit, int mark) } } -static int rev_list_insert_ref(const char *refname, const unsigned char *sha1) +static int rev_list_insert_ref(const char *refname, const struct object_id *oid) { - struct object *o = deref_tag(parse_object(sha1), refname, 0); + struct object *o = deref_tag(parse_object(oid->hash), refname, 0); if (o && o->type == OBJ_COMMIT) rev_list_push((struct commit *)o, SEEN); @@ -131,7 +131,7 @@ static int rev_list_insert_ref(const char *refname, const unsigned char *sha1) static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid, int flag, void *cb_data) { - return rev_list_insert_ref(refname, oid->hash); + return rev_list_insert_ref(refname, oid); } static int clear_marks(const char *refname, const struct object_id *oid, @@ -183,7 +183,7 @@ static void mark_common(struct commit *commit, Get the next rev to send, ignoring the common. */ -static const unsigned char *get_rev(void) +static const struct object_id *get_rev(void) { struct commit *commit = NULL; @@ -222,7 +222,7 @@ static const unsigned char *get_rev(void) } } - return commit->object.oid.hash; + return &commit->object.oid; } enum ack_type { @@ -251,7 +251,7 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd) } } -static enum ack_type get_ack(int fd, unsigned char *result_sha1) +static enum ack_type get_ack(int fd, struct object_id *result_oid) { int len; char *line = packet_read_line(fd, &len); @@ -262,7 +262,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1) if (!strcmp(line, "NAK")) return NAK; if (skip_prefix(line, "ACK ", &arg)) { - if (!get_sha1_hex(arg, result_sha1)) { + if (!get_oid_hex(arg, result_oid)) { arg += 40; len -= arg - line; if (len < 1) @@ -293,7 +293,7 @@ static void send_request(struct fetch_pack_args *args, static void insert_one_alternate_object(struct object *obj) { - rev_list_insert_ref(NULL, obj->oid.hash); + rev_list_insert_ref(NULL, &obj->oid); } #define INITIAL_FLUSH 16 @@ -317,12 +317,12 @@ static int next_flush(struct fetch_pack_args *args, int count) } static int find_common(struct fetch_pack_args *args, - int fd[2], unsigned char *result_sha1, + int fd[2], struct object_id *result_oid, struct ref *refs) { int fetching; int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval; - const unsigned char *sha1; + const struct object_id *oid; unsigned in_vain = 0; int got_continue = 0; int got_ready = 0; @@ -340,7 +340,7 @@ static int find_common(struct fetch_pack_args *args, fetching = 0; for ( ; refs ; refs = refs->next) { - unsigned char *remote = refs->old_oid.hash; + struct object_id *remote = &refs->old_oid; const char *remote_hex; struct object *o; @@ -354,12 +354,12 @@ static int find_common(struct fetch_pack_args *args, * interested in the case we *know* the object is * reachable and we have already scanned it. */ - if (((o = lookup_object(remote)) != NULL) && + if (((o = lookup_object(remote->hash)) != NULL) && (o->flags & COMPLETE)) { continue; } - remote_hex = sha1_to_hex(remote); + remote_hex = oid_to_hex(remote); if (!fetching) { struct strbuf c = STRBUF_INIT; if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed"); @@ -410,25 +410,25 @@ static int find_common(struct fetch_pack_args *args, if (args->deepen) { char *line; const char *arg; - unsigned char sha1[20]; + struct object_id oid; send_request(args, fd[1], &req_buf); while ((line = packet_read_line(fd[0], NULL))) { if (skip_prefix(line, "shallow ", &arg)) { - if (get_sha1_hex(arg, sha1)) + if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), line); - register_shallow(sha1); + register_shallow(oid.hash); continue; } if (skip_prefix(line, "unshallow ", &arg)) { - if (get_sha1_hex(arg, sha1)) + if (get_oid_hex(arg, &oid)) die(_("invalid unshallow line: %s"), line); - if (!lookup_object(sha1)) + if (!lookup_object(oid.hash)) die(_("object not found: %s"), line); /* make sure that it is parsed as shallow */ - if (!parse_object(sha1)) + if (!parse_object(oid.hash)) die(_("error in object: %s"), line); - if (unregister_shallow(sha1)) + if (unregister_shallow(oid.hash)) die(_("no shallow found: %s"), line); continue; } @@ -447,9 +447,9 @@ static int find_common(struct fetch_pack_args *args, flushes = 0; retval = -1; - while ((sha1 = get_rev())) { - packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1)); - print_verbose(args, "have %s", sha1_to_hex(sha1)); + while ((oid = get_rev())) { + packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid)); + print_verbose(args, "have %s", oid_to_hex(oid)); in_vain++; if (flush_at <= ++count) { int ack; @@ -469,10 +469,10 @@ static int find_common(struct fetch_pack_args *args, consume_shallow_list(args, fd[0]); do { - ack = get_ack(fd[0], result_sha1); + ack = get_ack(fd[0], result_oid); if (ack) print_verbose(args, _("got %s %d %s"), "ack", - ack, sha1_to_hex(result_sha1)); + ack, oid_to_hex(result_oid)); switch (ack) { case ACK: flushes = 0; @@ -483,9 +483,9 @@ static int find_common(struct fetch_pack_args *args, case ACK_ready: case ACK_continue: { struct commit *commit = - lookup_commit(result_sha1); + lookup_commit(result_oid->hash); if (!commit) - die(_("invalid commit %s"), sha1_to_hex(result_sha1)); + die(_("invalid commit %s"), oid_to_hex(result_oid)); if (args->stateless_rpc && ack == ACK_common && !(commit->object.flags & COMMON)) { @@ -493,7 +493,7 @@ static int find_common(struct fetch_pack_args *args, * on the next RPC request so the peer knows * it is in common with us. */ - const char *hex = sha1_to_hex(result_sha1); + const char *hex = oid_to_hex(result_oid); packet_buf_write(&req_buf, "have %s\n", hex); state_len = req_buf.len; /* @@ -538,10 +538,10 @@ done: if (!got_ready || !no_done) consume_shallow_list(args, fd[0]); while (flushes || multi_ack) { - int ack = get_ack(fd[0], result_sha1); + int ack = get_ack(fd[0], result_oid); if (ack) { print_verbose(args, _("got %s (%d) %s"), "ack", - ack, sha1_to_hex(result_sha1)); + ack, oid_to_hex(result_oid)); if (ack == ACK) return 0; multi_ack = 1; @@ -555,9 +555,9 @@ done: static struct commit_list *complete; -static int mark_complete(const unsigned char *sha1) +static int mark_complete(const struct object_id *oid) { - struct object *o = parse_object(sha1); + struct object *o = parse_object(oid->hash); while (o && o->type == OBJ_TAG) { struct tag *t = (struct tag *) o; @@ -579,7 +579,7 @@ static int mark_complete(const unsigned char *sha1) static int mark_complete_oid(const char *refname, const struct object_id *oid, int flag, void *cb_data) { - return mark_complete(oid->hash); + return mark_complete(oid); } static void mark_recent_complete_commits(struct fetch_pack_args *args, @@ -637,14 +637,15 @@ static void filter_refs(struct fetch_pack_args *args, /* Append unmatched requests to the list */ for (i = 0; i < nr_sought; i++) { - unsigned char sha1[20]; + struct object_id oid; + const char *p; ref = sought[i]; if (ref->match_status != REF_NOT_MATCHED) continue; - if (get_sha1_hex(ref->name, sha1) || - ref->name[40] != '\0' || - hashcmp(sha1, ref->old_oid.hash)) + if (parse_oid_hex(ref->name, &oid, &p) || + *p != '\0' || + oidcmp(&oid, &ref->old_oid)) continue; if ((allow_unadvertised_object_request & @@ -661,7 +662,7 @@ static void filter_refs(struct fetch_pack_args *args, static void mark_alternate_complete(struct object *obj) { - mark_complete(obj->oid.hash); + mark_complete(&obj->oid); } static int everything_local(struct fetch_pack_args *args, @@ -724,17 +725,17 @@ static int everything_local(struct fetch_pack_args *args, filter_refs(args, refs, sought, nr_sought); for (retval = 1, ref = *refs; ref ; ref = ref->next) { - const unsigned char *remote = ref->old_oid.hash; + const struct object_id *remote = &ref->old_oid; struct object *o; - o = lookup_object(remote); + o = lookup_object(remote->hash); if (!o || !(o->flags & COMPLETE)) { retval = 0; - print_verbose(args, "want %s (%s)", sha1_to_hex(remote), + print_verbose(args, "want %s (%s)", oid_to_hex(remote), ref->name); continue; } - print_verbose(args, _("already have %s (%s)"), sha1_to_hex(remote), + print_verbose(args, _("already have %s (%s)"), oid_to_hex(remote), ref->name); } return retval; @@ -873,7 +874,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, char **pack_lockfile) { struct ref *ref = copy_ref_list(orig_ref); - unsigned char sha1[20]; + struct object_id oid; const char *agent_feature; int agent_len; @@ -945,7 +946,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, packet_flush(fd[1]); goto all_done; } - if (find_common(args, fd, sha1, ref) < 0) + if (find_common(args, fd, &oid, ref) < 0) if (!args->keep_pack) /* When cloning, it is not unusual to have * no common commit. From fb4e352b4082a447c089151b1f2662844bda0354 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:28:55 +0000 Subject: [PATCH 069/566] Clean up outstanding object_id transforms. The semantic patch for standard object_id transforms found two outstanding places where we could make a transformation automatically. Apply these changes. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/diff.c | 2 +- reflog-walk.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/diff.c b/builtin/diff.c index d184aafab9..a25b4e4ae6 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -408,7 +408,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); - hashcpy(blob[blobs].oid.hash, obj->oid.hash); + oidcpy(&blob[blobs].oid, &obj->oid); blob[blobs].name = name; blob[blobs].mode = entry->mode; blobs++; diff --git a/reflog-walk.c b/reflog-walk.c index 99679f5825..c8fdf051d2 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -241,7 +241,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) logobj = parse_object(reflog->ooid.hash); } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT)); - if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->ooid.hash)) { + if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) { /* a root commit, but there are still more entries to show */ reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; logobj = parse_object(reflog->noid.hash); From e0a92804044a4025fc8abbfa1d92fd16f6f2e1f4 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:28:56 +0000 Subject: [PATCH 070/566] Convert struct cache_tree to use struct object_id Convert the sha1 member of struct cache_tree to struct object_id by changing the definition and applying the following semantic patch, plus the standard object_id transforms: @@ struct cache_tree E1; @@ - E1.sha1 + E1.oid.hash @@ struct cache_tree *E1; @@ - E1->sha1 + E1->oid.hash Fix up one reference to active_cache_tree which was not automatically caught by Coccinelle. These changes are prerequisites for converting parse_object. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/commit.c | 2 +- builtin/fsck.c | 4 ++-- cache-tree.c | 31 ++++++++++++++++--------------- cache-tree.h | 3 ++- merge-recursive.c | 2 +- revision.c | 2 +- sequencer.c | 3 ++- t/helper/test-dump-cache-tree.c | 4 ++-- 8 files changed, 27 insertions(+), 24 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 1d805f5da8..8685c888f0 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1758,7 +1758,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) append_merge_tag_headers(parents, &tail); } - if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1, + if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->oid.hash, parents, oid.hash, author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); diff --git a/builtin/fsck.c b/builtin/fsck.c index b5e13a4556..c40e14de65 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -599,10 +599,10 @@ static int fsck_cache_tree(struct cache_tree *it) fprintf(stderr, "Checking cache tree\n"); if (0 <= it->entry_count) { - struct object *obj = parse_object(it->sha1); + struct object *obj = parse_object(it->oid.hash); if (!obj) { error("%s: invalid sha1 pointer in cache-tree", - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); errors_found |= ERROR_REFS; return 1; } diff --git a/cache-tree.c b/cache-tree.c index 345ea35963..35d507ed79 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -225,7 +225,7 @@ int cache_tree_fully_valid(struct cache_tree *it) int i; if (!it) return 0; - if (it->entry_count < 0 || !has_sha1_file(it->sha1)) + if (it->entry_count < 0 || !has_sha1_file(it->oid.hash)) return 0; for (i = 0; i < it->subtree_nr; i++) { if (!cache_tree_fully_valid(it->down[i]->cache_tree)) @@ -253,7 +253,7 @@ static int update_one(struct cache_tree *it, *skip_count = 0; - if (0 <= it->entry_count && has_sha1_file(it->sha1)) + if (0 <= it->entry_count && has_sha1_file(it->oid.hash)) return it->entry_count; /* @@ -340,7 +340,7 @@ static int update_one(struct cache_tree *it, die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); i += sub->count; - sha1 = sub->cache_tree->sha1; + sha1 = sub->cache_tree->oid.hash; mode = S_IFDIR; contains_ita = sub->cache_tree->entry_count < 0; if (contains_ita) { @@ -402,12 +402,13 @@ static int update_one(struct cache_tree *it, unsigned char sha1[20]; hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1); if (has_sha1_file(sha1)) - hashcpy(it->sha1, sha1); + hashcpy(it->oid.hash, sha1); else to_invalidate = 1; } else if (dryrun) - hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); - else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) { + hash_sha1_file(buffer.buf, buffer.len, tree_type, + it->oid.hash); + else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->oid.hash)) { strbuf_release(&buffer); return -1; } @@ -417,7 +418,7 @@ static int update_one(struct cache_tree *it, #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); #endif return i; } @@ -457,14 +458,14 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it, if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n", pathlen, path, it->entry_count, it->subtree_nr, - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); else fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n", pathlen, path, it->subtree_nr); #endif if (0 <= it->entry_count) { - strbuf_add(buffer, it->sha1, 20); + strbuf_add(buffer, it->oid.hash, 20); } for (i = 0; i < it->subtree_nr; i++) { struct cache_tree_sub *down = it->down[i]; @@ -521,7 +522,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) if (0 <= it->entry_count) { if (size < 20) goto free_return; - hashcpy(it->sha1, (const unsigned char*)buf); + hashcpy(it->oid.hash, (const unsigned char*)buf); buf += 20; size -= 20; } @@ -530,7 +531,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n", *buffer, it->entry_count, subtree_nr, - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); else fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n", *buffer, subtree_nr); @@ -641,10 +642,10 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co subtree = cache_tree_find(index_state->cache_tree, prefix); if (!subtree) return WRITE_TREE_PREFIX_ERROR; - hashcpy(sha1, subtree->sha1); + hashcpy(sha1, subtree->oid.hash); } else - hashcpy(sha1, index_state->cache_tree->sha1); + hashcpy(sha1, index_state->cache_tree->oid.hash); if (0 <= newfd) rollback_lock_file(lock_file); @@ -663,7 +664,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) struct name_entry entry; int cnt; - hashcpy(it->sha1, tree->object.oid.hash); + oidcpy(&it->oid, &tree->object.oid); init_tree_desc(&desc, tree->buffer, tree->size); cnt = 0; while (tree_entry(&desc, &entry)) { @@ -718,7 +719,7 @@ int cache_tree_matches_traversal(struct cache_tree *root, it = find_cache_tree_from_traversal(root, info); it = cache_tree_find(it, ent->path); - if (it && it->entry_count > 0 && !hashcmp(ent->oid->hash, it->sha1)) + if (it && it->entry_count > 0 && !oidcmp(ent->oid, &it->oid)) return it->entry_count; return 0; } diff --git a/cache-tree.h b/cache-tree.h index 41c574663a..f7b9cab7ee 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -1,6 +1,7 @@ #ifndef CACHE_TREE_H #define CACHE_TREE_H +#include "cache.h" #include "tree.h" #include "tree-walk.h" @@ -15,7 +16,7 @@ struct cache_tree_sub { struct cache_tree { int entry_count; /* negative means "invalid" */ - unsigned char sha1[20]; + struct object_id oid; int subtree_nr; int subtree_alloc; struct cache_tree_sub **down; diff --git a/merge-recursive.c b/merge-recursive.c index 62decd51cc..9d6fd577e2 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -304,7 +304,7 @@ struct tree *write_tree_from_memory(struct merge_options *o) return NULL; } - result = lookup_tree(active_cache_tree->sha1); + result = lookup_tree(active_cache_tree->oid.hash); return result; } diff --git a/revision.c b/revision.c index 7ff61ff5f7..2b56c3baf0 100644 --- a/revision.c +++ b/revision.c @@ -1249,7 +1249,7 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs, int i; if (it->entry_count >= 0) { - struct tree *tree = lookup_tree(it->sha1); + struct tree *tree = lookup_tree(it->oid.hash); add_pending_object_with_path(revs, &tree->object, "", 040000, path->buf); } diff --git a/sequencer.c b/sequencer.c index 10c3b4ff81..b0f862b7bb 100644 --- a/sequencer.c +++ b/sequencer.c @@ -508,7 +508,8 @@ static int is_index_unchanged(void) if (cache_tree_update(&the_index, 0)) return error(_("unable to update cache tree\n")); - return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash); + return !oidcmp(&active_cache_tree->oid, + &head_commit->tree->object.oid); } static int write_author_script(const char *message) diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c index 7af116d49e..ebf3aab22d 100644 --- a/t/helper/test-dump-cache-tree.c +++ b/t/helper/test-dump-cache-tree.c @@ -10,7 +10,7 @@ static void dump_one(struct cache_tree *it, const char *pfx, const char *x) "invalid", x, pfx, it->subtree_nr); else printf("%s %s%s (%d entries, %d subtrees)\n", - sha1_to_hex(it->sha1), x, pfx, + oid_to_hex(&it->oid), x, pfx, it->entry_count, it->subtree_nr); } @@ -32,7 +32,7 @@ static int dump_cache_tree(struct cache_tree *it, } else { dump_one(it, pfx, ""); - if (hashcmp(it->sha1, ref->sha1) || + if (oidcmp(&it->oid, &ref->oid) || ref->entry_count != it->entry_count || ref->subtree_nr != it->subtree_nr) { /* claims to be valid but is lying */ From 511dca80cce868237b2b64f909ed83863fcf5455 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:28:57 +0000 Subject: [PATCH 071/566] builtin/name-rev: convert to struct object_id Convert all the uses of unsigned char [20] to struct object_id. Also, convert some hard-coded integers into constants. name_rev_line accepts a wide variety of free-form input and only interprets 40-character hex values, passing through everything else. Consequently, it is not a good candidate for parse_oid_hex, which is much stricter. This change is a prerequisite for converting parse_object. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/name-rev.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 92a5d8a5d2..00760ecc65 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -114,7 +114,7 @@ struct name_ref_data { static struct tip_table { struct tip_table_entry { - unsigned char sha1[20]; + struct object_id oid; const char *refname; } *table; int nr; @@ -122,13 +122,13 @@ static struct tip_table { int sorted; } tip_table; -static void add_to_tip_table(const unsigned char *sha1, const char *refname, +static void add_to_tip_table(const struct object_id *oid, const char *refname, int shorten_unambiguous) { refname = name_ref_abbrev(refname, shorten_unambiguous); ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc); - hashcpy(tip_table.table[tip_table.nr].sha1, sha1); + oidcpy(&tip_table.table[tip_table.nr].oid, oid); tip_table.table[tip_table.nr].refname = xstrdup(refname); tip_table.nr++; tip_table.sorted = 0; @@ -137,7 +137,7 @@ static void add_to_tip_table(const unsigned char *sha1, const char *refname, static int tipcmp(const void *a_, const void *b_) { const struct tip_table_entry *a = a_, *b = b_; - return hashcmp(a->sha1, b->sha1); + return oidcmp(&a->oid, &b->oid); } static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data) @@ -194,7 +194,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo return 0; } - add_to_tip_table(oid->hash, path, can_abbreviate_output); + add_to_tip_table(oid, path, can_abbreviate_output); while (o && o->type == OBJ_TAG) { struct tag *t = (struct tag *) o; @@ -216,7 +216,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo static const unsigned char *nth_tip_table_ent(size_t ix, void *table_) { struct tip_table_entry *table = table_; - return table[ix].sha1; + return table[ix].oid.hash; } static const char *get_exact_ref_match(const struct object *o) @@ -301,9 +301,9 @@ static void name_rev_line(char *p, struct name_ref_data *data) #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f')) if (!ishex(*p)) forty = 0; - else if (++forty == 40 && + else if (++forty == GIT_SHA1_HEXSZ && !ishex(*(p+1))) { - unsigned char sha1[40]; + struct object_id oid; const char *name = NULL; char c = *(p+1); int p_len = p - p_start + 1; @@ -311,9 +311,9 @@ static void name_rev_line(char *p, struct name_ref_data *data) forty = 0; *(p+1) = 0; - if (!get_sha1(p - 39, sha1)) { + if (!get_oid(p - (GIT_SHA1_HEXSZ - 1), &oid)) { struct object *o = - lookup_object(sha1); + lookup_object(oid.hash); if (o) name = get_rev_name(o, &buf); } @@ -323,7 +323,7 @@ static void name_rev_line(char *p, struct name_ref_data *data) continue; if (data->name_only) - printf("%.*s%s", p_len - 40, p_start, name); + printf("%.*s%s", p_len - GIT_SHA1_HEXSZ, p_start, name); else printf("%.*s (%s)", p_len, p_start, name); p_start = p + 1; @@ -374,18 +374,18 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) cutoff = 0; for (; argc; argc--, argv++) { - unsigned char sha1[20]; + struct object_id oid; struct object *object; struct commit *commit; - if (get_sha1(*argv, sha1)) { + if (get_oid(*argv, &oid)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); continue; } commit = NULL; - object = parse_object(sha1); + object = parse_object(oid.hash); if (object) { struct object *peeled = deref_tag(object, *argv, 0); if (peeled && peeled->type == OBJ_COMMIT) From af6730e7303d3025a8f50192e0f54acc74142484 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:28:58 +0000 Subject: [PATCH 072/566] builtin/prune: convert to struct object_id Convert the sole instance of unsigned char [20] to struct object_id. cmd_prune is a caller of parse_object, which we will convert later. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/prune.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/prune.c b/builtin/prune.c index 42633e0c6e..96dca7d58a 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -123,11 +123,11 @@ int cmd_prune(int argc, const char **argv, const char *prefix) die(_("cannot prune in a precious-objects repo")); while (argc--) { - unsigned char sha1[20]; + struct object_id oid; const char *name = *argv++; - if (!get_sha1(name, sha1)) { - struct object *object = parse_object_or_die(sha1, name); + if (!get_oid(name, &oid)) { + struct object *object = parse_object_or_die(oid.hash, name); add_pending_object(&revs, object, ""); } else From b8607f35b180a00b3f3240ff7d26d034a83fb23a Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:28:59 +0000 Subject: [PATCH 073/566] bundle: convert to struct object_id Convert the bundle code, plus the sole external user of struct ref_list_entry, to use struct object_id. Include cache.h from within bundle.h to provide the definition. Convert some of the hash parsing code to use parse_oid_hex to avoid needing to hard-code constant values. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- bundle.c | 33 +++++++++++++++++---------------- bundle.h | 4 +++- transport.c | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/bundle.c b/bundle.c index bbf4efa0a0..6e181bb3d8 100644 --- a/bundle.c +++ b/bundle.c @@ -12,11 +12,11 @@ static const char bundle_signature[] = "# v2 git bundle\n"; -static void add_to_ref_list(const unsigned char *sha1, const char *name, +static void add_to_ref_list(const struct object_id *oid, const char *name, struct ref_list *list) { ALLOC_GROW(list->list, list->nr + 1, list->alloc); - hashcpy(list->list[list->nr].sha1, sha1); + oidcpy(&list->list[list->nr].oid, oid); list->list[list->nr].name = xstrdup(name); list->nr++; } @@ -40,8 +40,9 @@ static int parse_bundle_header(int fd, struct bundle_header *header, /* The bundle header ends with an empty line */ while (!strbuf_getwholeline_fd(&buf, fd, '\n') && buf.len && buf.buf[0] != '\n') { - unsigned char sha1[20]; + struct object_id oid; int is_prereq = 0; + const char *p; if (*buf.buf == '-') { is_prereq = 1; @@ -54,9 +55,9 @@ static int parse_bundle_header(int fd, struct bundle_header *header, * Prerequisites have object name that is optionally * followed by SP and subject line. */ - if (get_sha1_hex(buf.buf, sha1) || - (buf.len > 40 && !isspace(buf.buf[40])) || - (!is_prereq && buf.len <= 40)) { + if (parse_oid_hex(buf.buf, &oid, &p) || + (*p && !isspace(*p)) || + (!is_prereq && !*p)) { if (report_path) error(_("unrecognized header: %s%s (%d)"), (is_prereq ? "-" : ""), buf.buf, (int)buf.len); @@ -64,9 +65,9 @@ static int parse_bundle_header(int fd, struct bundle_header *header, break; } else { if (is_prereq) - add_to_ref_list(sha1, "", &header->prerequisites); + add_to_ref_list(&oid, "", &header->prerequisites); else - add_to_ref_list(sha1, buf.buf + 41, &header->references); + add_to_ref_list(&oid, p + 1, &header->references); } } @@ -115,7 +116,7 @@ static int list_refs(struct ref_list *r, int argc, const char **argv) if (j == argc) continue; } - printf("%s %s\n", sha1_to_hex(r->list[i].sha1), + printf("%s %s\n", oid_to_hex(&r->list[i].oid), r->list[i].name); } return 0; @@ -141,7 +142,7 @@ int verify_bundle(struct bundle_header *header, int verbose) init_revisions(&revs, NULL); for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; - struct object *o = parse_object(e->sha1); + struct object *o = parse_object(e->oid.hash); if (o) { o->flags |= PREREQ_MARK; add_pending_object(&revs, o, e->name); @@ -149,7 +150,7 @@ int verify_bundle(struct bundle_header *header, int verbose) } if (++ret == 1) error("%s", message); - error("%s %s", sha1_to_hex(e->sha1), e->name); + error("%s %s", oid_to_hex(&e->oid), e->name); } if (revs.pending.nr != p->nr) return ret; @@ -285,16 +286,16 @@ static int compute_and_write_prerequisites(int bundle_fd, return -1; rls_fout = xfdopen(rls.out, "r"); while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { - unsigned char sha1[20]; + struct object_id oid; if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); - if (!get_sha1_hex(buf.buf + 1, sha1)) { - struct object *object = parse_object_or_die(sha1, buf.buf); + if (!get_oid_hex(buf.buf + 1, &oid)) { + struct object *object = parse_object_or_die(oid.hash, buf.buf); object->flags |= UNINTERESTING; add_pending_object(revs, object, buf.buf); } - } else if (!get_sha1_hex(buf.buf, sha1)) { - struct object *object = parse_object_or_die(sha1, buf.buf); + } else if (!get_oid_hex(buf.buf, &oid)) { + struct object *object = parse_object_or_die(oid.hash, buf.buf); object->flags |= SHOWN; } } diff --git a/bundle.h b/bundle.h index 1584e4d821..e9a4cb6a74 100644 --- a/bundle.h +++ b/bundle.h @@ -1,10 +1,12 @@ #ifndef BUNDLE_H #define BUNDLE_H +#include "cache.h" + struct ref_list { unsigned int nr, alloc; struct ref_list_entry { - unsigned char sha1[20]; + struct object_id oid; char *name; } *list; }; diff --git a/transport.c b/transport.c index 4d33138a75..9bfcf870f9 100644 --- a/transport.c +++ b/transport.c @@ -87,7 +87,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport, int for_pus for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; struct ref *ref = alloc_ref(e->name); - hashcpy(ref->old_oid.hash, e->sha1); + oidcpy(&ref->old_oid, &e->oid); ref->next = result; result = ref; } From 48713bfa2e5aba8bbc0a53d5d34d7940d2d756e3 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:29:00 +0000 Subject: [PATCH 074/566] branch: convert to struct object_id This change is required to convert lookup_commit_reference later. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- branch.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/branch.c b/branch.c index ad5a2299ba..1758c97086 100644 --- a/branch.c +++ b/branch.c @@ -191,9 +191,9 @@ int validate_new_branchname(const char *name, struct strbuf *ref, if (!attr_only) { const char *head; - unsigned char sha1[20]; + struct object_id oid; - head = resolve_ref_unsafe("HEAD", 0, sha1, NULL); + head = resolve_ref_unsafe("HEAD", 0, oid.hash, NULL); if (!is_bare_repository() && head && !strcmp(head, ref->buf)) die(_("Cannot force update the current branch.")); } @@ -233,7 +233,7 @@ void create_branch(const char *name, const char *start_name, int quiet, enum branch_track track) { struct commit *commit; - unsigned char sha1[20]; + struct object_id oid; char *real_ref; struct strbuf ref = STRBUF_INIT; int forcing = 0; @@ -253,7 +253,7 @@ void create_branch(const char *name, const char *start_name, } real_ref = NULL; - if (get_sha1(start_name, sha1)) { + if (get_oid(start_name, &oid)) { if (explicit_tracking) { if (advice_set_upstream_failure) { error(_(upstream_missing), start_name); @@ -265,7 +265,7 @@ void create_branch(const char *name, const char *start_name, die(_("Not a valid object name: '%s'."), start_name); } - switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) { + switch (dwim_ref(start_name, strlen(start_name), oid.hash, &real_ref)) { case 0: /* Not branching from any existing branch */ if (explicit_tracking) @@ -286,9 +286,9 @@ void create_branch(const char *name, const char *start_name, break; } - if ((commit = lookup_commit_reference(sha1)) == NULL) + if ((commit = lookup_commit_reference(oid.hash)) == NULL) die(_("Not a valid branch point: '%s'."), start_name); - hashcpy(sha1, commit->object.oid.hash); + oidcpy(&oid, &commit->object.oid); if (reflog) log_all_ref_updates = LOG_REFS_NORMAL; @@ -306,7 +306,7 @@ void create_branch(const char *name, const char *start_name, transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref.buf, - sha1, forcing ? NULL : null_sha1, + oid.hash, forcing ? NULL : null_sha1, 0, msg, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); From 4931b02f4ab6cbffc39feca95a055ce1a92a20f9 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:29:01 +0000 Subject: [PATCH 075/566] builtin/blame: convert static function to struct object_id This function is a caller of lookup_commit_reference_gently, which we will convert later. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/blame.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 07506a3e45..7d644d0925 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2461,7 +2461,7 @@ static const char *dwim_reverse_initial(struct scoreboard *sb) */ struct object *obj; struct commit *head_commit; - unsigned char head_sha1[20]; + struct object_id head_oid; if (sb->revs->pending.nr != 1) return NULL; @@ -2473,9 +2473,9 @@ static const char *dwim_reverse_initial(struct scoreboard *sb) return NULL; /* Do we have HEAD? */ - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL)) + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) return NULL; - head_commit = lookup_commit_reference_gently(head_sha1, 1); + head_commit = lookup_commit_reference_gently(head_oid.hash, 1); if (!head_commit) return NULL; From 8bc095f7d5b847501e06e8bc1eafc50a314b99b9 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:29:02 +0000 Subject: [PATCH 076/566] builtin/rev-parse: convert to struct object_id Some of the functions converted are callers of lookup_commit_reference. However, the changes involved in converting the entire thing are not too large, so we might as well convert it all. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/rev-parse.c | 56 ++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 0513330910..4af1222644 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -121,7 +121,7 @@ static void show_with_type(int type, const char *arg) } /* Output a revision, only if filter allows it */ -static void show_rev(int type, const unsigned char *sha1, const char *name) +static void show_rev(int type, const struct object_id *oid, const char *name) { if (!(filter & DO_REVS)) return; @@ -129,10 +129,10 @@ static void show_rev(int type, const unsigned char *sha1, const char *name) if ((symbolic || abbrev_ref) && name) { if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) { - unsigned char discard[20]; + struct object_id discard; char *full; - switch (dwim_ref(name, strlen(name), discard, &full)) { + switch (dwim_ref(name, strlen(name), discard.hash, &full)) { case 0: /* * Not found -- not a ref. We could @@ -158,9 +158,9 @@ static void show_rev(int type, const unsigned char *sha1, const char *name) } } else if (abbrev) - show_with_type(type, find_unique_abbrev(sha1, abbrev)); + show_with_type(type, find_unique_abbrev(oid->hash, abbrev)); else - show_with_type(type, sha1_to_hex(sha1)); + show_with_type(type, oid_to_hex(oid)); } /* Output a flag, only if filter allows it. */ @@ -180,11 +180,11 @@ static int show_default(void) const char *s = def; if (s) { - unsigned char sha1[20]; + struct object_id oid; def = NULL; - if (!get_sha1(s, sha1)) { - show_rev(NORMAL, sha1, s); + if (!get_oid(s, &oid)) { + show_rev(NORMAL, &oid, s); return 1; } } @@ -195,19 +195,19 @@ static int show_reference(const char *refname, const struct object_id *oid, int { if (ref_excluded(ref_excludes, refname)) return 0; - show_rev(NORMAL, oid->hash, refname); + show_rev(NORMAL, oid, refname); return 0; } static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) { - show_rev(REVERSED, oid->hash, refname); + show_rev(REVERSED, oid, refname); return 0; } static int show_abbrev(const struct object_id *oid, void *cb_data) { - show_rev(NORMAL, oid->hash, NULL); + show_rev(NORMAL, oid, NULL); return 0; } @@ -242,8 +242,8 @@ static int show_file(const char *arg, int output_prefix) static int try_difference(const char *arg) { char *dotdot; - unsigned char sha1[20]; - unsigned char end[20]; + struct object_id oid; + struct object_id end; const char *next; const char *this; int symmetric; @@ -273,18 +273,18 @@ static int try_difference(const char *arg) return 0; } - if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) { - show_rev(NORMAL, end, next); - show_rev(symmetric ? NORMAL : REVERSED, sha1, this); + if (!get_sha1_committish(this, oid.hash) && !get_sha1_committish(next, end.hash)) { + show_rev(NORMAL, &end, next); + show_rev(symmetric ? NORMAL : REVERSED, &oid, this); if (symmetric) { struct commit_list *exclude; struct commit *a, *b; - a = lookup_commit_reference(sha1); - b = lookup_commit_reference(end); + a = lookup_commit_reference(oid.hash); + b = lookup_commit_reference(end.hash); exclude = get_merge_bases(a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); - show_rev(REVERSED, commit->object.oid.hash, NULL); + show_rev(REVERSED, &commit->object.oid, NULL); } } *dotdot = '.'; @@ -297,7 +297,7 @@ static int try_difference(const char *arg) static int try_parent_shorthands(const char *arg) { char *dotdot; - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; struct commit_list *parents; int parent_number; @@ -327,12 +327,12 @@ static int try_parent_shorthands(const char *arg) return 0; *dotdot = 0; - if (get_sha1_committish(arg, sha1)) { + if (get_sha1_committish(arg, oid.hash)) { *dotdot = '^'; return 0; } - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(oid.hash); if (exclude_parent && exclude_parent > commit_list_count(commit->parents)) { *dotdot = '^'; @@ -340,7 +340,7 @@ static int try_parent_shorthands(const char *arg) } if (include_rev) - show_rev(NORMAL, sha1, arg); + show_rev(NORMAL, &oid, arg); for (parents = commit->parents, parent_number = 1; parents; parents = parents->next, parent_number++) { @@ -352,7 +352,7 @@ static int try_parent_shorthands(const char *arg) if (symbolic) name = xstrfmt("%s^%d", arg, parent_number); show_rev(include_parents ? NORMAL : REVERSED, - parents->item->object.oid.hash, name); + &parents->item->object.oid, name); free(name); } @@ -571,7 +571,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) int did_repo_setup = 0; int has_dashdash = 0; int output_prefix = 0; - unsigned char sha1[20]; + struct object_id oid; unsigned int flags = 0; const char *name = NULL; struct object_context unused; @@ -910,11 +910,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) name++; type = REVERSED; } - if (!get_sha1_with_context(name, flags, sha1, &unused)) { + if (!get_sha1_with_context(name, flags, oid.hash, &unused)) { if (verify) revs_count++; else - show_rev(type, sha1, name); + show_rev(type, &oid, name); continue; } if (verify) @@ -929,7 +929,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) strbuf_release(&buf); if (verify) { if (revs_count == 1) { - show_rev(type, sha1, name); + show_rev(type, &oid, name); return 0; } else if (revs_count == 0 && show_default()) return 0; From d7e6b6a8dcc8a98a4dcf6bc291f1e68b1acaecae Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Mon, 1 May 2017 02:29:03 +0000 Subject: [PATCH 077/566] fast-import: convert internal structs to struct object_id Convert struct tree_entry_ms, struct branch, struct tag, and struct hash_list to use struct object_id by changing the definition and applying the following semantic patch, plus the standard object_id transforms: @@ struct tree_entry_ms E1; @@ - E1.sha1 + E1.oid.hash @@ struct tree_entry_ms *E1; @@ - E1->sha1 + E1->oid.hash @@ struct branch E1; @@ - E1.sha1 + E1.oid.hash @@ struct branch *E1; @@ - E1->sha1 + E1->oid.hash @@ struct tag E1; @@ - E1.sha1 + E1.oid.hash @@ struct tag *E1; @@ - E1->sha1 + E1->oid.hash @@ struct hash_list E1; @@ - E1.sha1 + E1.oid.hash @@ struct hash_list *E1; @@ - E1->sha1 + E1->oid.hash Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- fast-import.c | 182 ++++++++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 86 deletions(-) diff --git a/fast-import.c b/fast-import.c index cf58f875b8..0212635ddd 100644 --- a/fast-import.c +++ b/fast-import.c @@ -226,7 +226,7 @@ struct tree_entry { struct atom_str *name; struct tree_entry_ms { uint16_t mode; - unsigned char sha1[20]; + struct object_id oid; } versions[2]; }; @@ -252,19 +252,19 @@ struct branch { unsigned active : 1; unsigned delete : 1; unsigned pack_id : PACK_ID_BITS; - unsigned char sha1[20]; + struct object_id oid; }; struct tag { struct tag *next_tag; const char *name; unsigned int pack_id; - unsigned char sha1[20]; + struct object_id oid; }; struct hash_list { struct hash_list *next; - unsigned char sha1[20]; + struct object_id oid; }; typedef enum { @@ -386,13 +386,15 @@ static void write_branch_report(FILE *rpt, struct branch *b) fputs(" active", rpt); if (b->branch_tree.tree) fputs(" loaded", rpt); - if (is_null_sha1(b->branch_tree.versions[1].sha1)) + if (is_null_oid(&b->branch_tree.versions[1].oid)) fputs(" dirty", rpt); fputc('\n', rpt); - fprintf(rpt, " tip commit : %s\n", sha1_to_hex(b->sha1)); - fprintf(rpt, " old tree : %s\n", sha1_to_hex(b->branch_tree.versions[0].sha1)); - fprintf(rpt, " cur tree : %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1)); + fprintf(rpt, " tip commit : %s\n", oid_to_hex(&b->oid)); + fprintf(rpt, " old tree : %s\n", + oid_to_hex(&b->branch_tree.versions[0].oid)); + fprintf(rpt, " cur tree : %s\n", + oid_to_hex(&b->branch_tree.versions[1].oid)); fprintf(rpt, " commit clock: %" PRIuMAX "\n", b->last_commit); fputs(" last pack : ", rpt); @@ -470,7 +472,7 @@ static void write_crash_report(const char *err) fputs("Annotated Tags\n", rpt); fputs("--------------\n", rpt); for (tg = first_tag; tg; tg = tg->next_tag) { - fputs(sha1_to_hex(tg->sha1), rpt); + fputs(oid_to_hex(&tg->oid), rpt); fputc(' ', rpt); fputs(tg->name, rpt); fputc('\n', rpt); @@ -876,7 +878,7 @@ static struct tree_content *dup_tree_content(struct tree_content *s) a = s->entries[i]; b = new_tree_entry(); memcpy(b, a, sizeof(*a)); - if (a->tree && is_null_sha1(b->versions[1].sha1)) + if (a->tree && is_null_oid(&b->versions[1].oid)) b->tree = dup_tree_content(a->tree); else b->tree = NULL; @@ -1041,12 +1043,14 @@ static void end_packfile(void) for (i = 0; i < branch_table_sz; i++) { for (b = branch_table[i]; b; b = b->table_next_branch) { if (b->pack_id == pack_id) - fprintf(pack_edges, " %s", sha1_to_hex(b->sha1)); + fprintf(pack_edges, " %s", + oid_to_hex(&b->oid)); } } for (t = first_tag; t; t = t->next_tag) { if (t->pack_id == pack_id) - fprintf(pack_edges, " %s", sha1_to_hex(t->sha1)); + fprintf(pack_edges, " %s", + oid_to_hex(&t->oid)); } fputc('\n', pack_edges); fflush(pack_edges); @@ -1385,7 +1389,7 @@ static const char *get_mode(const char *str, uint16_t *modep) static void load_tree(struct tree_entry *root) { - unsigned char *sha1 = root->versions[1].sha1; + unsigned char *sha1 = root->versions[1].oid.hash; struct object_entry *myoe; struct tree_content *t; unsigned long size; @@ -1426,8 +1430,8 @@ static void load_tree(struct tree_entry *root) e->versions[0].mode = e->versions[1].mode; e->name = to_atom(c, strlen(c)); c += e->name->str_len + 1; - hashcpy(e->versions[0].sha1, (unsigned char *)c); - hashcpy(e->versions[1].sha1, (unsigned char *)c); + hashcpy(e->versions[0].oid.hash, (unsigned char *)c); + hashcpy(e->versions[1].oid.hash, (unsigned char *)c); c += 20; } free(buf); @@ -1475,7 +1479,7 @@ static void mktree(struct tree_content *t, int v, struct strbuf *b) strbuf_addf(b, "%o %s%c", (unsigned int)(e->versions[v].mode & ~NO_DELTA), e->name->str_dat, '\0'); - strbuf_add(b, e->versions[v].sha1, 20); + strbuf_add(b, e->versions[v].oid.hash, 20); } } @@ -1486,7 +1490,7 @@ static void store_tree(struct tree_entry *root) struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 }; struct object_entry *le = NULL; - if (!is_null_sha1(root->versions[1].sha1)) + if (!is_null_oid(&root->versions[1].oid)) return; if (!root->tree) @@ -1499,7 +1503,7 @@ static void store_tree(struct tree_entry *root) } if (!(root->versions[0].mode & NO_DELTA)) - le = find_object(root->versions[0].sha1); + le = find_object(root->versions[0].oid.hash); if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) { mktree(t, 0, &old_tree); lo.data = old_tree; @@ -1508,14 +1512,14 @@ static void store_tree(struct tree_entry *root) } mktree(t, 1, &new_tree); - store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0); + store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].oid.hash, 0); t->delta_depth = lo.depth; for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { struct tree_entry *e = t->entries[i]; if (e->versions[1].mode) { e->versions[0].mode = e->versions[1].mode; - hashcpy(e->versions[0].sha1, e->versions[1].sha1); + oidcpy(&e->versions[0].oid, &e->versions[1].oid); t->entries[j++] = e; } else { release_tree_entry(e); @@ -1533,8 +1537,8 @@ static void tree_content_replace( { if (!S_ISDIR(mode)) die("Root cannot be a non-directory"); - hashclr(root->versions[0].sha1); - hashcpy(root->versions[1].sha1, sha1); + oidclr(&root->versions[0].oid); + hashcpy(root->versions[1].oid.hash, sha1); if (root->tree) release_tree_content_recursive(root->tree); root->tree = newtree; @@ -1568,10 +1572,10 @@ static int tree_content_set( if (!*slash1) { if (!S_ISDIR(mode) && e->versions[1].mode == mode - && !hashcmp(e->versions[1].sha1, sha1)) + && !hashcmp(e->versions[1].oid.hash, sha1)) return 0; e->versions[1].mode = mode; - hashcpy(e->versions[1].sha1, sha1); + hashcpy(e->versions[1].oid.hash, sha1); if (e->tree) release_tree_content_recursive(e->tree); e->tree = subtree; @@ -1592,7 +1596,7 @@ static int tree_content_set( if (S_ISDIR(e->versions[0].mode)) e->versions[0].mode |= NO_DELTA; - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } if (!S_ISDIR(e->versions[1].mode)) { @@ -1602,7 +1606,7 @@ static int tree_content_set( if (!e->tree) load_tree(e); if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) { - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } return 0; @@ -1614,7 +1618,7 @@ static int tree_content_set( e = new_tree_entry(); e->name = to_atom(p, n); e->versions[0].mode = 0; - hashclr(e->versions[0].sha1); + oidclr(&e->versions[0].oid); t->entries[t->entry_count++] = e; if (*slash1) { e->tree = new_tree_content(8); @@ -1623,9 +1627,9 @@ static int tree_content_set( } else { e->tree = subtree; e->versions[1].mode = mode; - hashcpy(e->versions[1].sha1, sha1); + hashcpy(e->versions[1].oid.hash, sha1); } - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } @@ -1670,7 +1674,7 @@ static int tree_content_remove( if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) { for (n = 0; n < e->tree->entry_count; n++) { if (e->tree->entries[n]->versions[1].mode) { - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } } @@ -1689,8 +1693,8 @@ del_entry: release_tree_content_recursive(e->tree); e->tree = NULL; e->versions[1].mode = 0; - hashclr(e->versions[1].sha1); - hashclr(root->versions[1].sha1); + oidclr(&e->versions[1].oid); + oidclr(&root->versions[1].oid); return 1; } @@ -1735,7 +1739,7 @@ static int tree_content_get( found_entry: memcpy(leaf, e, sizeof(*leaf)); - if (e->tree && is_null_sha1(e->versions[1].sha1)) + if (e->tree && is_null_oid(&e->versions[1].oid)) leaf->tree = dup_tree_content(e->tree); else leaf->tree = NULL; @@ -1749,7 +1753,7 @@ static int update_branch(struct branch *b) unsigned char old_sha1[20]; struct strbuf err = STRBUF_INIT; - if (is_null_sha1(b->sha1)) { + if (is_null_oid(&b->oid)) { if (b->delete) delete_ref(NULL, b->name, NULL, 0); return 0; @@ -1760,20 +1764,21 @@ static int update_branch(struct branch *b) struct commit *old_cmit, *new_cmit; old_cmit = lookup_commit_reference_gently(old_sha1, 0); - new_cmit = lookup_commit_reference_gently(b->sha1, 0); + new_cmit = lookup_commit_reference_gently(b->oid.hash, 0); if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); if (!in_merge_bases(old_cmit, new_cmit)) { warning("Not updating %s" " (new tip %s does not contain %s)", - b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1)); + b->name, oid_to_hex(&b->oid), + sha1_to_hex(old_sha1)); return -1; } } transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, b->name, b->sha1, old_sha1, + ref_transaction_update(transaction, b->name, b->oid.hash, old_sha1, 0, msg, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@ -1815,7 +1820,7 @@ static void dump_tags(void) strbuf_addf(&ref_name, "refs/tags/%s", t->name); if (ref_transaction_update(transaction, ref_name.buf, - t->sha1, NULL, 0, msg, &err)) { + t->oid.hash, NULL, 0, msg, &err)) { failure |= error("%s", err.buf); goto cleanup; } @@ -2274,7 +2279,7 @@ static uintmax_t do_change_note_fanout( if (!tree_content_remove(orig_root, fullpath, &leaf, 0)) die("Failed to remove path %s", fullpath); tree_content_set(orig_root, realpath, - leaf.versions[1].sha1, + leaf.versions[1].oid.hash, leaf.versions[1].mode, leaf.tree); } else if (S_ISDIR(e->versions[1].mode)) { @@ -2504,13 +2509,13 @@ static void file_change_cr(const char *s, struct branch *b, int rename) die("Path %s not in branch", s); if (!*d) { /* C "path/to/subdir" "" */ tree_content_replace(&b->branch_tree, - leaf.versions[1].sha1, + leaf.versions[1].oid.hash, leaf.versions[1].mode, leaf.tree); return; } tree_content_set(&b->branch_tree, d, - leaf.versions[1].sha1, + leaf.versions[1].oid.hash, leaf.versions[1].mode, leaf.tree); } @@ -2561,9 +2566,9 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa /* */ s = lookup_branch(p); if (s) { - if (is_null_sha1(s->sha1)) + if (is_null_oid(&s->oid)) die("Can't add a note on empty branch."); - hashcpy(commit_sha1, s->sha1); + hashcpy(commit_sha1, s->oid.hash); } else if (*p == ':') { uintmax_t commit_mark = parse_mark_ref_eol(p); struct object_entry *commit_oe = find_mark(commit_mark); @@ -2616,8 +2621,8 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa static void file_change_deleteall(struct branch *b) { release_tree_content_recursive(b->branch_tree.tree); - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); + oidclr(&b->branch_tree.versions[0].oid); + oidclr(&b->branch_tree.versions[1].oid); load_tree(&b->branch_tree); b->num_notes = 0; } @@ -2625,25 +2630,26 @@ static void file_change_deleteall(struct branch *b) static void parse_from_commit(struct branch *b, char *buf, unsigned long size) { if (!buf || size < 46) - die("Not a valid commit: %s", sha1_to_hex(b->sha1)); + die("Not a valid commit: %s", oid_to_hex(&b->oid)); if (memcmp("tree ", buf, 5) - || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) - die("The commit %s is corrupt", sha1_to_hex(b->sha1)); - hashcpy(b->branch_tree.versions[0].sha1, - b->branch_tree.versions[1].sha1); + || get_sha1_hex(buf + 5, b->branch_tree.versions[1].oid.hash)) + die("The commit %s is corrupt", oid_to_hex(&b->oid)); + oidcpy(&b->branch_tree.versions[0].oid, + &b->branch_tree.versions[1].oid); } static void parse_from_existing(struct branch *b) { - if (is_null_sha1(b->sha1)) { - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); + if (is_null_oid(&b->oid)) { + oidclr(&b->branch_tree.versions[0].oid); + oidclr(&b->branch_tree.versions[1].oid); } else { unsigned long size; char *buf; - buf = read_object_with_reference(b->sha1, - commit_type, &size, b->sha1); + buf = read_object_with_reference(b->oid.hash, + commit_type, &size, + b->oid.hash); parse_from_commit(b, buf, size); free(buf); } @@ -2658,23 +2664,23 @@ static int parse_from(struct branch *b) if (!skip_prefix(command_buf.buf, "from ", &from)) return 0; - hashcpy(sha1, b->branch_tree.versions[1].sha1); + hashcpy(sha1, b->branch_tree.versions[1].oid.hash); s = lookup_branch(from); if (b == s) die("Can't create a branch from itself: %s", b->name); else if (s) { - unsigned char *t = s->branch_tree.versions[1].sha1; - hashcpy(b->sha1, s->sha1); - hashcpy(b->branch_tree.versions[0].sha1, t); - hashcpy(b->branch_tree.versions[1].sha1, t); + unsigned char *t = s->branch_tree.versions[1].oid.hash; + oidcpy(&b->oid, &s->oid); + hashcpy(b->branch_tree.versions[0].oid.hash, t); + hashcpy(b->branch_tree.versions[1].oid.hash, t); } else if (*from == ':') { uintmax_t idnum = parse_mark_ref_eol(from); struct object_entry *oe = find_mark(idnum); if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); - if (hashcmp(b->sha1, oe->idx.sha1)) { - hashcpy(b->sha1, oe->idx.sha1); + if (hashcmp(b->oid.hash, oe->idx.sha1)) { + hashcpy(b->oid.hash, oe->idx.sha1); if (oe->pack_id != MAX_PACK_ID) { unsigned long size; char *buf = gfi_unpack_entry(oe, &size); @@ -2683,15 +2689,15 @@ static int parse_from(struct branch *b) } else parse_from_existing(b); } - } else if (!get_sha1(from, b->sha1)) { + } else if (!get_sha1(from, b->oid.hash)) { parse_from_existing(b); - if (is_null_sha1(b->sha1)) + if (is_null_oid(&b->oid)) b->delete = 1; } else die("Invalid ref name or SHA1 expression: %s", from); - if (b->branch_tree.tree && hashcmp(sha1, b->branch_tree.versions[1].sha1)) { + if (b->branch_tree.tree && hashcmp(sha1, b->branch_tree.versions[1].oid.hash)) { release_tree_content_recursive(b->branch_tree.tree); b->branch_tree.tree = NULL; } @@ -2711,17 +2717,19 @@ static struct hash_list *parse_merge(unsigned int *count) n = xmalloc(sizeof(*n)); s = lookup_branch(from); if (s) - hashcpy(n->sha1, s->sha1); + oidcpy(&n->oid, &s->oid); else if (*from == ':') { uintmax_t idnum = parse_mark_ref_eol(from); struct object_entry *oe = find_mark(idnum); if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); - hashcpy(n->sha1, oe->idx.sha1); - } else if (!get_sha1(from, n->sha1)) { + hashcpy(n->oid.hash, oe->idx.sha1); + } else if (!get_sha1(from, n->oid.hash)) { unsigned long size; - char *buf = read_object_with_reference(n->sha1, - commit_type, &size, n->sha1); + char *buf = read_object_with_reference(n->oid.hash, + commit_type, + &size, + n->oid.hash); if (!buf || size < 46) die("Not a valid commit: %s", from); free(buf); @@ -2808,17 +2816,19 @@ static void parse_new_commit(const char *arg) /* build the tree and the commit */ store_tree(&b->branch_tree); - hashcpy(b->branch_tree.versions[0].sha1, - b->branch_tree.versions[1].sha1); + oidcpy(&b->branch_tree.versions[0].oid, + &b->branch_tree.versions[1].oid); strbuf_reset(&new_data); strbuf_addf(&new_data, "tree %s\n", - sha1_to_hex(b->branch_tree.versions[1].sha1)); - if (!is_null_sha1(b->sha1)) - strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1)); + oid_to_hex(&b->branch_tree.versions[1].oid)); + if (!is_null_oid(&b->oid)) + strbuf_addf(&new_data, "parent %s\n", + oid_to_hex(&b->oid)); while (merge_list) { struct hash_list *next = merge_list->next; - strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1)); + strbuf_addf(&new_data, "parent %s\n", + oid_to_hex(&merge_list->oid)); free(merge_list); merge_list = next; } @@ -2831,7 +2841,7 @@ static void parse_new_commit(const char *arg) free(author); free(committer); - if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark)) + if (!store_object(OBJ_COMMIT, &new_data, NULL, b->oid.hash, next_mark)) b->pack_id = pack_id; b->last_commit = object_count_by_type[OBJ_COMMIT]; } @@ -2863,9 +2873,9 @@ static void parse_new_tag(const char *arg) die("Expected from command, got %s", command_buf.buf); s = lookup_branch(from); if (s) { - if (is_null_sha1(s->sha1)) + if (is_null_oid(&s->oid)) die("Can't tag an empty branch."); - hashcpy(sha1, s->sha1); + hashcpy(sha1, s->oid.hash); type = OBJ_COMMIT; } else if (*from == ':') { struct object_entry *oe; @@ -2910,7 +2920,7 @@ static void parse_new_tag(const char *arg) strbuf_addbuf(&new_data, &msg); free(tagger); - if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0)) + if (store_object(OBJ_TAG, &new_data, NULL, t->oid.hash, 0)) t->pack_id = MAX_PACK_ID; else t->pack_id = pack_id; @@ -2922,9 +2932,9 @@ static void parse_reset_branch(const char *arg) b = lookup_branch(arg); if (b) { - hashclr(b->sha1); - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); + oidclr(&b->oid); + oidclr(&b->branch_tree.versions[0].oid); + oidclr(&b->branch_tree.versions[1].oid); if (b->branch_tree.tree) { release_tree_content_recursive(b->branch_tree.tree); b->branch_tree.tree = NULL; @@ -3143,8 +3153,8 @@ static void parse_ls(const char *p, struct branch *b) } else { struct object_entry *e = parse_treeish_dataref(&p); root = new_tree_entry(); - hashcpy(root->versions[1].sha1, e->idx.sha1); - if (!is_null_sha1(root->versions[1].sha1)) + hashcpy(root->versions[1].oid.hash, e->idx.sha1); + if (!is_null_oid(&root->versions[1].oid)) root->versions[1].mode = S_IFDIR; load_tree(root); } @@ -3166,7 +3176,7 @@ static void parse_ls(const char *p, struct branch *b) if (S_ISDIR(leaf.versions[1].mode)) store_tree(&leaf); - print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p); + print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, p); if (leaf.tree) release_tree_content_recursive(leaf.tree); if (!b || root != &b->branch_tree) From 515360f9e9be976344e3d21cf6f28dcd2ff3c4f9 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 1 May 2017 17:21:14 -0700 Subject: [PATCH 078/566] credential doc: make multiple-helper behavior more prominent Git's configuration system works by reading multiple configuration files in order, from general to specific: - first, the system configuration /etc/gitconfig - then the user's configuration (~/.gitconfig or ~/.config/git/config) - then the repository configuration (.git/config) For single-valued configuration items, the latest value wins. For multi-valued configuration items, values accumulate in that order. For example, this allows setting a credential helper globally in ~/.gitconfig that git will try to use in all repositories, regardless of whether they additionally provide another helper. This is usually a nice thing --- e.g. I can install helpers to use my OS keychain and to cache credentials for a short period of time globally. Sometimes people want to be able to override an inherited setting. For the credential.helper setting, this is done by setting the configuration item to empty before giving it a new value. This is already documented but the documentation is hard to find --- git-config(1) says to look at gitcredentials(7) and the config reference in gitcredentials(7) doesn't mention this issue. Move the documentation to the config reference to make it easier to find. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/gitcredentials.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt index f3a75d1ce1..f970196bc1 100644 --- a/Documentation/gitcredentials.txt +++ b/Documentation/gitcredentials.txt @@ -101,16 +101,6 @@ $ git help credential-foo $ git config --global credential.helper foo ------------------------------------------- -If there are multiple instances of the `credential.helper` configuration -variable, each helper will be tried in turn, and may provide a username, -password, or nothing. Once Git has acquired both a username and a -password, no more helpers will be tried. - -If `credential.helper` is configured to the empty string, this resets -the helper list to empty (so you may override a helper set by a -lower-priority config file by configuring the empty-string helper, -followed by whatever set of helpers you would like). - CREDENTIAL CONTEXTS ------------------- @@ -162,6 +152,16 @@ helper:: shell (so, for example, setting this to `foo --option=bar` will execute `git credential-foo --option=bar` via the shell. See the manual of specific helpers for examples of their use. ++ +If there are multiple instances of the `credential.helper` configuration +variable, each helper will be tried in turn, and may provide a username, +password, or nothing. Once Git has acquired both a username and a +password, no more helpers will be tried. ++ +If `credential.helper` is configured to the empty string, this resets +the helper list to empty (so you may override a helper set by a +lower-priority config file by configuring the empty-string helper, +followed by whatever set of helpers you would like). username:: From db4eca1feaafa0669b7ba64c10314dfe8836576a Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 1 May 2017 17:05:15 -0700 Subject: [PATCH 079/566] clone: handle empty config values in -c "git clone --config" uses the following incantation to add an item to a config file, instead of replacing an existing value: git_config_set_multivar_gently(key, value, "^$", 0) As long as no existing value matches the regex ^$, that works as intended and adds to the config. When a value is empty, though, it replaces the existing value. Noticed while trying to set credential.helper during a clone to use a specific helper without inheriting from ~/.gitconfig and /etc/gitconfig. That is, I ran git clone -c credential.helper= \ -c credential.helper=myhelper \ https://example.com/repo intending to produce the configuration [credential] helper = helper = myhelper Without this patch, the 'helper =' line is not included and the credential helper from /etc/gitconfig gets used. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- builtin/clone.c | 4 +++- t/t5611-clone-config.sh | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/builtin/clone.c b/builtin/clone.c index 6c76a6ed66..8f45a95c83 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -755,7 +755,9 @@ static int checkout(int submodule_progress) static int write_one_config(const char *key, const char *value, void *data) { - return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0); + return git_config_set_multivar_gently(key, + value ? value : "true", + CONFIG_REGEX_NONE, 0); } static void write_config(struct string_list *config) diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh index e4850b778c..39329eb7a8 100755 --- a/t/t5611-clone-config.sh +++ b/t/t5611-clone-config.sh @@ -19,6 +19,14 @@ test_expect_success 'clone -c can set multi-keys' ' test_cmp expect actual ' +test_expect_success 'clone -c can set multi-keys, including some empty' ' + rm -rf child && + git clone -c credential.helper= -c credential.helper=hi . child && + printf "%s\n" "" hi >expect && + git --git-dir=child/.git config --get-all credential.helper >actual && + test_cmp expect actual +' + test_expect_success 'clone -c without a value is boolean true' ' rm -rf child && git clone -c core.foo . child && From a17062cf42556593fb73547e613b0be867026257 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 2 May 2017 12:32:12 -0700 Subject: [PATCH 080/566] submodule_move_head: reuse child_process structure for futher commands We do not need to declare another struct child_process, but we can just reuse the existing `cp` struct. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/submodule.c b/submodule.c index d3299e29c0..a2377ce019 100644 --- a/submodule.c +++ b/submodule.c @@ -1468,15 +1468,15 @@ int submodule_move_head(const char *path, if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) { if (new) { - struct child_process cp1 = CHILD_PROCESS_INIT; + child_process_init(&cp); /* also set the HEAD accordingly */ - cp1.git_cmd = 1; - cp1.no_stdin = 1; - cp1.dir = path; + cp.git_cmd = 1; + cp.no_stdin = 1; + cp.dir = path; - argv_array_pushl(&cp1.args, "update-ref", "HEAD", new, NULL); + argv_array_pushl(&cp.args, "update-ref", "HEAD", new, NULL); - if (run_command(&cp1)) { + if (run_command(&cp)) { ret = -1; goto out; } From da27bc81f00fb5315341b26db8879c266e8ba1a2 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 2 May 2017 12:32:13 -0700 Subject: [PATCH 081/566] submodule: avoid auto-discovery in new working tree manipulator code All commands that are run in a submodule, are run in a correct setup, there is no need to prepare the environment without setting the GIT_DIR variable. By setting the GIT_DIR variable we fix issues as discussed in 10f5c52656 (submodule: avoid auto-discovery in prepare_submodule_repo_env(), 2016-09-01) Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/submodule.c b/submodule.c index a2377ce019..b0141a66dd 100644 --- a/submodule.c +++ b/submodule.c @@ -1363,7 +1363,7 @@ static int submodule_has_dirty_index(const struct submodule *sub) { struct child_process cp = CHILD_PROCESS_INIT; - prepare_submodule_repo_env_no_git_dir(&cp.env_array); + prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; argv_array_pushl(&cp.args, "diff-index", "--quiet", @@ -1380,7 +1380,7 @@ static int submodule_has_dirty_index(const struct submodule *sub) static void submodule_reset_index(const char *path) { struct child_process cp = CHILD_PROCESS_INIT; - prepare_submodule_repo_env_no_git_dir(&cp.env_array); + prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; @@ -1438,7 +1438,7 @@ int submodule_move_head(const char *path, } } - prepare_submodule_repo_env_no_git_dir(&cp.env_array); + prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; cp.no_stdin = 1; From 218c883783ee7c23a0955507f5b7ac4027428d63 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 2 May 2017 12:32:14 -0700 Subject: [PATCH 082/566] submodule: properly recurse for read-tree and checkout We forgot to prepare the submodule env, which is only a problem for nested submodules. See 2e5d6503bd (ls-files: fix recurse-submodules with nested submodules, 2017-04-13) for further explanation. To come up with a proper test for this, we'd need to look at nested submodules just as in that given commit. It turns out we're lucky and these tests already exist, but are marked as failing. We need to pass `--recurse-submodules` to read-tree additionally to make these tests pass. Passing that flag alone would not make the tests pass, such that this covers testing for the bug fix of the submodule env as well. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 3 ++- t/lib-submodule-update.sh | 7 +------ t/t1013-read-tree-submodule.sh | 1 - t/t2013-checkout-submodule.sh | 1 - 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/submodule.c b/submodule.c index b0141a66dd..b3ae642f29 100644 --- a/submodule.c +++ b/submodule.c @@ -1446,7 +1446,7 @@ int submodule_move_head(const char *path, argv_array_pushf(&cp.args, "--super-prefix=%s%s/", get_super_prefix_or_empty(), path); - argv_array_pushl(&cp.args, "read-tree", NULL); + argv_array_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL); if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN) argv_array_push(&cp.args, "-n"); @@ -1474,6 +1474,7 @@ int submodule_move_head(const char *path, cp.no_stdin = 1; cp.dir = path; + prepare_submodule_repo_env(&cp.env_array); argv_array_pushl(&cp.args, "update-ref", "HEAD", new, NULL); if (run_command(&cp)) { diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index fb4f7b014e..2c17826e95 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -787,11 +787,6 @@ test_submodule_switch_recursing () { then RESULTDS=failure fi - RESULTR=success - if test "$KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED" = 1 - then - RESULTR=failure - fi RESULTOI=success if test "$KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED" = 1 then @@ -1003,7 +998,7 @@ test_submodule_switch_recursing () { ' # recursing deeper than one level doesn't work yet. - test_expect_$RESULTR "$command: modified submodule updates submodule recursively" ' + test_expect_success "$command: modified submodule updates submodule recursively" ' prolog && reset_work_tree_to_interested add_nested_sub && ( diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh index de1ba02dc5..7019d0a04f 100755 --- a/t/t1013-read-tree-submodule.sh +++ b/t/t1013-read-tree-submodule.sh @@ -5,7 +5,6 @@ test_description='read-tree can handle submodules' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh -KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1 diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh index e8f70b806f..aa35223369 100755 --- a/t/t2013-checkout-submodule.sh +++ b/t/t2013-checkout-submodule.sh @@ -64,7 +64,6 @@ test_expect_success '"checkout " honors submodule.*.ignore from .git/ ' KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 -KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 test_submodule_switch_recursing "git checkout --recurse-submodules" test_submodule_forced_switch_recursing "git checkout -f --recurse-submodules" From 6fa68ff28856f85f8633ea054856e98962f167d0 Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Sat, 29 Apr 2017 20:59:58 +0200 Subject: [PATCH 083/566] travis-ci: handle Git for Windows CI status "failed" explicitly Git for Windows CI returns "completed: failed" if a build or test failure happened. This case was processed as "Unhandled status". Handle the case explicitly. Signed-off-by: Lars Schneider Signed-off-by: Junio C Hamano --- ci/run-windows-build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/run-windows-build.sh b/ci/run-windows-build.sh index 4e3a50b60e..c72a2bd526 100755 --- a/ci/run-windows-build.sh +++ b/ci/run-windows-build.sh @@ -61,7 +61,8 @@ do case "$STATUS" in inProgress|postponed|notStarted) sleep 10 ;; # continue "completed: succeeded") RESULT="success"; break;; # success - *) echo "Unhandled status: $STATUS"; break;; # failure + "completed: failed") break;; # failure + *) echo "Unhandled status: $STATUS"; break;; # unknown esac done From 016d66f512bbbd7d766f6244d989b4549d5b0d5b Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Wed, 3 May 2017 23:50:15 +0200 Subject: [PATCH 084/566] travis-ci: retry if Git for Windows CI returns HTTP error 502 or 503 The Git for Windows CI web app sometimes returns HTTP errors of "502 bad gateway" or "503 service unavailable" [1]. We also need to check the HTTP content because the GfW web app seems to pass through (error) results from other Azure calls with HTTP code 200. Wait a little and retry the request if this happens. [1] https://docs.microsoft.com/en-in/azure/app-service-web/app-service-web-troubleshoot-http-502-http-503 Signed-off-by: Lars Schneider Signed-off-by: Junio C Hamano --- ci/run-windows-build.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ci/run-windows-build.sh b/ci/run-windows-build.sh index c72a2bd526..c55efa1d1f 100755 --- a/ci/run-windows-build.sh +++ b/ci/run-windows-build.sh @@ -14,14 +14,33 @@ COMMIT=$2 gfwci () { local CURL_ERROR_CODE HTTP_CODE - exec 3>&1 + CONTENT_FILE=$(mktemp -t "git-windows-ci-XXXXXX") + while test -z $HTTP_CODE + do HTTP_CODE=$(curl \ -H "Authentication: Bearer $GFW_CI_TOKEN" \ --silent --retry 5 --write-out '%{HTTP_CODE}' \ - --output >(sed "$(printf '1s/^\xef\xbb\xbf//')" >cat >&3) \ + --output >(sed "$(printf '1s/^\xef\xbb\xbf//')" >$CONTENT_FILE) \ "https://git-for-windows-ci.azurewebsites.net/api/TestNow?$1" \ ) CURL_ERROR_CODE=$? + # The GfW CI web app sometimes returns HTTP errors of + # "502 bad gateway" or "503 service unavailable". + # We also need to check the HTTP content because the GfW web + # app seems to pass through (error) results from other Azure + # calls with HTTP code 200. + # Wait a little and retry if we detect this error. More info: + # https://docs.microsoft.com/en-in/azure/app-service-web/app-service-web-troubleshoot-http-502-http-503 + if test $HTTP_CODE -eq 502 || + test $HTTP_CODE -eq 503 || + grep "502 - Web server received an invalid response" $CONTENT_FILE >/dev/null + then + sleep 10 + HTTP_CODE= + fi + done + cat $CONTENT_FILE + rm $CONTENT_FILE if test $CURL_ERROR_CODE -ne 0 then return $CURL_ERROR_CODE From 0e6d899feeda4ebe87ddd44cba7279f55c5177ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Wed, 3 May 2017 13:54:30 +0000 Subject: [PATCH 085/566] config.mak.uname: set NO_REGEX=NeedsStartEnd on AIX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the NO_REGEX=NeedsStartEnd Makefile flag by default on AIX. Since commit 2f8952250a ("regex: add regexec_buf() that can work on a non NUL-terminated string", 2016-09-21) git has errored out at compile-time if the regular expression library doesn't support REG_STARTEND. While looking through Google search results for the use of NO_REGEX I found a Chef recipe that set this on AIX[1], looking through the documentation for the latest version of AIX (7.2, released October 2015) shows that its regexec() doesn't have REG_STARTEND. 1. https://github.com/chef/omnibus-software/commit/e247e36761#diff-3df898345d670979b74acc0bf71d8c47 2. https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.basetrf2/regexec.htm Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- config.mak.uname | 1 + 1 file changed, 1 insertion(+) diff --git a/config.mak.uname b/config.mak.uname index 399fe19271..192629f143 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -237,6 +237,7 @@ ifeq ($(uname_S),AIX) NO_MKDTEMP = YesPlease NO_STRLCPY = YesPlease NO_NSEC = YesPlease + NO_REGEX = NeedsStartEnd FREAD_READS_DIRECTORIES = UnfortunatelyYes INTERNAL_QSORT = UnfortunatelyYes NEEDS_LIBICONV = YesPlease From 12cd0bf9b0291bb9a688c197a413d19f3c54710e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:21 -0700 Subject: [PATCH 086/566] dir: stop using the index compatibility macros In order to make it clearer where the_index is being referenced, stop using the index compatibility macros in dir.c. This is to make it easier to identify the functions which need to be convert to taking in a 'struct index_state' as a parameter. The end goal would be to eliminate the need to reference global index state in dir.c. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/dir.c b/dir.c index f451bfa48c..97eadd45e6 100644 --- a/dir.c +++ b/dir.c @@ -7,6 +7,7 @@ * Copyright (C) Linus Torvalds, 2005-2006 * Junio Hamano, 2005-2006 */ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "dir.h" #include "attr.h" @@ -596,12 +597,12 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size, void *data; len = strlen(path); - pos = cache_name_pos(path, len); + pos = index_name_pos(&the_index, path, len); if (pos < 0) return NULL; - if (!ce_skip_worktree(active_cache[pos])) + if (!ce_skip_worktree(the_index.cache[pos])) return NULL; - data = read_sha1_file(active_cache[pos]->oid.hash, &type, &sz); + data = read_sha1_file(the_index.cache[pos]->oid.hash, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -609,7 +610,7 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size, *size = xsize_t(sz); if (sha1_stat) { memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat)); - hashcpy(sha1_stat->sha1, active_cache[pos]->oid.hash); + hashcpy(sha1_stat->sha1, the_index.cache[pos]->oid.hash); } return data; } @@ -785,12 +786,12 @@ static int add_excludes(const char *fname, const char *base, int baselen, !match_stat_data_racy(&the_index, &sha1_stat->stat, &st)) ; /* no content change, ss->sha1 still good */ else if (check_index && - (pos = cache_name_pos(fname, strlen(fname))) >= 0 && - !ce_stage(active_cache[pos]) && - ce_uptodate(active_cache[pos]) && + (pos = index_name_pos(&the_index, fname, strlen(fname))) >= 0 && + !ce_stage(the_index.cache[pos]) && + ce_uptodate(the_index.cache[pos]) && !would_convert_to_git(fname)) hashcpy(sha1_stat->sha1, - active_cache[pos]->oid.hash); + the_index.cache[pos]->oid.hash); else hash_sha1_file(buf, size, "blob", sha1_stat->sha1); fill_stat_data(&sha1_stat->stat, &st); @@ -1235,7 +1236,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) { - if (cache_file_exists(pathname, len, ignore_case)) + if (index_file_exists(&the_index, pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); @@ -1244,7 +1245,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len) { - if (!cache_name_is_other(pathname, len)) + if (!index_name_is_other(&the_index, pathname, len)) return NULL; ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc); @@ -1266,10 +1267,10 @@ static enum exist_status directory_exists_in_index_icase(const char *dirname, in { struct cache_entry *ce; - if (cache_dir_exists(dirname, len)) + if (index_dir_exists(&the_index, dirname, len)) return index_directory; - ce = cache_file_exists(dirname, len, ignore_case); + ce = index_file_exists(&the_index, dirname, len, ignore_case); if (ce && S_ISGITLINK(ce->ce_mode)) return index_gitdir; @@ -1290,11 +1291,11 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len) if (ignore_case) return directory_exists_in_index_icase(dirname, len); - pos = cache_name_pos(dirname, len); + pos = index_name_pos(&the_index, dirname, len); if (pos < 0) pos = -pos-1; - while (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos++]; + while (pos < the_index.cache_nr) { + const struct cache_entry *ce = the_index.cache[pos++]; unsigned char endchar; if (strncmp(ce->name, dirname, len)) @@ -1460,7 +1461,7 @@ static int get_index_dtype(const char *path, int len) int pos; const struct cache_entry *ce; - ce = cache_file_exists(path, len, 0); + ce = index_file_exists(&the_index, path, len, 0); if (ce) { if (!ce_uptodate(ce)) return DT_UNKNOWN; @@ -1474,12 +1475,12 @@ static int get_index_dtype(const char *path, int len) } /* Try to look it up as a directory */ - pos = cache_name_pos(path, len); + pos = index_name_pos(&the_index, path, len); if (pos >= 0) return DT_UNKNOWN; pos = -pos-1; - while (pos < active_nr) { - ce = active_cache[pos++]; + while (pos < the_index.cache_nr) { + ce = the_index.cache[pos++]; if (strncmp(ce->name, path, len)) break; if (ce->name[len] > '/') @@ -1522,7 +1523,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, int dtype, struct dirent *de) { int exclude; - int has_path_in_index = !!cache_file_exists(path->buf, path->len, ignore_case); + int has_path_in_index = !!index_file_exists(&the_index, path->buf, path->len, ignore_case); if (dtype == DT_UNKNOWN) dtype = get_dtype(de, path->buf, path->len); From 6f52b741a7e58dbabee196c22acd012107488ccb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:22 -0700 Subject: [PATCH 087/566] dir: convert read_skip_worktree_file_from_index to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dir.c b/dir.c index 97eadd45e6..44307b9630 100644 --- a/dir.c +++ b/dir.c @@ -588,7 +588,8 @@ void add_exclude(const char *string, const char *base, x->el = el; } -static void *read_skip_worktree_file_from_index(const char *path, size_t *size, +static void *read_skip_worktree_file_from_index(const struct index_state *istate, + const char *path, size_t *size, struct sha1_stat *sha1_stat) { int pos, len; @@ -597,12 +598,12 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size, void *data; len = strlen(path); - pos = index_name_pos(&the_index, path, len); + pos = index_name_pos(istate, path, len); if (pos < 0) return NULL; - if (!ce_skip_worktree(the_index.cache[pos])) + if (!ce_skip_worktree(istate->cache[pos])) return NULL; - data = read_sha1_file(the_index.cache[pos]->oid.hash, &type, &sz); + data = read_sha1_file(istate->cache[pos]->oid.hash, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -610,7 +611,7 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size, *size = xsize_t(sz); if (sha1_stat) { memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat)); - hashcpy(sha1_stat->sha1, the_index.cache[pos]->oid.hash); + hashcpy(sha1_stat->sha1, istate->cache[pos]->oid.hash); } return data; } @@ -751,7 +752,7 @@ static int add_excludes(const char *fname, const char *base, int baselen, if (0 <= fd) close(fd); if (!check_index || - (buf = read_skip_worktree_file_from_index(fname, &size, sha1_stat)) == NULL) + (buf = read_skip_worktree_file_from_index(&the_index, fname, &size, sha1_stat)) == NULL) return -1; if (size == 0) { free(buf); From ae520e36758f45f1b9972f2b91db91a7f1c5559e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:23 -0700 Subject: [PATCH 088/566] dir: convert directory_exists_in_index to take index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dir.c b/dir.c index 44307b9630..5c635198d5 100644 --- a/dir.c +++ b/dir.c @@ -1264,14 +1264,15 @@ enum exist_status { * the directory name; instead, use the case insensitive * directory hash. */ -static enum exist_status directory_exists_in_index_icase(const char *dirname, int len) +static enum exist_status directory_exists_in_index_icase(struct index_state *istate, + const char *dirname, int len) { struct cache_entry *ce; - if (index_dir_exists(&the_index, dirname, len)) + if (index_dir_exists(istate, dirname, len)) return index_directory; - ce = index_file_exists(&the_index, dirname, len, ignore_case); + ce = index_file_exists(istate, dirname, len, ignore_case); if (ce && S_ISGITLINK(ce->ce_mode)) return index_gitdir; @@ -1285,18 +1286,19 @@ static enum exist_status directory_exists_in_index_icase(const char *dirname, in * the files it contains) will sort with the '/' at the * end. */ -static enum exist_status directory_exists_in_index(const char *dirname, int len) +static enum exist_status directory_exists_in_index(struct index_state *istate, + const char *dirname, int len) { int pos; if (ignore_case) - return directory_exists_in_index_icase(dirname, len); + return directory_exists_in_index_icase(istate, dirname, len); - pos = index_name_pos(&the_index, dirname, len); + pos = index_name_pos(istate, dirname, len); if (pos < 0) pos = -pos-1; - while (pos < the_index.cache_nr) { - const struct cache_entry *ce = the_index.cache[pos++]; + while (pos < istate->cache_nr) { + const struct cache_entry *ce = istate->cache[pos++]; unsigned char endchar; if (strncmp(ce->name, dirname, len)) @@ -1351,7 +1353,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir, const struct pathspec *pathspec) { /* The "len-1" is to strip the final '/' */ - switch (directory_exists_in_index(dirname, len-1)) { + switch (directory_exists_in_index(&the_index, dirname, len-1)) { case index_directory: return path_recurse; @@ -1554,7 +1556,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, if ((dir->flags & DIR_COLLECT_KILLED_ONLY) && (dtype == DT_DIR) && !has_path_in_index && - (directory_exists_in_index(path->buf, path->len) == index_nonexistent)) + (directory_exists_in_index(&the_index, path->buf, path->len) == index_nonexistent)) return path_none; exclude = is_excluded(dir, path->buf, &dtype); From 98f2a687b9a971fec86254537c37df00f18a6e80 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:24 -0700 Subject: [PATCH 089/566] dir: convert get_dtype to take index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/dir.c b/dir.c index 5c635198d5..4515f00836 100644 --- a/dir.c +++ b/dir.c @@ -48,7 +48,8 @@ struct cached_dir { static enum path_treatment read_directory_recursive(struct dir_struct *dir, const char *path, int len, struct untracked_cache_dir *untracked, int check_only, const struct pathspec *pathspec); -static int get_dtype(struct dirent *de, const char *path, int len); +static int get_dtype(struct dirent *de, struct index_state *istate, + const char *path, int len); int fspathcmp(const char *a, const char *b) { @@ -975,7 +976,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, if (x->flags & EXC_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) - *dtype = get_dtype(NULL, pathname, pathlen); + *dtype = get_dtype(NULL, &the_index, pathname, pathlen); if (*dtype != DT_DIR) continue; } @@ -1459,12 +1460,13 @@ static int exclude_matches_pathspec(const char *path, int pathlen, return 0; } -static int get_index_dtype(const char *path, int len) +static int get_index_dtype(struct index_state *istate, + const char *path, int len) { int pos; const struct cache_entry *ce; - ce = index_file_exists(&the_index, path, len, 0); + ce = index_file_exists(istate, path, len, 0); if (ce) { if (!ce_uptodate(ce)) return DT_UNKNOWN; @@ -1478,12 +1480,12 @@ static int get_index_dtype(const char *path, int len) } /* Try to look it up as a directory */ - pos = index_name_pos(&the_index, path, len); + pos = index_name_pos(istate, path, len); if (pos >= 0) return DT_UNKNOWN; pos = -pos-1; - while (pos < the_index.cache_nr) { - ce = the_index.cache[pos++]; + while (pos < istate->cache_nr) { + ce = istate->cache[pos++]; if (strncmp(ce->name, path, len)) break; if (ce->name[len] > '/') @@ -1497,14 +1499,15 @@ static int get_index_dtype(const char *path, int len) return DT_UNKNOWN; } -static int get_dtype(struct dirent *de, const char *path, int len) +static int get_dtype(struct dirent *de, struct index_state *istate, + const char *path, int len) { int dtype = de ? DTYPE(de) : DT_UNKNOWN; struct stat st; if (dtype != DT_UNKNOWN) return dtype; - dtype = get_index_dtype(path, len); + dtype = get_index_dtype(istate, path, len); if (dtype != DT_UNKNOWN) return dtype; if (lstat(path, &st)) @@ -1529,7 +1532,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, int has_path_in_index = !!index_file_exists(&the_index, path->buf, path->len, ignore_case); if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, path->buf, path->len); + dtype = get_dtype(de, &the_index, path->buf, path->len); /* Always exclude indexed files */ if (dtype != DT_DIR && has_path_in_index) From 9e58becab9d2512bad0fdf150fedbea84d6b9b03 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:25 -0700 Subject: [PATCH 090/566] dir: convert dir_add* to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/add.c | 3 ++- dir.c | 18 +++++++++++------- dir.h | 4 +++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index 9f53f020d0..bf5e676e46 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -437,7 +437,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, path, &dtype)) - dir_add_ignored(&dir, path, pathspec.items[i].len); + dir_add_ignored(&dir, &the_index, + path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), pathspec.items[i].original); diff --git a/dir.c b/dir.c index 4515f00836..a508e8076d 100644 --- a/dir.c +++ b/dir.c @@ -1236,18 +1236,22 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) return ent; } -static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) +static struct dir_entry *dir_add_name(struct dir_struct *dir, + struct index_state *istate, + const char *pathname, int len) { - if (index_file_exists(&the_index, pathname, len, ignore_case)) + if (index_file_exists(istate, pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); return dir->entries[dir->nr++] = dir_entry_new(pathname, len); } -struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len) +struct dir_entry *dir_add_ignored(struct dir_struct *dir, + struct index_state *istate, + const char *pathname, int len) { - if (!index_name_is_other(&the_index, pathname, len)) + if (!index_name_is_other(istate, pathname, len)) return NULL; ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc); @@ -1819,18 +1823,18 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, switch (state) { case path_excluded: if (dir->flags & DIR_SHOW_IGNORED) - dir_add_name(dir, path.buf, path.len); + dir_add_name(dir, &the_index, path.buf, path.len); else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || ((dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path.buf, path.len, pathspec))) - dir_add_ignored(dir, path.buf, path.len); + dir_add_ignored(dir, &the_index, path.buf, path.len); break; case path_untracked: if (dir->flags & DIR_SHOW_IGNORED) break; - dir_add_name(dir, path.buf, path.len); + dir_add_name(dir, &the_index, path.buf, path.len); if (cdir.fdir) add_untracked(untracked, path.buf + baselen); break; diff --git a/dir.h b/dir.h index bf23a470af..a9f8099833 100644 --- a/dir.h +++ b/dir.h @@ -219,7 +219,9 @@ extern int read_directory(struct dir_struct *, const char *path, int len, const extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, struct exclude_list *el); -struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len); +struct dir_entry *dir_add_ignored(struct dir_struct *dir, + struct index_state *istate, + const char *pathname, int len); /* * these implement the matching logic for dir.c:excluded_from_list and From 2b70e88d367f3e697023c2b7c3054e8a26e6faaa Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:26 -0700 Subject: [PATCH 091/566] dir: convert last_exclude_matching_from_list to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dir.c b/dir.c index a508e8076d..077b756c20 100644 --- a/dir.c +++ b/dir.c @@ -961,7 +961,8 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, - struct exclude_list *el) + struct exclude_list *el, + struct index_state *istate) { struct exclude *exc = NULL; /* undecided */ int i; @@ -976,7 +977,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, if (x->flags & EXC_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) - *dtype = get_dtype(NULL, &the_index, pathname, pathlen); + *dtype = get_dtype(NULL, istate, pathname, pathlen); if (*dtype != DT_DIR) continue; } @@ -1012,13 +1013,14 @@ int is_excluded_from_list(const char *pathname, struct exclude_list *el) { struct exclude *exclude; - exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el); + exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el, &the_index); if (exclude) return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return -1; /* undecided */ } static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, + struct index_state *istate, const char *pathname, int pathlen, const char *basename, int *dtype_p) { @@ -1030,7 +1032,7 @@ static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, for (j = group->nr - 1; j >= 0; j--) { exclude = last_exclude_matching_from_list( pathname, pathlen, basename, dtype_p, - &group->el[j]); + &group->el[j], istate); if (exclude) return exclude; } @@ -1121,6 +1123,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) int dt = DT_DIR; dir->basebuf.buf[stk->baselen - 1] = 0; dir->exclude = last_exclude_matching_from_lists(dir, + &the_index, dir->basebuf.buf, stk->baselen - 1, dir->basebuf.buf + current, &dt); dir->basebuf.buf[stk->baselen - 1] = '/'; @@ -1209,7 +1212,7 @@ struct exclude *last_exclude_matching(struct dir_struct *dir, if (dir->exclude) return dir->exclude; - return last_exclude_matching_from_lists(dir, pathname, pathlen, + return last_exclude_matching_from_lists(dir, &the_index, pathname, pathlen, basename, dtype_p); } From fba92be8f73818c4e79dc0b93ba26733d0ba5efe Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:27 -0700 Subject: [PATCH 092/566] dir: convert is_excluded_from_list to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 5 +++-- dir.h | 6 ++++-- unpack-trees.c | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dir.c b/dir.c index 077b756c20..497a2db85c 100644 --- a/dir.c +++ b/dir.c @@ -1010,10 +1010,11 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, */ int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, - struct exclude_list *el) + struct exclude_list *el, struct index_state *istate) { struct exclude *exclude; - exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el, &the_index); + exclude = last_exclude_matching_from_list(pathname, pathlen, basename, + dtype, el, istate); if (exclude) return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return -1; /* undecided */ diff --git a/dir.h b/dir.h index a9f8099833..64254c7e74 100644 --- a/dir.h +++ b/dir.h @@ -217,8 +217,10 @@ extern int within_depth(const char *name, int namelen, int depth, int max_depth) extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec); extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec); -extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, - int *dtype, struct exclude_list *el); +extern int is_excluded_from_list(const char *pathname, int pathlen, + const char *basename, int *dtype, + struct exclude_list *el, + struct index_state *istate); struct dir_entry *dir_add_ignored(struct dir_struct *dir, struct index_state *istate, const char *pathname, int len); diff --git a/unpack-trees.c b/unpack-trees.c index aa15111fef..df9f975d01 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1068,7 +1068,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, struct cache_entry **cache_end; int dtype = DT_DIR; int ret = is_excluded_from_list(prefix->buf, prefix->len, - basename, &dtype, el); + basename, &dtype, el, &the_index); int rc; strbuf_addch(prefix, '/'); @@ -1171,7 +1171,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr, /* Non-directory */ dtype = ce_to_dtype(ce); ret = is_excluded_from_list(ce->name, ce_namelen(ce), - name, &dtype, el); + name, &dtype, el, &the_index); if (ret < 0) ret = defval; if (ret > 0) From 473e39307d8c9db93bd08d82898e7fff7002c8cb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:28 -0700 Subject: [PATCH 093/566] dir: convert add_excludes to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 29 +++++++++++++++-------------- dir.h | 2 +- unpack-trees.c | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/dir.c b/dir.c index 497a2db85c..b86d02ff94 100644 --- a/dir.c +++ b/dir.c @@ -730,7 +730,7 @@ static void invalidate_directory(struct untracked_cache *uc, /* * Given a file with name "fname", read it (either from disk, or from - * the index if "check_index" is non-zero), parse it and store the + * an index if 'istate' is non-null), parse it and store the * exclude rules in "el". * * If "ss" is not NULL, compute SHA-1 of the exclude file and fill @@ -738,7 +738,8 @@ static void invalidate_directory(struct untracked_cache *uc, * ss_valid is non-zero, "ss" must contain good value as input. */ static int add_excludes(const char *fname, const char *base, int baselen, - struct exclude_list *el, int check_index, + struct exclude_list *el, + struct index_state *istate, struct sha1_stat *sha1_stat) { struct stat st; @@ -752,8 +753,8 @@ static int add_excludes(const char *fname, const char *base, int baselen, warn_on_inaccessible(fname); if (0 <= fd) close(fd); - if (!check_index || - (buf = read_skip_worktree_file_from_index(&the_index, fname, &size, sha1_stat)) == NULL) + if (!istate || + (buf = read_skip_worktree_file_from_index(istate, fname, &size, sha1_stat)) == NULL) return -1; if (size == 0) { free(buf); @@ -785,15 +786,15 @@ static int add_excludes(const char *fname, const char *base, int baselen, if (sha1_stat) { int pos; if (sha1_stat->valid && - !match_stat_data_racy(&the_index, &sha1_stat->stat, &st)) + !match_stat_data_racy(istate, &sha1_stat->stat, &st)) ; /* no content change, ss->sha1 still good */ - else if (check_index && - (pos = index_name_pos(&the_index, fname, strlen(fname))) >= 0 && - !ce_stage(the_index.cache[pos]) && - ce_uptodate(the_index.cache[pos]) && + else if (istate && + (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 && + !ce_stage(istate->cache[pos]) && + ce_uptodate(istate->cache[pos]) && !would_convert_to_git(fname)) hashcpy(sha1_stat->sha1, - the_index.cache[pos]->oid.hash); + istate->cache[pos]->oid.hash); else hash_sha1_file(buf, size, "blob", sha1_stat->sha1); fill_stat_data(&sha1_stat->stat, &st); @@ -824,9 +825,9 @@ static int add_excludes(const char *fname, const char *base, int baselen, int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, struct exclude_list *el, - int check_index) + struct index_state *istate) { - return add_excludes(fname, base, baselen, el, check_index, NULL); + return add_excludes(fname, base, baselen, el, istate, NULL); } struct exclude_list *add_exclude_list(struct dir_struct *dir, @@ -858,7 +859,7 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname, if (!dir->untracked) dir->unmanaged_exclude_files++; el = add_exclude_list(dir, EXC_FILE, fname); - if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0) + if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0) die("cannot use %s as an exclude file", fname); } @@ -1166,7 +1167,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) strbuf_addbuf(&sb, &dir->basebuf); strbuf_addstr(&sb, dir->exclude_per_dir); el->src = strbuf_detach(&sb, NULL); - add_excludes(el->src, el->src, stk->baselen, el, 1, + add_excludes(el->src, el->src, stk->baselen, el, &the_index, untracked ? &sha1_stat : NULL); } /* diff --git a/dir.h b/dir.h index 64254c7e74..1bcda0d234 100644 --- a/dir.h +++ b/dir.h @@ -243,7 +243,7 @@ extern int is_excluded(struct dir_struct *dir, const char *name, int *dtype); extern struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type, const char *src); extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, - struct exclude_list *el, int check_index); + struct exclude_list *el, struct index_state *istate); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen); extern void add_exclude(const char *string, const char *base, diff --git a/unpack-trees.c b/unpack-trees.c index df9f975d01..f278cd23f0 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1251,7 +1251,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->skip_sparse_checkout = 1; if (!o->skip_sparse_checkout) { char *sparse = git_pathdup("info/sparse-checkout"); - if (add_excludes_from_file_to_list(sparse, "", 0, &el, 0) < 0) + if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0) o->skip_sparse_checkout = 1; else o->el = ⪙ From e799ed408eb59ca631c4908ba4236cd146ff6bd2 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:29 -0700 Subject: [PATCH 094/566] dir: convert prep_exclude to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dir.c b/dir.c index b86d02ff94..50b5e720e9 100644 --- a/dir.c +++ b/dir.c @@ -1046,7 +1046,9 @@ static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, * Loads the per-directory exclude list for the substring of base * which has a char length of baselen. */ -static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) +static void prep_exclude(struct dir_struct *dir, + struct index_state *istate, + const char *base, int baselen) { struct exclude_list_group *group; struct exclude_list *el; @@ -1125,7 +1127,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) int dt = DT_DIR; dir->basebuf.buf[stk->baselen - 1] = 0; dir->exclude = last_exclude_matching_from_lists(dir, - &the_index, + istate, dir->basebuf.buf, stk->baselen - 1, dir->basebuf.buf + current, &dt); dir->basebuf.buf[stk->baselen - 1] = '/'; @@ -1167,7 +1169,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) strbuf_addbuf(&sb, &dir->basebuf); strbuf_addstr(&sb, dir->exclude_per_dir); el->src = strbuf_detach(&sb, NULL); - add_excludes(el->src, el->src, stk->baselen, el, &the_index, + add_excludes(el->src, el->src, stk->baselen, el, istate, untracked ? &sha1_stat : NULL); } /* @@ -1209,7 +1211,7 @@ struct exclude *last_exclude_matching(struct dir_struct *dir, const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; - prep_exclude(dir, pathname, basename-pathname); + prep_exclude(dir, &the_index, pathname, basename-pathname); if (dir->exclude) return dir->exclude; @@ -1695,10 +1697,10 @@ static int valid_cached_dir(struct dir_struct *dir, */ if (path->len && path->buf[path->len - 1] != '/') { strbuf_addch(path, '/'); - prep_exclude(dir, path->buf, path->len); + prep_exclude(dir, &the_index, path->buf, path->len); strbuf_setlen(path, path->len - 1); } else - prep_exclude(dir, path->buf, path->len); + prep_exclude(dir, &the_index, path->buf, path->len); /* hopefully prep_exclude() haven't invalidated this entry... */ return untracked->valid; From a0bba65b107651946431bee4c71d05d812988d9d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:30 -0700 Subject: [PATCH 095/566] dir: convert is_excluded to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/add.c | 2 +- builtin/check-ignore.c | 3 ++- builtin/clean.c | 2 +- builtin/ls-files.c | 2 +- dir.c | 16 +++++++++------- dir.h | 5 ++++- unpack-trees.c | 2 +- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index bf5e676e46..0b52aeced3 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -436,7 +436,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, path, &dtype)) + if (is_excluded(&dir, &the_index, path, &dtype)) dir_add_ignored(&dir, &the_index, path, pathspec.items[i].len); } else diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 1d73d3ca3d..d2293b22e1 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -101,7 +101,8 @@ static int check_ignore(struct dir_struct *dir, full_path = pathspec.items[i].match; exclude = NULL; if (!seen[i]) { - exclude = last_exclude_matching(dir, full_path, &dtype); + exclude = last_exclude_matching(dir, &the_index, + full_path, &dtype); } if (!quiet && (exclude || show_non_matching)) output_exclude(pathspec.items[i].original, exclude); diff --git a/builtin/clean.c b/builtin/clean.c index d861f836a2..39866afab4 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -683,7 +683,7 @@ static int filter_by_patterns_cmd(void) for_each_string_list_item(item, &del_list) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, item->string, &dtype)) { + if (is_excluded(&dir, &the_index, item->string, &dtype)) { *item->string = '\0'; changed++; } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a6c70dbe9e..7a8c5681b6 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -322,7 +322,7 @@ static void show_ru_info(void) static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce) { int dtype = ce_to_dtype(ce); - return is_excluded(dir, ce->name, &dtype); + return is_excluded(dir, &the_index, ce->name, &dtype); } static void show_files(struct dir_struct *dir) diff --git a/dir.c b/dir.c index 50b5e720e9..a15da672c3 100644 --- a/dir.c +++ b/dir.c @@ -1204,19 +1204,20 @@ static void prep_exclude(struct dir_struct *dir, * undecided. */ struct exclude *last_exclude_matching(struct dir_struct *dir, - const char *pathname, - int *dtype_p) + struct index_state *istate, + const char *pathname, + int *dtype_p) { int pathlen = strlen(pathname); const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; - prep_exclude(dir, &the_index, pathname, basename-pathname); + prep_exclude(dir, istate, pathname, basename-pathname); if (dir->exclude) return dir->exclude; - return last_exclude_matching_from_lists(dir, &the_index, pathname, pathlen, + return last_exclude_matching_from_lists(dir, istate, pathname, pathlen, basename, dtype_p); } @@ -1225,10 +1226,11 @@ struct exclude *last_exclude_matching(struct dir_struct *dir, * scans all exclude lists to determine whether pathname is excluded. * Returns 1 if true, otherwise 0. */ -int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) +int is_excluded(struct dir_struct *dir, struct index_state *istate, + const char *pathname, int *dtype_p) { struct exclude *exclude = - last_exclude_matching(dir, pathname, dtype_p); + last_exclude_matching(dir, istate, pathname, dtype_p); if (exclude) return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return 0; @@ -1573,7 +1575,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, (directory_exists_in_index(&the_index, path->buf, path->len) == index_nonexistent)) return path_none; - exclude = is_excluded(dir, path->buf, &dtype); + exclude = is_excluded(dir, &the_index, path->buf, &dtype); /* * Excluded? If we don't explicitly want to show diff --git a/dir.h b/dir.h index 1bcda0d234..b745f1e5d7 100644 --- a/dir.h +++ b/dir.h @@ -236,9 +236,12 @@ extern int match_pathname(const char *, int, const char *, int, int, unsigned); extern struct exclude *last_exclude_matching(struct dir_struct *dir, + struct index_state *istate, const char *name, int *dtype); -extern int is_excluded(struct dir_struct *dir, const char *name, int *dtype); +extern int is_excluded(struct dir_struct *dir, + struct index_state *istate, + const char *name, int *dtype); extern struct exclude_list *add_exclude_list(struct dir_struct *dir, int group_type, const char *src); diff --git a/unpack-trees.c b/unpack-trees.c index f278cd23f0..d5b401450f 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1634,7 +1634,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype, return 0; if (o->dir && - is_excluded(o->dir, name, &dtype)) + is_excluded(o->dir, &the_index, name, &dtype)) /* * ce->name is explicitly excluded, so it is Ok to * overwrite it. From 207a06cea39579586952a9a50bd25352e5c952f1 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:31 -0700 Subject: [PATCH 096/566] dir: convert open_cached_dir to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dir.c b/dir.c index a15da672c3..f08d30ee45 100644 --- a/dir.c +++ b/dir.c @@ -1664,6 +1664,7 @@ static void add_untracked(struct untracked_cache_dir *dir, const char *name) static int valid_cached_dir(struct dir_struct *dir, struct untracked_cache_dir *untracked, + struct index_state *istate, struct strbuf *path, int check_only) { @@ -1678,7 +1679,7 @@ static int valid_cached_dir(struct dir_struct *dir, return 0; } if (!untracked->valid || - match_stat_data_racy(&the_index, &untracked->stat_data, &st)) { + match_stat_data_racy(istate, &untracked->stat_data, &st)) { if (untracked->valid) invalidate_directory(dir->untracked, untracked); fill_stat_data(&untracked->stat_data, &st); @@ -1699,10 +1700,10 @@ static int valid_cached_dir(struct dir_struct *dir, */ if (path->len && path->buf[path->len - 1] != '/') { strbuf_addch(path, '/'); - prep_exclude(dir, &the_index, path->buf, path->len); + prep_exclude(dir, istate, path->buf, path->len); strbuf_setlen(path, path->len - 1); } else - prep_exclude(dir, &the_index, path->buf, path->len); + prep_exclude(dir, istate, path->buf, path->len); /* hopefully prep_exclude() haven't invalidated this entry... */ return untracked->valid; @@ -1711,12 +1712,13 @@ static int valid_cached_dir(struct dir_struct *dir, static int open_cached_dir(struct cached_dir *cdir, struct dir_struct *dir, struct untracked_cache_dir *untracked, + struct index_state *istate, struct strbuf *path, int check_only) { memset(cdir, 0, sizeof(*cdir)); cdir->untracked = untracked; - if (valid_cached_dir(dir, untracked, path, check_only)) + if (valid_cached_dir(dir, untracked, istate, path, check_only)) return 0; cdir->fdir = opendir(path->len ? path->buf : "."); if (dir->untracked) @@ -1789,7 +1791,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, strbuf_add(&path, base, baselen); - if (open_cached_dir(&cdir, dir, untracked, &path, check_only)) + if (open_cached_dir(&cdir, dir, untracked, &the_index, &path, check_only)) goto out; if (untracked) From 0ef8e169aa40ac98dce71f61074bcd729cd9ba54 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:32 -0700 Subject: [PATCH 097/566] dir: convert read_directory_recursive to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/dir.c b/dir.c index f08d30ee45..3318ebbcb4 100644 --- a/dir.c +++ b/dir.c @@ -46,7 +46,8 @@ struct cached_dir { }; static enum path_treatment read_directory_recursive(struct dir_struct *dir, - const char *path, int len, struct untracked_cache_dir *untracked, + struct index_state *istate, const char *path, int len, + struct untracked_cache_dir *untracked, int check_only, const struct pathspec *pathspec); static int get_dtype(struct dirent *de, struct index_state *istate, const char *path, int len); @@ -1362,12 +1363,13 @@ static enum exist_status directory_exists_in_index(struct index_state *istate, * (c) otherwise, we recurse into it. */ static enum path_treatment treat_directory(struct dir_struct *dir, + struct index_state *istate, struct untracked_cache_dir *untracked, const char *dirname, int len, int baselen, int exclude, const struct pathspec *pathspec) { /* The "len-1" is to strip the final '/' */ - switch (directory_exists_in_index(&the_index, dirname, len-1)) { + switch (directory_exists_in_index(istate, dirname, len-1)) { case index_directory: return path_recurse; @@ -1392,7 +1394,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir, untracked = lookup_untracked(dir->untracked, untracked, dirname + baselen, len - baselen); - return read_directory_recursive(dir, dirname, len, + return read_directory_recursive(dir, istate, dirname, len, untracked, 1, pathspec); } @@ -1536,16 +1538,17 @@ static int get_dtype(struct dirent *de, struct index_state *istate, static enum path_treatment treat_one_path(struct dir_struct *dir, struct untracked_cache_dir *untracked, + struct index_state *istate, struct strbuf *path, int baselen, const struct pathspec *pathspec, int dtype, struct dirent *de) { int exclude; - int has_path_in_index = !!index_file_exists(&the_index, path->buf, path->len, ignore_case); + int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case); if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, &the_index, path->buf, path->len); + dtype = get_dtype(de, istate, path->buf, path->len); /* Always exclude indexed files */ if (dtype != DT_DIR && has_path_in_index) @@ -1572,10 +1575,10 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, if ((dir->flags & DIR_COLLECT_KILLED_ONLY) && (dtype == DT_DIR) && !has_path_in_index && - (directory_exists_in_index(&the_index, path->buf, path->len) == index_nonexistent)) + (directory_exists_in_index(istate, path->buf, path->len) == index_nonexistent)) return path_none; - exclude = is_excluded(dir, &the_index, path->buf, &dtype); + exclude = is_excluded(dir, istate, path->buf, &dtype); /* * Excluded? If we don't explicitly want to show @@ -1589,7 +1592,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, return path_none; case DT_DIR: strbuf_addch(path, '/'); - return treat_directory(dir, untracked, path->buf, path->len, + return treat_directory(dir, istate, untracked, path->buf, path->len, baselen, exclude, pathspec); case DT_REG: case DT_LNK: @@ -1600,6 +1603,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, static enum path_treatment treat_path_fast(struct dir_struct *dir, struct untracked_cache_dir *untracked, struct cached_dir *cdir, + struct index_state *istate, struct strbuf *path, int baselen, const struct pathspec *pathspec) @@ -1618,7 +1622,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir, * to its bottom. Verify again the same set of directories * with check_only set. */ - return read_directory_recursive(dir, path->buf, path->len, + return read_directory_recursive(dir, istate, path->buf, path->len, cdir->ucd, 1, pathspec); /* * We get path_recurse in the first run when @@ -1632,6 +1636,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir, static enum path_treatment treat_path(struct dir_struct *dir, struct untracked_cache_dir *untracked, struct cached_dir *cdir, + struct index_state *istate, struct strbuf *path, int baselen, const struct pathspec *pathspec) @@ -1640,7 +1645,7 @@ static enum path_treatment treat_path(struct dir_struct *dir, struct dirent *de = cdir->de; if (!de) - return treat_path_fast(dir, untracked, cdir, path, + return treat_path_fast(dir, untracked, cdir, istate, path, baselen, pathspec); if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git")) return path_none; @@ -1650,7 +1655,7 @@ static enum path_treatment treat_path(struct dir_struct *dir, return path_none; dtype = DTYPE(de); - return treat_one_path(dir, untracked, path, baselen, pathspec, dtype, de); + return treat_one_path(dir, untracked, istate, path, baselen, pathspec, dtype, de); } static void add_untracked(struct untracked_cache_dir *dir, const char *name) @@ -1781,9 +1786,9 @@ static void close_cached_dir(struct cached_dir *cdir) * Returns the most significant path_treatment value encountered in the scan. */ static enum path_treatment read_directory_recursive(struct dir_struct *dir, - const char *base, int baselen, - struct untracked_cache_dir *untracked, int check_only, - const struct pathspec *pathspec) + struct index_state *istate, const char *base, int baselen, + struct untracked_cache_dir *untracked, int check_only, + const struct pathspec *pathspec) { struct cached_dir cdir; enum path_treatment state, subdir_state, dir_state = path_none; @@ -1791,7 +1796,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, strbuf_add(&path, base, baselen); - if (open_cached_dir(&cdir, dir, untracked, &the_index, &path, check_only)) + if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only)) goto out; if (untracked) @@ -1799,7 +1804,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, while (!read_cached_dir(&cdir)) { /* check how the file or directory should be treated */ - state = treat_path(dir, untracked, &cdir, &path, + state = treat_path(dir, untracked, &cdir, istate, &path, baselen, pathspec); if (state > dir_state) @@ -1812,7 +1817,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, path.buf + baselen, path.len - baselen); subdir_state = - read_directory_recursive(dir, path.buf, + read_directory_recursive(dir, istate, path.buf, path.len, ud, check_only, pathspec); if (subdir_state > dir_state) @@ -1834,18 +1839,18 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, switch (state) { case path_excluded: if (dir->flags & DIR_SHOW_IGNORED) - dir_add_name(dir, &the_index, path.buf, path.len); + dir_add_name(dir, istate, path.buf, path.len); else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || ((dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path.buf, path.len, pathspec))) - dir_add_ignored(dir, &the_index, path.buf, path.len); + dir_add_ignored(dir, istate, path.buf, path.len); break; case path_untracked: if (dir->flags & DIR_SHOW_IGNORED) break; - dir_add_name(dir, &the_index, path.buf, path.len); + dir_add_name(dir, istate, path.buf, path.len); if (cdir.fdir) add_untracked(untracked, path.buf + baselen); break; @@ -1870,6 +1875,7 @@ static int cmp_name(const void *p1, const void *p2) } static int treat_leading_path(struct dir_struct *dir, + struct index_state *istate, const char *path, int len, const struct pathspec *pathspec) { @@ -1897,7 +1903,7 @@ static int treat_leading_path(struct dir_struct *dir, break; if (simplify_away(sb.buf, sb.len, pathspec)) break; - if (treat_one_path(dir, NULL, &sb, baselen, pathspec, + if (treat_one_path(dir, NULL, istate, &sb, baselen, pathspec, DT_DIR, NULL) == path_none) break; /* do not recurse into it */ if (len <= baselen) { @@ -2080,8 +2086,8 @@ int read_directory(struct dir_struct *dir, const char *path, * e.g. prep_exclude() */ dir->untracked = NULL; - if (!len || treat_leading_path(dir, path, len, pathspec)) - read_directory_recursive(dir, path, len, untracked, 0, pathspec); + if (!len || treat_leading_path(dir, &the_index, path, len, pathspec)) + read_directory_recursive(dir, &the_index, path, len, untracked, 0, pathspec); QSORT(dir->entries, dir->nr, cmp_name); QSORT(dir->ignored, dir->ignored_nr, cmp_name); if (dir->untracked) { From 2c1eb104543265c2d26cf36303b35e426dcacf68 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:33 -0700 Subject: [PATCH 098/566] dir: convert read_directory to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- dir.c | 16 ++++++++-------- dir.h | 4 +++- unpack-trees.c | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dir.c b/dir.c index 3318ebbcb4..4eb8cb6a21 100644 --- a/dir.c +++ b/dir.c @@ -190,7 +190,7 @@ int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) prefix = prefix_len ? pathspec->items[0].match : ""; /* Read the directory and prune it */ - read_directory(dir, prefix, prefix_len, pathspec); + read_directory(dir, &the_index, prefix, prefix_len, pathspec); return prefix_len; } @@ -2071,8 +2071,8 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d return root; } -int read_directory(struct dir_struct *dir, const char *path, - int len, const struct pathspec *pathspec) +int read_directory(struct dir_struct *dir, struct index_state *istate, + const char *path, int len, const struct pathspec *pathspec) { struct untracked_cache_dir *untracked; @@ -2086,8 +2086,8 @@ int read_directory(struct dir_struct *dir, const char *path, * e.g. prep_exclude() */ dir->untracked = NULL; - if (!len || treat_leading_path(dir, &the_index, path, len, pathspec)) - read_directory_recursive(dir, &the_index, path, len, untracked, 0, pathspec); + if (!len || treat_leading_path(dir, istate, path, len, pathspec)) + read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec); QSORT(dir->entries, dir->nr, cmp_name); QSORT(dir->ignored, dir->ignored_nr, cmp_name); if (dir->untracked) { @@ -2101,12 +2101,12 @@ int read_directory(struct dir_struct *dir, const char *path, dir->untracked->gitignore_invalidated, dir->untracked->dir_invalidated, dir->untracked->dir_opened); - if (dir->untracked == the_index.untracked && + if (dir->untracked == istate->untracked && (dir->untracked->dir_opened || dir->untracked->gitignore_invalidated || dir->untracked->dir_invalidated)) - the_index.cache_changed |= UNTRACKED_CHANGED; - if (dir->untracked != the_index.untracked) { + istate->cache_changed |= UNTRACKED_CHANGED; + if (dir->untracked != istate->untracked) { free(dir->untracked); dir->untracked = NULL; } diff --git a/dir.h b/dir.h index b745f1e5d7..a23bcd005c 100644 --- a/dir.h +++ b/dir.h @@ -215,7 +215,9 @@ extern int report_path_error(const char *ps_matched, const struct pathspec *path extern int within_depth(const char *name, int namelen, int depth, int max_depth); extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec); -extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec); +extern int read_directory(struct dir_struct *, struct index_state *istate, + const char *path, int len, + const struct pathspec *pathspec); extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, diff --git a/unpack-trees.c b/unpack-trees.c index d5b401450f..cff6b3f8c4 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1592,7 +1592,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce, memset(&d, 0, sizeof(d)); if (o->dir) d.exclude_per_dir = o->dir->exclude_per_dir; - i = read_directory(&d, pathbuf, namelen+1, NULL); + i = read_directory(&d, &the_index, pathbuf, namelen+1, NULL); if (i) return o->gently ? -1 : add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name); From 0d32c183b636ffba6462b21ed0c844b6cb887b3f Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 5 May 2017 12:53:34 -0700 Subject: [PATCH 099/566] dir: convert fill_directory to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/add.c | 2 +- builtin/clean.c | 2 +- builtin/grep.c | 2 +- builtin/ls-files.c | 2 +- dir.c | 6 ++++-- dir.h | 4 +++- wt-status.c | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index 0b52aeced3..36cad00ae6 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -400,7 +400,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, &pathspec); + baselen = fill_directory(&dir, &the_index, &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen); } diff --git a/builtin/clean.c b/builtin/clean.c index 39866afab4..329b68c40b 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -930,7 +930,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) PATHSPEC_PREFER_CWD, prefix, argv); - fill_directory(&dir, &pathspec); + fill_directory(&dir, &the_index, &pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; diff --git a/builtin/grep.c b/builtin/grep.c index 3ffb5b4e81..f2829629b6 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -866,7 +866,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, if (exc_std) setup_standard_excludes(&dir); - fill_directory(&dir, pathspec); + fill_directory(&dir, &the_index, pathspec); for (i = 0; i < dir.nr; i++) { if (!dir_path_match(dir.entries[i], pathspec, 0, NULL)) continue; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 7a8c5681b6..61271b52cf 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -333,7 +333,7 @@ static void show_files(struct dir_struct *dir) if (show_others || show_killed) { if (!show_others) dir->flags |= DIR_COLLECT_KILLED_ONLY; - fill_directory(dir, &pathspec); + fill_directory(dir, &the_index, &pathspec); if (show_others) show_other_files(dir); if (show_killed) diff --git a/dir.c b/dir.c index 4eb8cb6a21..3f3167e55a 100644 --- a/dir.c +++ b/dir.c @@ -177,7 +177,9 @@ char *common_prefix(const struct pathspec *pathspec) return len ? xmemdupz(pathspec->items[0].match, len) : NULL; } -int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) +int fill_directory(struct dir_struct *dir, + struct index_state *istate, + const struct pathspec *pathspec) { const char *prefix; size_t prefix_len; @@ -190,7 +192,7 @@ int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) prefix = prefix_len ? pathspec->items[0].match : ""; /* Read the directory and prune it */ - read_directory(dir, &the_index, prefix, prefix_len, pathspec); + read_directory(dir, istate, prefix, prefix_len, pathspec); return prefix_len; } diff --git a/dir.h b/dir.h index a23bcd005c..17d110693d 100644 --- a/dir.h +++ b/dir.h @@ -214,7 +214,9 @@ extern int match_pathspec(const struct pathspec *pathspec, extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix); extern int within_depth(const char *name, int namelen, int depth, int max_depth); -extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec); +extern int fill_directory(struct dir_struct *dir, + struct index_state *istate, + const struct pathspec *pathspec); extern int read_directory(struct dir_struct *, struct index_state *istate, const char *path, int len, const struct pathspec *pathspec); diff --git a/wt-status.c b/wt-status.c index 0375484962..3b40f74055 100644 --- a/wt-status.c +++ b/wt-status.c @@ -665,7 +665,7 @@ static void wt_status_collect_untracked(struct wt_status *s) dir.untracked = the_index.untracked; setup_standard_excludes(&dir); - fill_directory(&dir, &s->pathspec); + fill_directory(&dir, &the_index, &s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; From 5a5221427cb8b78c3246ffa305900db935017c54 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Thu, 4 May 2017 14:43:55 -0700 Subject: [PATCH 100/566] diff: recurse into nested submodules for inline diff When fd47ae6a5b (diff: teach diff to display submodule difference with an inline diff, 2016-08-31) was introduced, we did not think of recursing into nested submodules. When showing the inline diff for submodules, automatically recurse into nested submodules as well with inline submodule diffs. Signed-off-by: Stefan Beller Acked-by: Jacob Keller Signed-off-by: Junio C Hamano --- submodule.c | 3 +- t/t4060-diff-submodule-option-diff-format.sh | 41 ++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/submodule.c b/submodule.c index 5a62aa2960..be29b3471a 100644 --- a/submodule.c +++ b/submodule.c @@ -475,7 +475,8 @@ void show_submodule_inline_diff(FILE *f, const char *path, cp.no_stdin = 1; /* TODO: other options may need to be passed here. */ - argv_array_push(&cp.args, "diff"); + argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL); + argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix); if (DIFF_OPT_TST(o, REVERSE_DIFF)) { argv_array_pushf(&cp.args, "--src-prefix=%s%s/", diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh index 7e23b55ea4..33b661140d 100755 --- a/t/t4060-diff-submodule-option-diff-format.sh +++ b/t/t4060-diff-submodule-option-diff-format.sh @@ -746,4 +746,45 @@ test_expect_success 'diff --submodule=diff with .git file' ' test_cmp expected actual ' +test_expect_success 'diff --submodule=diff recurses into nested submodules' ' + cat >expected <<-EOF && + Submodule sm2 contains modified content + Submodule sm2 a5a65c9..280969a: + diff --git a/sm2/.gitmodules b/sm2/.gitmodules + new file mode 100644 + index 0000000..3a816b8 + --- /dev/null + +++ b/sm2/.gitmodules + @@ -0,0 +1,3 @@ + +[submodule "nested"] + + path = nested + + url = ../sm2 + Submodule nested 0000000...b55928c (new submodule) + diff --git a/sm2/nested/file b/sm2/nested/file + new file mode 100644 + index 0000000..ca281f5 + --- /dev/null + +++ b/sm2/nested/file + @@ -0,0 +1 @@ + +nested content + diff --git a/sm2/nested/foo8 b/sm2/nested/foo8 + new file mode 100644 + index 0000000..db9916b + --- /dev/null + +++ b/sm2/nested/foo8 + @@ -0,0 +1 @@ + +foo8 + diff --git a/sm2/nested/foo9 b/sm2/nested/foo9 + new file mode 100644 + index 0000000..9c3b4f6 + --- /dev/null + +++ b/sm2/nested/foo9 + @@ -0,0 +1 @@ + +foo9 + EOF + git diff --submodule=diff >actual 2>err && + test_must_be_empty err && + test_cmp expected actual +' + test_done From 2e477d8d71709120023987f52a93721100ad0f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Fri, 5 May 2017 10:08:03 +0000 Subject: [PATCH 101/566] doc: replace a couple of broken gmane links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace a couple of broken links to gmane with links to other archives. See commit 54471fdcc3 ("README: replace gmane link with public-inbox", 2016-12-15) for prior art. With this change there's still 4 references left in the code: $ git grep -E '(article|thread)\.gmane.org' -- |grep -v RelNotes|wc -l 4 I couldn't find alternative links for those. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/CodingGuidelines | 2 +- Documentation/git-bisect-lk2009.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index a4191aa388..2248cf7324 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -24,7 +24,7 @@ code. For Git in general, a few rough rules are: "Once it _is_ in the tree, it's not really worth the patch noise to go and fix it up." - Cf. http://article.gmane.org/gmane.linux.kernel/943020 + Cf. http://lkml.iu.edu/hypermail/linux/kernel/1001.3/01069.html Make your code readable and sensible, and don't try to be clever. diff --git a/Documentation/git-bisect-lk2009.txt b/Documentation/git-bisect-lk2009.txt index 8ac75fcc25..b5d5e8b544 100644 --- a/Documentation/git-bisect-lk2009.txt +++ b/Documentation/git-bisect-lk2009.txt @@ -1350,7 +1350,7 @@ References - [[[1]]] https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'. Nist Planning Report 02-3], see Executive Summary and Chapter 8. - [[[2]]] http://www.oracle.com/technetwork/java/codeconvtoc-136057.html['Code Conventions for the Java Programming Language'. Sun Microsystems.] - [[[3]]] https://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.] -- [[[4]]] http://article.gmane.org/gmane.comp.version-control.git/45195/[Junio C Hamano. 'Automated bisect success story'. Gmane.] +- [[[4]]] https://public-inbox.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/[Junio C Hamano. 'Automated bisect success story'.] - [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.] - [[[6]]] https://lwn.net/Articles/277872/[Jonathan Corbet. 'Bisection divides users and developers'. LWN.net.] - [[[7]]] http://article.gmane.org/gmane.linux.scsi/36652/[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Gmane.] From f9d7abec2ad2f9eb3d8873169cc28c34273df082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Fri, 5 May 2017 16:57:12 +0200 Subject: [PATCH 102/566] split-index: add and use unshare_split_index() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When split-index is being used, we have two cache_entry arrays in index_state->cache[] and index_state->split_index->base->cache[]. index_state->cache[] may share the same entries with base->cache[] so we can quickly determine what entries are shared. This makes memory management tricky, we can't free base->cache[] until we know index_state->cache[] does not point to any of those entries. unshare_split_index() is added for this purpose, to find shared entries and either duplicate them in index_state->cache[], or discard them. Either way it should be safe to free base->cache[] after unshare_split_index(). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- read-cache.c | 10 ++------- split-index.c | 57 +++++++++++++++++++++++++++++++++++++++------------ split-index.h | 1 + 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/read-cache.c b/read-cache.c index 0d0081a11b..8da84ae2d1 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1877,15 +1877,9 @@ int discard_index(struct index_state *istate) { int i; - for (i = 0; i < istate->cache_nr; i++) { - if (istate->cache[i]->index && - istate->split_index && - istate->split_index->base && - istate->cache[i]->index <= istate->split_index->base->cache_nr && - istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1]) - continue; + unshare_split_index(istate, 1); + for (i = 0; i < istate->cache_nr; i++) free(istate->cache[i]); - } resolve_undo_clear_index(istate); istate->cache_nr = 0; istate->cache_changed = 0; diff --git a/split-index.c b/split-index.c index f519e60f87..49bd197f71 100644 --- a/split-index.c +++ b/split-index.c @@ -73,10 +73,17 @@ void move_cache_to_base_index(struct index_state *istate) int i; /* - * do not delete old si->base, its index entries may be shared - * with istate->cache[]. Accept a bit of leaking here because - * this code is only used by short-lived update-index. + * If "si" is shared with another index_state (e.g. by + * unpack-trees code), we will need to duplicate split_index + * struct. It's not happening now though, luckily. */ + assert(si->refcount <= 1); + + unshare_split_index(istate, 0); + if (si->base) { + discard_index(si->base); + free(si->base); + } si->base = xcalloc(1, sizeof(*si->base)); si->base->version = istate->version; /* zero timestamp disables racy test in ce_write_index() */ @@ -275,11 +282,41 @@ void finish_writing_split_index(struct index_state *istate) istate->cache_nr = si->saved_cache_nr; } +void unshare_split_index(struct index_state *istate, int discard) +{ + struct split_index *si = istate->split_index; + int i; + + if (!si || !si->base) + return; + + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + struct cache_entry *new = NULL; + + if (!ce->index || + ce->index > si->base->cache_nr || + ce != si->base->cache[ce->index - 1]) + continue; + + if (!discard) { + int len = ce_namelen(ce); + new = xcalloc(1, cache_entry_size(len)); + copy_cache_entry(new, ce); + memcpy(new->name, ce->name, len); + new->index = 0; + } + istate->cache[i] = new; + } +} + + void discard_split_index(struct index_state *istate) { struct split_index *si = istate->split_index; if (!si) return; + unshare_split_index(istate, 0); istate->split_index = NULL; si->refcount--; if (si->refcount) @@ -328,14 +365,8 @@ void add_split_index(struct index_state *istate) void remove_split_index(struct index_state *istate) { - if (istate->split_index) { - /* - * can't discard_split_index(&the_index); because that - * will destroy split_index->base->cache[], which may - * be shared with the_index.cache[]. So yeah we're - * leaking a bit here. - */ - istate->split_index = NULL; - istate->cache_changed |= SOMETHING_CHANGED; - } + if (!istate->split_index) + return; + discard_split_index(istate); + istate->cache_changed |= SOMETHING_CHANGED; } diff --git a/split-index.h b/split-index.h index df91c1bda8..65c0f09b2b 100644 --- a/split-index.h +++ b/split-index.h @@ -33,5 +33,6 @@ void finish_writing_split_index(struct index_state *istate); void discard_split_index(struct index_state *istate); void add_split_index(struct index_state *istate); void remove_split_index(struct index_state *istate); +void unshare_split_index(struct index_state *istate, int discard); #endif From de950c577317f499df44c383ecbb7967f4905db9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 5 May 2017 16:57:13 +0200 Subject: [PATCH 103/566] p3400: add perf tests for rebasing many changes Rebasing onto many changes is interesting, but it's also interesting to see what happens when rebasing many changes. And while at it, let's also look at the impact of using a split index. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/perf/p3400-rebase.sh | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh index b3e7d525d2..ce271ca4c1 100755 --- a/t/perf/p3400-rebase.sh +++ b/t/perf/p3400-rebase.sh @@ -5,7 +5,7 @@ test_description='Tests rebase performance' test_perf_default_repo -test_expect_success 'setup' ' +test_expect_success 'setup rebasing on top of a lot of changes' ' git checkout -f -b base && git checkout -b to-rebase && git checkout -b upstream && @@ -33,4 +33,24 @@ test_perf 'rebase on top of a lot of unrelated changes' ' git rebase --onto base HEAD^ ' +test_expect_success 'setup rebasing many changes without split-index' ' + git config core.splitIndex false && + git checkout -b upstream2 to-rebase && + git checkout -b to-rebase2 upstream +' + +test_perf 'rebase a lot of unrelated changes without split-index' ' + git rebase --onto upstream2 base && + git rebase --onto base upstream2 +' + +test_expect_success 'setup rebasing many changes with split-index' ' + git config core.splitIndex true +' + +test_perf 'rebase a lot of unrelated changes with split-index' ' + git rebase --onto upstream2 base && + git rebase --onto base upstream2 +' + test_done From 070e5f72d9d6f0056895bd3222e75f8075ed4c0e Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:53 -0400 Subject: [PATCH 104/566] convert: remove erroneous tests for errno == EPIPE start_multi_file_filter() and apply_multi_file_filter() currently test for errno == EPIPE but treating EPIPE as an error is already happening from one of the packet_write() functions. Signed-off-by: Ben Peart Found/Fixed-by: Jeff King Acked-by: Lars Schneider Signed-off-by: Junio C Hamano --- convert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/convert.c b/convert.c index 8d652bf27c..3908416726 100644 --- a/convert.c +++ b/convert.c @@ -661,7 +661,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons done: sigchain_pop(SIGPIPE); - if (err || errno == EPIPE) { + if (err) { error("initialization for external filter '%s' failed", cmd); kill_multi_file_filter(hashmap, entry); return NULL; @@ -752,7 +752,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len done: sigchain_pop(SIGPIPE); - if (err || errno == EPIPE) { + if (err) { if (!strcmp(filter_status.buf, "error")) { /* The filter signaled a problem with the file. */ } else if (!strcmp(filter_status.buf, "abort")) { From 974b50c5565107e1e3209176682ee6f6e420bd2b Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:54 -0400 Subject: [PATCH 105/566] pkt-line: fix packet_read_line() to handle len < 0 errors Update packet_read_line() to test for len > 0 to avoid potential bug if read functions return lengths less than zero to indicate errors. Signed-off-by: Ben Peart Found/Fixed-by: Lars Schneider Signed-off-by: Junio C Hamano --- pkt-line.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkt-line.c b/pkt-line.c index d4b6bfe076..6f05b1a4a8 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -315,7 +315,7 @@ static char *packet_read_line_generic(int fd, PACKET_READ_CHOMP_NEWLINE); if (dst_len) *dst_len = len; - return len ? packet_buffer : NULL; + return (len > 0) ? packet_buffer : NULL; } char *packet_read_line(int fd, int *len_p) From 825b9226bf7a962bc76571319b1444bc02fdcdf8 Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:55 -0400 Subject: [PATCH 106/566] pkt-line: add packet_read_line_gently() Add packet_read_line_gently() to enable reading a line without dying on EOF. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- pkt-line.c | 12 ++++++++++++ pkt-line.h | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/pkt-line.c b/pkt-line.c index 6f05b1a4a8..7db9119573 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -323,6 +323,18 @@ char *packet_read_line(int fd, int *len_p) return packet_read_line_generic(fd, NULL, NULL, len_p); } +int packet_read_line_gently(int fd, int *dst_len, char **dst_line) +{ + int len = packet_read(fd, NULL, NULL, + packet_buffer, sizeof(packet_buffer), + PACKET_READ_CHOMP_NEWLINE|PACKET_READ_GENTLE_ON_EOF); + if (dst_len) + *dst_len = len; + if (dst_line) + *dst_line = (len > 0) ? packet_buffer : NULL; + return len; +} + char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len) { return packet_read_line_generic(-1, src, src_len, dst_len); diff --git a/pkt-line.h b/pkt-line.h index 18eac64830..66ef610fc4 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -73,6 +73,17 @@ int packet_read(int fd, char **src_buffer, size_t *src_len, char */ char *packet_read_line(int fd, int *size); +/* + * Convenience wrapper for packet_read that sets the PACKET_READ_GENTLE_ON_EOF + * and CHOMP_NEWLINE options. The return value specifies the number of bytes + * read into the buffer or -1 on truncated input. If the *dst_line parameter + * is not NULL it will return NULL for a flush packet or when the number of + * bytes copied is zero and otherwise points to a static buffer (that may be + * overwritten by subsequent calls). If the size parameter is not NULL, the + * length of the packet is written to it. + */ +int packet_read_line_gently(int fd, int *size, char **dst_line); + /* * Same as packet_read_line, but read from a buf rather than a descriptor; * see packet_read for details on how src_* is used. From c0c70f7ac080c3b4ecc50a3207eb66e97c10271d Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:56 -0400 Subject: [PATCH 107/566] convert: move packet_write_line() into pkt-line as packet_writel() Add packet_writel() which writes multiple lines in a single call and then calls packet_flush_gently(). Update convert.c to use the new packet_writel() function from pkt-line. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- convert.c | 23 ++--------------------- pkt-line.c | 19 +++++++++++++++++++ pkt-line.h | 1 + 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/convert.c b/convert.c index 3908416726..0f1b5a6340 100644 --- a/convert.c +++ b/convert.c @@ -521,25 +521,6 @@ static struct cmd2process *find_multi_file_filter_entry(struct hashmap *hashmap, return hashmap_get(hashmap, &key, NULL); } -static int packet_write_list(int fd, const char *line, ...) -{ - va_list args; - int err; - va_start(args, line); - for (;;) { - if (!line) - break; - if (strlen(line) > LARGE_PACKET_DATA_MAX) - return -1; - err = packet_write_fmt_gently(fd, "%s\n", line); - if (err) - return err; - line = va_arg(args, const char*); - } - va_end(args); - return packet_flush_gently(fd); -} - static void read_multi_file_filter_status(int fd, struct strbuf *status) { struct strbuf **pair; @@ -616,7 +597,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons sigchain_push(SIGPIPE, SIG_IGN); - err = packet_write_list(process->in, "git-filter-client", "version=2", NULL); + err = packet_writel(process->in, "git-filter-client", "version=2", NULL); if (err) goto done; @@ -632,7 +613,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons if (err) goto done; - err = packet_write_list(process->in, "capability=clean", "capability=smudge", NULL); + err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL); for (;;) { cap_buf = packet_read_line(process->out, NULL); diff --git a/pkt-line.c b/pkt-line.c index 7db9119573..9d845ecc3c 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -171,6 +171,25 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...) return status; } +int packet_writel(int fd, const char *line, ...) +{ + va_list args; + int err; + va_start(args, line); + for (;;) { + if (!line) + break; + if (strlen(line) > LARGE_PACKET_DATA_MAX) + return -1; + err = packet_write_fmt_gently(fd, "%s\n", line); + if (err) + return err; + line = va_arg(args, const char*); + } + va_end(args); + return packet_flush_gently(fd); +} + static int packet_write_gently(const int fd_out, const char *buf, size_t size) { static char packet_write_buffer[LARGE_PACKET_MAX]; diff --git a/pkt-line.h b/pkt-line.h index 66ef610fc4..b2965869ad 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -25,6 +25,7 @@ void packet_buf_flush(struct strbuf *buf); void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3))); int packet_flush_gently(int fd); int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3))); +int packet_writel(int fd, const char *line, ...); int write_packetized_from_fd(int fd_in, int fd_out); int write_packetized_from_buf(const char *src_in, size_t len, int fd_out); From 79e913c24aa37d0ede9ed9e8962a19634ae5129a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 May 2017 19:13:52 +0200 Subject: [PATCH 108/566] checkout: check return value of resolve_refdup before using hash If resolve_refdup() fails it returns NULL and possibly leaves its hash output parameter untouched. Make sure to use it only if the function succeeded, in order to avoid accessing uninitialized memory. Found with t/t2011-checkout-invalid-head.sh --valgrind. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin/checkout.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 81f07c3ef2..5744499098 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -814,7 +814,8 @@ static int switch_branches(const struct checkout_opts *opts, int flag, writeout_error = 0; memset(&old, 0, sizeof(old)); old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag); - old.commit = lookup_commit_reference_gently(rev.hash, 1); + if (old.path) + old.commit = lookup_commit_reference_gently(rev.hash, 1); if (!(flag & REF_ISSYMREF)) old.path = NULL; From 57e0ef0e0e90c4cb72c35db874fc3d035b88ce4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 May 2017 19:13:56 +0200 Subject: [PATCH 109/566] am: check return value of resolve_refdup before using hash If resolve_refdup() fails it returns NULL and possibly leaves its hash output parameter untouched. Make sure to use it only if the function succeeded, in order to avoid accessing uninitialized memory. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin/am.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/am.c b/builtin/am.c index 31fb60578f..695968622a 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2157,7 +2157,7 @@ static void am_abort(struct am_state *state) am_rerere_clear(); curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL); - has_curr_head = !is_null_oid(&curr_head); + has_curr_head = curr_branch && !is_null_oid(&curr_head); if (!has_curr_head) hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN); From 1a845a2c88e5b72acce33c9719ec36fcd0f9c877 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:24 +0200 Subject: [PATCH 110/566] mingw: avoid memory leak when splitting PATH In the (admittedly, concocted) case that PATH consists only of path delimiters, we would leak the duplicated string. Reported by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/mingw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compat/mingw.c b/compat/mingw.c index 3fbfda5978..fe0e3ccd24 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -961,8 +961,10 @@ static char **get_path_split(void) ++n; } } - if (!n) + if (!n) { + free(envpath); return NULL; + } ALLOC_ARRAY(path, n + 1); p = envpath; From 3057da5ad51b3256a6f6b94d034e6a5571ce5ece Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:29 +0200 Subject: [PATCH 111/566] winansi: avoid use of uninitialized value To initialize the foreground color attributes of "plain text", our ANSI emulation tries to infer them from the currently attached console while running the is_console() function. This function first tries to detect any console attached to stdout, then it is called with stderr. If neither stdout nor stderr has any console attached, it does not actually matter what we use for "plain text" attributes, as we never need to output any text to any console in that case. However, after working on stdout and stderr, is_console() is called with stdin, and it still tries to initialize the "plain text" attributes if they had not been initialized earlier. In this case, we cannot detect any attributes, and we used an uninitialized value for them. Naturally, Coverity complained about this use case because it could not reason about the code deeply enough to figure out that we do not even use those attributes in that case. Let's just initialize the value to 0 in that case, both to avoid future Coverity reports, and to help catch future regressions in case anybody changes the order of the is_console() calls (which would make the text black on black). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/winansi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compat/winansi.c b/compat/winansi.c index 793420f9d0..a551de90eb 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -105,6 +105,13 @@ static int is_console(int fd) if (!fd) { if (!GetConsoleMode(hcon, &mode)) return 0; + /* + * This code path is only reached if there is no console + * attached to stdout/stderr, i.e. we will not need to output + * any text to any console, therefore we might just as well + * use black as foreground color. + */ + sbi.wAttributes = 0; } else if (!GetConsoleScreenBufferInfo(hcon, &sbi)) return 0; From b6b066adf9e1e970a6d8295db630ab1e1f3bc71c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:34 +0200 Subject: [PATCH 112/566] winansi: avoid buffer overrun When we could not convert the UTF-8 sequence into Unicode for writing to the Console, we should not try to write an insanely-long sequence of invalid wide characters (mistaking the negative return value for an unsigned length). Reported by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/winansi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compat/winansi.c b/compat/winansi.c index a551de90eb..a11a0f16d2 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -140,6 +140,11 @@ static void write_console(unsigned char *str, size_t len) /* convert utf-8 to utf-16 */ int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len); + if (wlen < 0) { + wchar_t *err = L"[invalid]"; + WriteConsoleW(console, err, wcslen(err), &dummy, NULL); + return; + } /* write directly to console */ WriteConsoleW(console, wbuf, wlen, &dummy, NULL); From 5748693b9123f55363211d18e8074db5e1b8384e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:38 +0200 Subject: [PATCH 113/566] add_commit_patch_id(): avoid allocating memory unnecessarily It would appear that we allocate (and forget to release) memory if the patch ID is not even defined. Reported by the Coverity tool. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- patch-ids.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/patch-ids.c b/patch-ids.c index fa8f11de82..92eba7a059 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -99,11 +99,12 @@ struct patch_id *has_commit_patch_id(struct commit *commit, struct patch_id *add_commit_patch_id(struct commit *commit, struct patch_ids *ids) { - struct patch_id *key = xcalloc(1, sizeof(*key)); + struct patch_id *key; if (!patch_id_defined(commit)) return NULL; + key = xcalloc(1, sizeof(*key)); if (init_patch_id_entry(key, commit, ids)) { free(key); return NULL; From 4db7dbdb4ae5426ab133b3844ea2a6889c6897d7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:41 +0200 Subject: [PATCH 114/566] git_config_rename_section_in_file(): avoid resource leak In case of errors, we really want the file descriptor to be closed. Discovered by a Coverity scan. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- config.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index b4a3205da3..a30056ec7e 100644 --- a/config.c +++ b/config.c @@ -2621,7 +2621,7 @@ int git_config_rename_section_in_file(const char *config_filename, struct lock_file *lock; int out_fd; char buf[1024]; - FILE *config_file; + FILE *config_file = NULL; struct stat st; if (new_name && !section_name_is_ok(new_name)) { @@ -2703,11 +2703,14 @@ int git_config_rename_section_in_file(const char *config_filename, } } fclose(config_file); + config_file = NULL; commit_and_out: if (commit_lock_file(lock) < 0) ret = error_errno("could not write config file %s", config_filename); out: + if (config_file) + fclose(config_file); rollback_lock_file(lock); out_no_rollback: free(filename_buf); From 5b34ba414d105a8219e55babd0780a935a0c0c20 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:45 +0200 Subject: [PATCH 115/566] get_mail_commit_oid(): avoid resource leak When we fail to read, or parse, the file, we still want to close the file descriptor and release the strbuf. Reported via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/am.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index a95dd8b4e6..9c5c2c778e 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1351,19 +1351,16 @@ static int get_mail_commit_oid(struct object_id *commit_id, const char *mail) struct strbuf sb = STRBUF_INIT; FILE *fp = xfopen(mail, "r"); const char *x; + int ret = 0; - if (strbuf_getline_lf(&sb, fp)) - return -1; - - if (!skip_prefix(sb.buf, "From ", &x)) - return -1; - - if (get_oid_hex(x, commit_id) < 0) - return -1; + if (strbuf_getline_lf(&sb, fp) || + !skip_prefix(sb.buf, "From ", &x) || + get_oid_hex(x, commit_id) < 0) + ret = -1; strbuf_release(&sb); fclose(fp); - return 0; + return ret; } /** From 5f3296c069bfe2872592f96b29bdcc8f0edd5eb8 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:48 +0200 Subject: [PATCH 116/566] difftool: address a couple of resource/memory leaks This change plugs a couple of memory leaks and makes sure that the file descriptor is closed in run_dir_diff(). Spotted by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/difftool.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/builtin/difftool.c b/builtin/difftool.c index 1354d0e462..b9a892f269 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -226,6 +226,7 @@ static void changed_files(struct hashmap *result, const char *index_path, hashmap_entry_init(entry, strhash(buf.buf)); hashmap_add(result, entry); } + fclose(fp); if (finish_command(&diff_files)) die("diff-files did not exit properly"); strbuf_release(&index_env); @@ -439,8 +440,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, } if (lmode && status != 'C') { - if (checkout_path(lmode, &loid, src_path, &lstate)) - return error("could not write '%s'", src_path); + if (checkout_path(lmode, &loid, src_path, &lstate)) { + ret = error("could not write '%s'", src_path); + goto finish; + } } if (rmode && !S_ISLNK(rmode)) { @@ -456,9 +459,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, hashmap_add(&working_tree_dups, entry); if (!use_wt_file(workdir, dst_path, &roid)) { - if (checkout_path(rmode, &roid, dst_path, &rstate)) - return error("could not write '%s'", - dst_path); + if (checkout_path(rmode, &roid, dst_path, + &rstate)) { + ret = error("could not write '%s'", + dst_path); + goto finish; + } } else if (!is_null_oid(&roid)) { /* * Changes in the working tree need special @@ -473,10 +479,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, ADD_CACHE_JUST_APPEND); add_path(&rdir, rdir_len, dst_path); - if (ensure_leading_directories(rdir.buf)) - return error("could not create " - "directory for '%s'", - dst_path); + if (ensure_leading_directories(rdir.buf)) { + ret = error("could not create " + "directory for '%s'", + dst_path); + goto finish; + } add_path(&wtdir, wtdir_len, dst_path); if (symlinks) { if (symlink(wtdir.buf, rdir.buf)) { @@ -497,13 +505,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, } } + fclose(fp); + fp = NULL; if (finish_command(&child)) { ret = error("error occurred running diff --raw"); goto finish; } if (!i) - return 0; + goto finish; /* * Changes to submodules require special treatment.This loop writes a @@ -626,6 +636,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, exit_cleanup(tmpdir, rc); finish: + if (fp) + fclose(fp); + free(lbase_dir); free(rbase_dir); strbuf_release(&ldir); From e7b65e205af88755567c3283ee29ca9fb9af11a9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:55:52 +0200 Subject: [PATCH 117/566] status: close file descriptor after reading git-rebase-todo Reported via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- wt-status.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wt-status.c b/wt-status.c index 0375484962..0a6e16dbe0 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1168,6 +1168,7 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines) abbrev_sha1_in_line(&line); string_list_append(lines, line.buf); } + fclose(f); return 0; } From f0733c13ed8b79bb10e240c4b4a6630784c7d258 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:14 +0200 Subject: [PATCH 118/566] mailinfo & mailsplit: check for EOF while parsing While POSIX states that it is okay to pass EOF to isspace() (and it seems to be implied that EOF should *not* be treated as whitespace), and also to pass EOF to ungetc() (which seems to be intended to fail without buffering the character), it is much better to handle these cases explicitly. Not only does it reduce head-scratching (and helps static analysis avoid reporting false positives), it also lets us handle files containing nothing but whitespace by erroring out. Reported via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/mailsplit.c | 10 ++++++++++ mailinfo.c | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c index 30681681c1..664400b816 100644 --- a/builtin/mailsplit.c +++ b/builtin/mailsplit.c @@ -232,6 +232,16 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, do { peek = fgetc(f); + if (peek == EOF) { + if (f == stdin) + /* empty stdin is OK */ + ret = skip; + else { + fclose(f); + error(_("empty mbox: '%s'"), file); + } + goto out; + } } while (isspace(peek)); ungetc(peek, f); diff --git a/mailinfo.c b/mailinfo.c index 68037758f2..f92cb9f729 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -882,7 +882,10 @@ static int read_one_header_line(struct strbuf *line, FILE *in) for (;;) { int peek; - peek = fgetc(in); ungetc(peek, in); + peek = fgetc(in); + if (peek == EOF) + break; + ungetc(peek, in); if (peek != ' ' && peek != '\t') break; if (strbuf_getline_lf(&continuation, in)) @@ -1099,6 +1102,10 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch) do { peek = fgetc(mi->input); + if (peek == EOF) { + fclose(cmitmsg); + return error("empty patch: '%s'", patch); + } } while (isspace(peek)); ungetc(peek, mi->input); From 05c2b7ba49e36a6beff30204948dba79451c4233 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:17 +0200 Subject: [PATCH 119/566] cat-file: fix memory leak Discovered by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 1890d7a639..9af863e791 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -165,6 +165,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, die("git cat-file %s: bad file", obj_name); write_or_die(1, buf, size); + free(buf); return 0; } From 514e8039444565f7806e065579090167db5e489c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:40 +0200 Subject: [PATCH 120/566] checkout: fix memory leak This change addresses part of the NEEDSWORK comment above the code, therefore the comment needs to be adjusted, too. Discovered via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/checkout.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index bfa5419f33..5faea3a05f 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -235,14 +235,14 @@ static int checkout_merged(int pos, const struct checkout *state) /* * NEEDSWORK: * There is absolutely no reason to write this as a blob object - * and create a phony cache entry just to leak. This hack is - * primarily to get to the write_entry() machinery that massages - * the contents to work-tree format and writes out which only - * allows it for a cache entry. The code in write_entry() needs - * to be refactored to allow us to feed a - * instead of a cache entry. Such a refactoring would help - * merge_recursive as well (it also writes the merge result to the - * object database even when it may contain conflicts). + * and create a phony cache entry. This hack is primarily to get + * to the write_entry() machinery that massages the contents to + * work-tree format and writes out which only allows it for a + * cache entry. The code in write_entry() needs to be refactored + * to allow us to feed a instead of a cache + * entry. Such a refactoring would help merge_recursive as well + * (it also writes the merge result to the object database even + * when it may contain conflicts). */ if (write_sha1_file(result_buf.ptr, result_buf.size, blob_type, oid.hash)) @@ -251,6 +251,7 @@ static int checkout_merged(int pos, const struct checkout *state) if (!ce) die(_("make_cache_entry failed for path '%s'"), path); status = checkout_entry(ce, state, NULL); + free(ce); return status; } From 41fc6b33fc0cef748f84906810bb36fcd491f0a1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:44 +0200 Subject: [PATCH 121/566] split_commit_in_progress(): simplify & fix memory leak This function did a whole lot of unnecessary work, such as reading in four files just to figure out that, oh, hey, we do not need to look at them after all because the HEAD is not detached. Simplify the entire function to return early when possible, to read in the files only when necessary, and to release the allocated memory always (there was a leak, reported via Coverity, where we failed to release the allocated strings if the HEAD is not detached). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- wt-status.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/wt-status.c b/wt-status.c index 0a6e16dbe0..117ac8cfb0 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1082,29 +1082,29 @@ static char *read_line_from_git_path(const char *filename) static int split_commit_in_progress(struct wt_status *s) { int split_in_progress = 0; - char *head = read_line_from_git_path("HEAD"); - char *orig_head = read_line_from_git_path("ORIG_HEAD"); - char *rebase_amend = read_line_from_git_path("rebase-merge/amend"); - char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head"); + char *head, *orig_head, *rebase_amend, *rebase_orig_head; - if (!head || !orig_head || !rebase_amend || !rebase_orig_head || + if ((!s->amend && !s->nowarn && !s->workdir_dirty) || !s->branch || strcmp(s->branch, "HEAD")) - return split_in_progress; + return 0; - if (!strcmp(rebase_amend, rebase_orig_head)) { - if (strcmp(head, rebase_amend)) - split_in_progress = 1; - } else if (strcmp(orig_head, rebase_orig_head)) { + head = read_line_from_git_path("HEAD"); + orig_head = read_line_from_git_path("ORIG_HEAD"); + rebase_amend = read_line_from_git_path("rebase-merge/amend"); + rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head"); + + if (!head || !orig_head || !rebase_amend || !rebase_orig_head) + ; /* fall through, no split in progress */ + else if (!strcmp(rebase_amend, rebase_orig_head)) + split_in_progress = !!strcmp(head, rebase_amend); + else if (strcmp(orig_head, rebase_orig_head)) split_in_progress = 1; - } - - if (!s->amend && !s->nowarn && !s->workdir_dirty) - split_in_progress = 0; free(head); free(orig_head); free(rebase_amend); free(rebase_orig_head); + return split_in_progress; } From da6f847559d8794f25da9f7c090929baebce7751 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:47 +0200 Subject: [PATCH 122/566] setup_bare_git_dir(): help static analysis Coverity reported a memory leak in this function. However, it can only be called once, as setup_git_directory() changes global state and hence is not reentrant. Mark the variable as static to indicate that this is a singleton. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.c b/setup.c index 0309c27821..0320a9ad14 100644 --- a/setup.c +++ b/setup.c @@ -748,7 +748,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset, /* --work-tree is set without --git-dir; use discovered one */ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { - const char *gitdir; + static const char *gitdir; gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset); if (chdir(cwd->buf)) From 2d4dcf210e156153a9c1b11bc60d647d5a327624 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:51 +0200 Subject: [PATCH 123/566] setup_discovered_git_dir(): plug memory leak The setup_explicit_git_dir() function does not take custody of the string passed as first parameter; we have to release it if we turned the value of git_dir into an absolute path. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- setup.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.c b/setup.c index 0320a9ad14..e3f7699a90 100644 --- a/setup.c +++ b/setup.c @@ -703,11 +703,16 @@ static const char *setup_discovered_git_dir(const char *gitdir, /* --work-tree is set without --git-dir; use discovered one */ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { + char *to_free = NULL; + const char *ret; + if (offset != cwd->len && !is_absolute_path(gitdir)) - gitdir = real_pathdup(gitdir, 1); + gitdir = to_free = real_pathdup(gitdir, 1); if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); - return setup_explicit_git_dir(gitdir, cwd, nongit_ok); + ret = setup_explicit_git_dir(gitdir, cwd, nongit_ok); + free(to_free); + return ret; } /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */ From 077a34e0b9401f9bcea991d850f1111a70e7d3d1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:56:54 +0200 Subject: [PATCH 124/566] pack-redundant: plug memory leak Identified via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/pack-redundant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 72c815844d..cb1df1c761 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -442,6 +442,7 @@ static void minimize(struct pack_list **min) /* return if there are no objects missing from the unique set */ if (missing->size == 0) { *min = unique; + free(missing); return; } From 43e61e715283ba3e6cfe5ebf9e831e1d96cec399 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:57:28 +0200 Subject: [PATCH 125/566] mktree: plug memory leaks reported by Coverity Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/mktree.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index de9b40fc63..da0fd8cd70 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -72,7 +72,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss unsigned mode; enum object_type mode_type; /* object type derived from mode */ enum object_type obj_type; /* object type derived from sha */ - char *path; + char *path, *to_free = NULL; unsigned char sha1[20]; ptr = buf; @@ -102,7 +102,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss struct strbuf p_uq = STRBUF_INIT; if (unquote_c_style(&p_uq, path, NULL)) die("invalid quoting"); - path = strbuf_detach(&p_uq, NULL); + path = to_free = strbuf_detach(&p_uq, NULL); } /* @@ -136,6 +136,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss } append_to_tree(mode, sha1, path); + free(to_free); } int cmd_mktree(int ac, const char **av, const char *prefix) From 1efb1e9a21d16efddd1440bd2684bf19dc9546e0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:57:33 +0200 Subject: [PATCH 126/566] fast-export: avoid leaking memory in handle_tag() Reported by, you guessed it, Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/fast-export.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e0220630d0..64617ad8e3 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -734,6 +734,7 @@ static void handle_tag(const char *name, struct tag *tag) oid_to_hex(&tag->object.oid)); case DROP: /* Ignore this tag altogether */ + free(buf); return; case REWRITE: if (tagged->type != OBJ_COMMIT) { @@ -765,6 +766,7 @@ static void handle_tag(const char *name, struct tag *tag) (int)(tagger_end - tagger), tagger, tagger == tagger_end ? "" : "\n", (int)message_size, (int)message_size, message ? message : ""); + free(buf); } static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name) From bda6e82801e3c01d51634e6c722783295fd079ae Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:57:55 +0200 Subject: [PATCH 127/566] receive-pack: plug memory leak in update() Reported via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/receive-pack.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index f96834f42c..48c07ddb65 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -986,7 +986,8 @@ static const char *update(struct command *cmd, struct shallow_info *si) { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; - const char *namespaced_name, *ret; + static char *namespaced_name; + const char *ret; struct object_id *old_oid = &cmd->old_oid; struct object_id *new_oid = &cmd->new_oid; @@ -997,6 +998,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) } strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name); + free(namespaced_name); namespaced_name = strbuf_detach(&namespaced_name_buf, NULL); if (is_ref_checked_out(namespaced_name)) { From 6cf034e7ed3ec0692535416d9d339b60a830bf23 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:58:01 +0200 Subject: [PATCH 128/566] line-log: avoid memory leak Discovered by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- line-log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/line-log.c b/line-log.c index a23b910471..b9087814b8 100644 --- a/line-log.c +++ b/line-log.c @@ -1125,6 +1125,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c changed = process_all_files(&parent_range, rev, &queue, range); if (parent) add_line_range(rev, parent, parent_range); + free_line_log_data(parent_range); return changed; } From 7c565a6b2d8bf7fe989c85dc75df7fabc8113f40 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:58:35 +0200 Subject: [PATCH 129/566] shallow: avoid memory leak Reported by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- shallow.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shallow.c b/shallow.c index 25b6db989b..f9370961f9 100644 --- a/shallow.c +++ b/shallow.c @@ -473,11 +473,15 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1, struct commit_list *head = NULL; int bitmap_nr = (info->nr_bits + 31) / 32; size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr); - uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */ - uint32_t *bitmap = paint_alloc(info); struct commit *c = lookup_commit_reference_gently(sha1, 1); + uint32_t *tmp; /* to be freed before return */ + uint32_t *bitmap; + if (!c) return; + + tmp = xmalloc(bitmap_size); + bitmap = paint_alloc(info); memset(bitmap, 0, bitmap_size); bitmap[id / 32] |= (1U << (id % 32)); commit_list_insert(c, &head); From 5026b471751092ab971f3d4ae46320bc8ce40ff5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:58:42 +0200 Subject: [PATCH 130/566] add_reflog_for_walk: avoid memory leak We free()d the `log` buffer when dwim_log() returned 1, but not when it returned a larger value (which meant that it still allocated the buffer but we simply ignored it). While in the vicinity, make sure that the `reflogs` structure as well as the `branch` variable are released properly, too. Identified by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- reflog-walk.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/reflog-walk.c b/reflog-walk.c index 99679f5825..c63eb1a3fd 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -183,7 +183,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info, if (!reflogs || reflogs->nr == 0) { struct object_id oid; char *b; - if (dwim_log(branch, strlen(branch), oid.hash, &b) == 1) { + int ret = dwim_log(branch, strlen(branch), + oid.hash, &b); + if (ret > 1) + free(b); + else if (ret == 1) { if (reflogs) { free(reflogs->ref); free(reflogs); @@ -193,17 +197,27 @@ int add_reflog_for_walk(struct reflog_walk_info *info, reflogs = read_complete_reflog(branch); } } - if (!reflogs || reflogs->nr == 0) + if (!reflogs || reflogs->nr == 0) { + if (reflogs) { + free(reflogs->ref); + free(reflogs); + } + free(branch); return -1; + } string_list_insert(&info->complete_reflogs, branch)->util = reflogs; } + free(branch); commit_reflog = xcalloc(1, sizeof(struct commit_reflog)); if (recno < 0) { commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); if (commit_reflog->recno < 0) { - free(branch); + if (reflogs) { + free(reflogs->ref); + free(reflogs); + } free(commit_reflog); return -1; } From 3dc7ea91dab9959b6bec82c05df18d6d1bb70619 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:59:01 +0200 Subject: [PATCH 131/566] remote: plug memory leak in match_explicit() The `guess_ref()` returns an allocated buffer of which `make_linked_ref()` does not take custody (`alloc_ref()` makes a copy), therefore we need to release the buffer afterwards. Noticed via Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- remote.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/remote.c b/remote.c index 801137c72e..72b4591b98 100644 --- a/remote.c +++ b/remote.c @@ -1191,9 +1191,10 @@ static int match_explicit(struct ref *src, struct ref *dst, else if (is_null_oid(&matched_src->new_oid)) error("unable to delete '%s': remote ref does not exist", dst_value); - else if ((dst_guess = guess_ref(dst_value, matched_src))) + else if ((dst_guess = guess_ref(dst_value, matched_src))) { matched_dst = make_linked_ref(dst_guess, dst_tail); - else + free(dst_guess); + } else error("unable to push to unqualified destination: %s\n" "The destination refspec neither matches an " "existing ref on the remote nor\n" From 5308224633cf138f436357b2a8a87a546373af72 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:59:06 +0200 Subject: [PATCH 132/566] name-rev: avoid leaking memory in the `deref` case When the `name_rev()` function is asked to dereference the tip name, it allocates memory. But when it turns out that another tip already described the commit better than the current one, we forgot to release the memory. Pointed out by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/name-rev.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 92a5d8a5d2..e7a3fe7ee7 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -28,6 +28,7 @@ static void name_rev(struct commit *commit, struct rev_name *name = (struct rev_name *)commit->util; struct commit_list *parents; int parent_number = 1; + char *to_free = NULL; parse_commit(commit); @@ -35,7 +36,7 @@ static void name_rev(struct commit *commit, return; if (deref) { - tip_name = xstrfmt("%s^0", tip_name); + tip_name = to_free = xstrfmt("%s^0", tip_name); if (generation) die("generation: %d, but deref?", generation); @@ -53,8 +54,10 @@ copy_data: name->taggerdate = taggerdate; name->generation = generation; name->distance = distance; - } else + } else { + free(to_free); return; + } for (parents = commit->parents; parents; From 2e11f58fa6a0c904d9b00e51cfb2d8a3ee77b360 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:59:13 +0200 Subject: [PATCH 133/566] show_worktree(): plug memory leak The buffer allocated by shorten_unambiguous_ref() needs to be released. Discovered by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin/worktree.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index 1722a9bdc2..ff5dfd2b10 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -414,9 +414,11 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV)); if (wt->is_detached) strbuf_addstr(&sb, "(detached HEAD)"); - else if (wt->head_ref) - strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0)); - else + else if (wt->head_ref) { + char *ref = shorten_unambiguous_ref(wt->head_ref, 0); + strbuf_addf(&sb, "[%s]", ref); + free(ref); + } else strbuf_addstr(&sb, "(error)"); } printf("%s\n", sb.buf); From d32de66a07c600c14f70e6d435f8f9c496b0d625 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2017 15:59:19 +0200 Subject: [PATCH 134/566] submodule_uses_worktrees(): plug memory leak There is really no reason why we would need to hold onto the allocated string longer than necessary. Reported by Coverity. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- worktree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worktree.c b/worktree.c index bae787cf8d..89a81b13de 100644 --- a/worktree.c +++ b/worktree.c @@ -399,6 +399,7 @@ int submodule_uses_worktrees(const char *path) /* The env would be set for the superproject. */ get_common_dir_noenv(&sb, submodule_gitdir); + free(submodule_gitdir); /* * The check below is only known to be good for repository format @@ -418,7 +419,6 @@ int submodule_uses_worktrees(const char *path) /* See if there is any file inside the worktrees directory. */ dir = opendir(sb.buf); strbuf_release(&sb); - free(submodule_gitdir); if (!dir) return 0; From 912c13d58faf355589a3f67fd55b2015561e0184 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:09:56 +0000 Subject: [PATCH 135/566] fast-import: convert to struct object_id Convert the remaining parts of fast-import.c to use struct object_id. Convert several instances of get_sha1_hex to parse_oid_hex to avoid needing to specify constants. Convert other hardcoded values to named constants. Finally, use the is_empty_tree_oid function instead of a direct comparison against a fixed string. Note that the odd computation with GIT_MAX_HEXSZ is due to the insertion of a slash between every two hex digits in the path, plus one for the terminating NUL. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- fast-import.c | 317 +++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 158 deletions(-) diff --git a/fast-import.c b/fast-import.c index 0212635ddd..ba886c9e7e 100644 --- a/fast-import.c +++ b/fast-import.c @@ -557,7 +557,7 @@ static void alloc_objects(unsigned int cnt) alloc_count += cnt; } -static struct object_entry *new_object(unsigned char *sha1) +static struct object_entry *new_object(struct object_id *oid) { struct object_entry *e; @@ -565,32 +565,32 @@ static struct object_entry *new_object(unsigned char *sha1) alloc_objects(object_entry_alloc); e = blocks->next_free++; - hashcpy(e->idx.sha1, sha1); + hashcpy(e->idx.sha1, oid->hash); return e; } -static struct object_entry *find_object(unsigned char *sha1) +static struct object_entry *find_object(struct object_id *oid) { - unsigned int h = sha1[0] << 8 | sha1[1]; + unsigned int h = oid->hash[0] << 8 | oid->hash[1]; struct object_entry *e; for (e = object_table[h]; e; e = e->next) - if (!hashcmp(sha1, e->idx.sha1)) + if (!hashcmp(oid->hash, e->idx.sha1)) return e; return NULL; } -static struct object_entry *insert_object(unsigned char *sha1) +static struct object_entry *insert_object(struct object_id *oid) { - unsigned int h = sha1[0] << 8 | sha1[1]; + unsigned int h = oid->hash[0] << 8 | oid->hash[1]; struct object_entry *e = object_table[h]; while (e) { - if (!hashcmp(sha1, e->idx.sha1)) + if (!hashcmp(oid->hash, e->idx.sha1)) return e; e = e->next; } - e = new_object(sha1); + e = new_object(oid); e->next = object_table[h]; e->idx.offset = 0; object_table[h] = e; @@ -1007,17 +1007,17 @@ static void end_packfile(void) clear_delta_base_cache(); if (object_count) { struct packed_git *new_p; - unsigned char cur_pack_sha1[20]; + struct object_id cur_pack_oid; char *idx_name; int i; struct branch *b; struct tag *t; close_pack_windows(pack_data); - sha1close(pack_file, cur_pack_sha1, 0); + sha1close(pack_file, cur_pack_oid.hash, 0); fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1, pack_data->pack_name, object_count, - cur_pack_sha1, pack_size); + cur_pack_oid.hash, pack_size); if (object_count <= unpack_limit) { if (!loosen_small_pack(pack_data)) { @@ -1083,13 +1083,13 @@ static int store_object( enum object_type type, struct strbuf *dat, struct last_object *last, - unsigned char *sha1out, + struct object_id *oidout, uintmax_t mark) { void *out, *delta; struct object_entry *e; unsigned char hdr[96]; - unsigned char sha1[20]; + struct object_id oid; unsigned long hdrlen, deltalen; git_SHA_CTX c; git_zstream s; @@ -1099,17 +1099,17 @@ static int store_object( git_SHA1_Init(&c); git_SHA1_Update(&c, hdr, hdrlen); git_SHA1_Update(&c, dat->buf, dat->len); - git_SHA1_Final(sha1, &c); - if (sha1out) - hashcpy(sha1out, sha1); + git_SHA1_Final(oid.hash, &c); + if (oidout) + oidcpy(oidout, &oid); - e = insert_object(sha1); + e = insert_object(&oid); if (mark) insert_mark(mark, e); if (e->idx.offset) { duplicate_count_by_type[type]++; return 1; - } else if (find_sha1_pack(sha1, packed_git)) { + } else if (find_sha1_pack(oid.hash, packed_git)) { e->type = type; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@ -1222,13 +1222,13 @@ static void truncate_pack(struct sha1file_checkpoint *checkpoint) pack_size = checkpoint->offset; } -static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark) +static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) { size_t in_sz = 64 * 1024, out_sz = 64 * 1024; unsigned char *in_buf = xmalloc(in_sz); unsigned char *out_buf = xmalloc(out_sz); struct object_entry *e; - unsigned char sha1[20]; + struct object_id oid; unsigned long hdrlen; off_t offset; git_SHA_CTX c; @@ -1291,12 +1291,12 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark) } } git_deflate_end(&s); - git_SHA1_Final(sha1, &c); + git_SHA1_Final(oid.hash, &c); - if (sha1out) - hashcpy(sha1out, sha1); + if (oidout) + oidcpy(oidout, &oid); - e = insert_object(sha1); + e = insert_object(&oid); if (mark) insert_mark(mark, e); @@ -1305,7 +1305,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark) duplicate_count_by_type[OBJ_BLOB]++; truncate_pack(&checkpoint); - } else if (find_sha1_pack(sha1, packed_git)) { + } else if (find_sha1_pack(oid.hash, packed_git)) { e->type = OBJ_BLOB; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@ -1389,7 +1389,7 @@ static const char *get_mode(const char *str, uint16_t *modep) static void load_tree(struct tree_entry *root) { - unsigned char *sha1 = root->versions[1].oid.hash; + struct object_id *oid = &root->versions[1].oid; struct object_entry *myoe; struct tree_content *t; unsigned long size; @@ -1397,22 +1397,22 @@ static void load_tree(struct tree_entry *root) const char *c; root->tree = t = new_tree_content(8); - if (is_null_sha1(sha1)) + if (is_null_oid(oid)) return; - myoe = find_object(sha1); + myoe = find_object(oid); if (myoe && myoe->pack_id != MAX_PACK_ID) { if (myoe->type != OBJ_TREE) - die("Not a tree: %s", sha1_to_hex(sha1)); + die("Not a tree: %s", oid_to_hex(oid)); t->delta_depth = myoe->depth; buf = gfi_unpack_entry(myoe, &size); if (!buf) - die("Can't load tree %s", sha1_to_hex(sha1)); + die("Can't load tree %s", oid_to_hex(oid)); } else { enum object_type type; - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid->hash, &type, &size); if (!buf || type != OBJ_TREE) - die("Can't load tree %s", sha1_to_hex(sha1)); + die("Can't load tree %s", oid_to_hex(oid)); } c = buf; @@ -1426,13 +1426,13 @@ static void load_tree(struct tree_entry *root) e->tree = NULL; c = get_mode(c, &e->versions[1].mode); if (!c) - die("Corrupt mode in %s", sha1_to_hex(sha1)); + die("Corrupt mode in %s", oid_to_hex(oid)); e->versions[0].mode = e->versions[1].mode; e->name = to_atom(c, strlen(c)); c += e->name->str_len + 1; hashcpy(e->versions[0].oid.hash, (unsigned char *)c); hashcpy(e->versions[1].oid.hash, (unsigned char *)c); - c += 20; + c += GIT_SHA1_RAWSZ; } free(buf); } @@ -1479,7 +1479,7 @@ static void mktree(struct tree_content *t, int v, struct strbuf *b) strbuf_addf(b, "%o %s%c", (unsigned int)(e->versions[v].mode & ~NO_DELTA), e->name->str_dat, '\0'); - strbuf_add(b, e->versions[v].oid.hash, 20); + strbuf_add(b, e->versions[v].oid.hash, GIT_SHA1_RAWSZ); } } @@ -1503,7 +1503,7 @@ static void store_tree(struct tree_entry *root) } if (!(root->versions[0].mode & NO_DELTA)) - le = find_object(root->versions[0].oid.hash); + le = find_object(&root->versions[0].oid); if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) { mktree(t, 0, &old_tree); lo.data = old_tree; @@ -1512,7 +1512,7 @@ static void store_tree(struct tree_entry *root) } mktree(t, 1, &new_tree); - store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].oid.hash, 0); + store_object(OBJ_TREE, &new_tree, &lo, &root->versions[1].oid, 0); t->delta_depth = lo.depth; for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { @@ -1531,14 +1531,14 @@ static void store_tree(struct tree_entry *root) static void tree_content_replace( struct tree_entry *root, - const unsigned char *sha1, + const struct object_id *oid, const uint16_t mode, struct tree_content *newtree) { if (!S_ISDIR(mode)) die("Root cannot be a non-directory"); oidclr(&root->versions[0].oid); - hashcpy(root->versions[1].oid.hash, sha1); + oidcpy(&root->versions[1].oid, oid); if (root->tree) release_tree_content_recursive(root->tree); root->tree = newtree; @@ -1547,7 +1547,7 @@ static void tree_content_replace( static int tree_content_set( struct tree_entry *root, const char *p, - const unsigned char *sha1, + const struct object_id *oid, const uint16_t mode, struct tree_content *subtree) { @@ -1572,10 +1572,10 @@ static int tree_content_set( if (!*slash1) { if (!S_ISDIR(mode) && e->versions[1].mode == mode - && !hashcmp(e->versions[1].oid.hash, sha1)) + && !oidcmp(&e->versions[1].oid, oid)) return 0; e->versions[1].mode = mode; - hashcpy(e->versions[1].oid.hash, sha1); + oidcpy(&e->versions[1].oid, oid); if (e->tree) release_tree_content_recursive(e->tree); e->tree = subtree; @@ -1605,7 +1605,7 @@ static int tree_content_set( } if (!e->tree) load_tree(e); - if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) { + if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) { oidclr(&root->versions[1].oid); return 1; } @@ -1623,11 +1623,11 @@ static int tree_content_set( if (*slash1) { e->tree = new_tree_content(8); e->versions[1].mode = S_IFDIR; - tree_content_set(e, slash1 + 1, sha1, mode, subtree); + tree_content_set(e, slash1 + 1, oid, mode, subtree); } else { e->tree = subtree; e->versions[1].mode = mode; - hashcpy(e->versions[1].oid.hash, sha1); + oidcpy(&e->versions[1].oid, oid); } oidclr(&root->versions[1].oid); return 1; @@ -1750,7 +1750,7 @@ static int update_branch(struct branch *b) { static const char *msg = "fast-import"; struct ref_transaction *transaction; - unsigned char old_sha1[20]; + struct object_id old_oid; struct strbuf err = STRBUF_INIT; if (is_null_oid(&b->oid)) { @@ -1758,12 +1758,12 @@ static int update_branch(struct branch *b) delete_ref(NULL, b->name, NULL, 0); return 0; } - if (read_ref(b->name, old_sha1)) - hashclr(old_sha1); - if (!force_update && !is_null_sha1(old_sha1)) { + if (read_ref(b->name, old_oid.hash)) + oidclr(&old_oid); + if (!force_update && !is_null_oid(&old_oid)) { struct commit *old_cmit, *new_cmit; - old_cmit = lookup_commit_reference_gently(old_sha1, 0); + old_cmit = lookup_commit_reference_gently(old_oid.hash, 0); new_cmit = lookup_commit_reference_gently(b->oid.hash, 0); if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); @@ -1772,13 +1772,13 @@ static int update_branch(struct branch *b) warning("Not updating %s" " (new tip %s does not contain %s)", b->name, oid_to_hex(&b->oid), - sha1_to_hex(old_sha1)); + oid_to_hex(&old_oid)); return -1; } } transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, b->name, b->oid.hash, old_sha1, + ref_transaction_update(transaction, b->name, b->oid.hash, old_oid.hash, 0, msg, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@ -1898,7 +1898,7 @@ static void read_marks(void) while (fgets(line, sizeof(line), f)) { uintmax_t mark; char *end; - unsigned char sha1[20]; + struct object_id oid; struct object_entry *e; end = strchr(line, '\n'); @@ -1907,14 +1907,14 @@ static void read_marks(void) *end = 0; mark = strtoumax(line + 1, &end, 10); if (!mark || end == line + 1 - || *end != ' ' || get_sha1_hex(end + 1, sha1)) + || *end != ' ' || get_oid_hex(end + 1, &oid)) die("corrupt mark line: %s", line); - e = find_object(sha1); + e = find_object(&oid); if (!e) { - enum object_type type = sha1_object_info(sha1, NULL); + enum object_type type = sha1_object_info(oid.hash, NULL); if (type < 0) - die("object not found: %s", sha1_to_hex(sha1)); - e = insert_object(sha1); + die("object not found: %s", oid_to_hex(&oid)); + e = insert_object(&oid); e->type = type; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@ -2122,21 +2122,21 @@ static char *parse_ident(const char *buf) static void parse_and_store_blob( struct last_object *last, - unsigned char *sha1out, + struct object_id *oidout, uintmax_t mark) { static struct strbuf buf = STRBUF_INIT; uintmax_t len; if (parse_data(&buf, big_file_threshold, &len)) - store_object(OBJ_BLOB, &buf, last, sha1out, mark); + store_object(OBJ_BLOB, &buf, last, oidout, mark); else { if (last) { strbuf_release(&last->data); last->offset = 0; last->depth = 0; } - stream_blob(len, sha1out, mark); + stream_blob(len, oidout, mark); skip_optional_lf(); } } @@ -2212,21 +2212,21 @@ static void construct_path_with_fanout(const char *hex_sha1, path[i++] = '/'; fanout--; } - memcpy(path + i, hex_sha1 + j, 40 - j); - path[i + 40 - j] = '\0'; + memcpy(path + i, hex_sha1 + j, GIT_SHA1_HEXSZ - j); + path[i + GIT_SHA1_HEXSZ - j] = '\0'; } static uintmax_t do_change_note_fanout( struct tree_entry *orig_root, struct tree_entry *root, - char *hex_sha1, unsigned int hex_sha1_len, + char *hex_oid, unsigned int hex_oid_len, char *fullpath, unsigned int fullpath_len, unsigned char fanout) { struct tree_content *t; struct tree_entry *e, leaf; - unsigned int i, tmp_hex_sha1_len, tmp_fullpath_len; + unsigned int i, tmp_hex_oid_len, tmp_fullpath_len; uintmax_t num_notes = 0; - unsigned char sha1[20]; + struct object_id oid; char realpath[60]; if (!root->tree) @@ -2235,7 +2235,7 @@ static uintmax_t do_change_note_fanout( for (i = 0; t && i < t->entry_count; i++) { e = t->entries[i]; - tmp_hex_sha1_len = hex_sha1_len + e->name->str_len; + tmp_hex_oid_len = hex_oid_len + e->name->str_len; tmp_fullpath_len = fullpath_len; /* @@ -2247,12 +2247,12 @@ static uintmax_t do_change_note_fanout( * of 2 chars. */ if (!e->versions[1].mode || - tmp_hex_sha1_len > 40 || + tmp_hex_oid_len > GIT_SHA1_HEXSZ || e->name->str_len % 2) continue; /* This _may_ be a note entry, or a subdir containing notes */ - memcpy(hex_sha1 + hex_sha1_len, e->name->str_dat, + memcpy(hex_oid + hex_oid_len, e->name->str_dat, e->name->str_len); if (tmp_fullpath_len) fullpath[tmp_fullpath_len++] = '/'; @@ -2261,14 +2261,14 @@ static uintmax_t do_change_note_fanout( tmp_fullpath_len += e->name->str_len; fullpath[tmp_fullpath_len] = '\0'; - if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) { + if (tmp_hex_oid_len == GIT_SHA1_HEXSZ && !get_oid_hex(hex_oid, &oid)) { /* This is a note entry */ if (fanout == 0xff) { /* Counting mode, no rename */ num_notes++; continue; } - construct_path_with_fanout(hex_sha1, fanout, realpath); + construct_path_with_fanout(hex_oid, fanout, realpath); if (!strcmp(fullpath, realpath)) { /* Note entry is in correct location */ num_notes++; @@ -2279,13 +2279,13 @@ static uintmax_t do_change_note_fanout( if (!tree_content_remove(orig_root, fullpath, &leaf, 0)) die("Failed to remove path %s", fullpath); tree_content_set(orig_root, realpath, - leaf.versions[1].oid.hash, + &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); } else if (S_ISDIR(e->versions[1].mode)) { /* This is a subdir that may contain note entries */ num_notes += do_change_note_fanout(orig_root, e, - hex_sha1, tmp_hex_sha1_len, + hex_oid, tmp_hex_oid_len, fullpath, tmp_fullpath_len, fanout); } @@ -2298,8 +2298,14 @@ static uintmax_t do_change_note_fanout( static uintmax_t change_note_fanout(struct tree_entry *root, unsigned char fanout) { - char hex_sha1[40], path[60]; - return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout); + /* + * The size of path is due to one slash between every two hex digits, + * plus the terminating NUL. Note that there is no slash at the end, so + * the number of slashes is one less than half the number of hex + * characters. + */ + char hex_oid[GIT_MAX_HEXSZ], path[GIT_MAX_HEXSZ + (GIT_MAX_HEXSZ / 2) - 1 + 1]; + return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout); } /* @@ -2360,7 +2366,7 @@ static void file_change_m(const char *p, struct branch *b) static struct strbuf uq = STRBUF_INIT; const char *endp; struct object_entry *oe; - unsigned char sha1[20]; + struct object_id oid; uint16_t mode, inline_data = 0; p = get_mode(p, &mode); @@ -2383,15 +2389,14 @@ static void file_change_m(const char *p, struct branch *b) if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); - hashcpy(sha1, oe->idx.sha1); + hashcpy(oid.hash, oe->idx.sha1); } else if (skip_prefix(p, "inline ", &p)) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ } else { - if (get_sha1_hex(p, sha1)) + if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); - oe = find_object(sha1); - p += 40; + oe = find_object(&oid); if (*p++ != ' ') die("Missing space after SHA1: %s", command_buf.buf); } @@ -2404,7 +2409,7 @@ static void file_change_m(const char *p, struct branch *b) } /* Git does not track empty, non-toplevel directories. */ - if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) { + if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *p) { tree_content_remove(&b->branch_tree, p, NULL, 0); return; } @@ -2431,12 +2436,12 @@ static void file_change_m(const char *p, struct branch *b) p = uq.buf; } read_next_command(); - parse_and_store_blob(&last_blob, sha1, 0); + parse_and_store_blob(&last_blob, &oid, 0); } else { enum object_type expected = S_ISDIR(mode) ? OBJ_TREE: OBJ_BLOB; enum object_type type = oe ? oe->type : - sha1_object_info(sha1, NULL); + sha1_object_info(oid.hash, NULL); if (type < 0) die("%s not found: %s", S_ISDIR(mode) ? "Tree" : "Blob", @@ -2448,10 +2453,10 @@ static void file_change_m(const char *p, struct branch *b) } if (!*p) { - tree_content_replace(&b->branch_tree, sha1, mode, NULL); + tree_content_replace(&b->branch_tree, &oid, mode, NULL); return; } - tree_content_set(&b->branch_tree, p, sha1, mode, NULL); + tree_content_set(&b->branch_tree, p, &oid, mode, NULL); } static void file_change_d(const char *p, struct branch *b) @@ -2509,13 +2514,13 @@ static void file_change_cr(const char *s, struct branch *b, int rename) die("Path %s not in branch", s); if (!*d) { /* C "path/to/subdir" "" */ tree_content_replace(&b->branch_tree, - leaf.versions[1].oid.hash, + &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); return; } tree_content_set(&b->branch_tree, d, - leaf.versions[1].oid.hash, + &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); } @@ -2525,7 +2530,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa static struct strbuf uq = STRBUF_INIT; struct object_entry *oe; struct branch *s; - unsigned char sha1[20], commit_sha1[20]; + struct object_id oid, commit_oid; char path[60]; uint16_t inline_data = 0; unsigned char new_fanout; @@ -2550,15 +2555,14 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa /* or 'inline' */ if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); - hashcpy(sha1, oe->idx.sha1); + hashcpy(oid.hash, oe->idx.sha1); } else if (skip_prefix(p, "inline ", &p)) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ } else { - if (get_sha1_hex(p, sha1)) + if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); - oe = find_object(sha1); - p += 40; + oe = find_object(&oid); if (*p++ != ' ') die("Missing space after SHA1: %s", command_buf.buf); } @@ -2568,17 +2572,17 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa if (s) { if (is_null_oid(&s->oid)) die("Can't add a note on empty branch."); - hashcpy(commit_sha1, s->oid.hash); + oidcpy(&commit_oid, &s->oid); } else if (*p == ':') { uintmax_t commit_mark = parse_mark_ref_eol(p); struct object_entry *commit_oe = find_mark(commit_mark); if (commit_oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", commit_mark); - hashcpy(commit_sha1, commit_oe->idx.sha1); - } else if (!get_sha1(p, commit_sha1)) { + hashcpy(commit_oid.hash, commit_oe->idx.sha1); + } else if (!get_oid(p, &commit_oid)) { unsigned long size; - char *buf = read_object_with_reference(commit_sha1, - commit_type, &size, commit_sha1); + char *buf = read_object_with_reference(commit_oid.hash, + commit_type, &size, commit_oid.hash); if (!buf || size < 46) die("Not a valid commit: %s", p); free(buf); @@ -2591,13 +2595,13 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa p = uq.buf; } read_next_command(); - parse_and_store_blob(&last_blob, sha1, 0); + parse_and_store_blob(&last_blob, &oid, 0); } else if (oe) { if (oe->type != OBJ_BLOB) die("Not a blob (actually a %s): %s", typename(oe->type), command_buf.buf); - } else if (!is_null_sha1(sha1)) { - enum object_type type = sha1_object_info(sha1, NULL); + } else if (!is_null_oid(&oid)) { + enum object_type type = sha1_object_info(oid.hash, NULL); if (type < 0) die("Blob not found: %s", command_buf.buf); if (type != OBJ_BLOB) @@ -2605,17 +2609,17 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa typename(type), command_buf.buf); } - construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path); + construct_path_with_fanout(oid_to_hex(&commit_oid), *old_fanout, path); if (tree_content_remove(&b->branch_tree, path, NULL, 0)) b->num_notes--; - if (is_null_sha1(sha1)) + if (is_null_oid(&oid)) return; /* nothing to insert */ b->num_notes++; new_fanout = convert_num_notes_to_fanout(b->num_notes); - construct_path_with_fanout(sha1_to_hex(commit_sha1), new_fanout, path); - tree_content_set(&b->branch_tree, path, sha1, S_IFREG | 0644, NULL); + construct_path_with_fanout(oid_to_hex(&commit_oid), new_fanout, path); + tree_content_set(&b->branch_tree, path, &oid, S_IFREG | 0644, NULL); } static void file_change_deleteall(struct branch *b) @@ -2629,10 +2633,10 @@ static void file_change_deleteall(struct branch *b) static void parse_from_commit(struct branch *b, char *buf, unsigned long size) { - if (!buf || size < 46) + if (!buf || size < GIT_SHA1_HEXSZ + 6) die("Not a valid commit: %s", oid_to_hex(&b->oid)); if (memcmp("tree ", buf, 5) - || get_sha1_hex(buf + 5, b->branch_tree.versions[1].oid.hash)) + || get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid)) die("The commit %s is corrupt", oid_to_hex(&b->oid)); oidcpy(&b->branch_tree.versions[0].oid, &b->branch_tree.versions[1].oid); @@ -2659,21 +2663,21 @@ static int parse_from(struct branch *b) { const char *from; struct branch *s; - unsigned char sha1[20]; + struct object_id oid; if (!skip_prefix(command_buf.buf, "from ", &from)) return 0; - hashcpy(sha1, b->branch_tree.versions[1].oid.hash); + oidcpy(&oid, &b->branch_tree.versions[1].oid); s = lookup_branch(from); if (b == s) die("Can't create a branch from itself: %s", b->name); else if (s) { - unsigned char *t = s->branch_tree.versions[1].oid.hash; + struct object_id *t = &s->branch_tree.versions[1].oid; oidcpy(&b->oid, &s->oid); - hashcpy(b->branch_tree.versions[0].oid.hash, t); - hashcpy(b->branch_tree.versions[1].oid.hash, t); + oidcpy(&b->branch_tree.versions[0].oid, t); + oidcpy(&b->branch_tree.versions[1].oid, t); } else if (*from == ':') { uintmax_t idnum = parse_mark_ref_eol(from); struct object_entry *oe = find_mark(idnum); @@ -2689,7 +2693,7 @@ static int parse_from(struct branch *b) } else parse_from_existing(b); } - } else if (!get_sha1(from, b->oid.hash)) { + } else if (!get_oid(from, &b->oid)) { parse_from_existing(b); if (is_null_oid(&b->oid)) b->delete = 1; @@ -2697,7 +2701,7 @@ static int parse_from(struct branch *b) else die("Invalid ref name or SHA1 expression: %s", from); - if (b->branch_tree.tree && hashcmp(sha1, b->branch_tree.versions[1].oid.hash)) { + if (b->branch_tree.tree && oidcmp(&oid, &b->branch_tree.versions[1].oid)) { release_tree_content_recursive(b->branch_tree.tree); b->branch_tree.tree = NULL; } @@ -2724,12 +2728,10 @@ static struct hash_list *parse_merge(unsigned int *count) if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); hashcpy(n->oid.hash, oe->idx.sha1); - } else if (!get_sha1(from, n->oid.hash)) { + } else if (!get_oid(from, &n->oid)) { unsigned long size; char *buf = read_object_with_reference(n->oid.hash, - commit_type, - &size, - n->oid.hash); + commit_type, &size, n->oid.hash); if (!buf || size < 46) die("Not a valid commit: %s", from); free(buf); @@ -2841,7 +2843,7 @@ static void parse_new_commit(const char *arg) free(author); free(committer); - if (!store_object(OBJ_COMMIT, &new_data, NULL, b->oid.hash, next_mark)) + if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark)) b->pack_id = pack_id; b->last_commit = object_count_by_type[OBJ_COMMIT]; } @@ -2854,7 +2856,7 @@ static void parse_new_tag(const char *arg) struct branch *s; struct tag *t; uintmax_t from_mark = 0; - unsigned char sha1[20]; + struct object_id oid; enum object_type type; const char *v; @@ -2875,18 +2877,18 @@ static void parse_new_tag(const char *arg) if (s) { if (is_null_oid(&s->oid)) die("Can't tag an empty branch."); - hashcpy(sha1, s->oid.hash); + oidcpy(&oid, &s->oid); type = OBJ_COMMIT; } else if (*from == ':') { struct object_entry *oe; from_mark = parse_mark_ref_eol(from); oe = find_mark(from_mark); type = oe->type; - hashcpy(sha1, oe->idx.sha1); - } else if (!get_sha1(from, sha1)) { - struct object_entry *oe = find_object(sha1); + hashcpy(oid.hash, oe->idx.sha1); + } else if (!get_oid(from, &oid)) { + struct object_entry *oe = find_object(&oid); if (!oe) { - type = sha1_object_info(sha1, NULL); + type = sha1_object_info(oid.hash, NULL); if (type < 0) die("Not a valid object: %s", from); } else @@ -2912,7 +2914,7 @@ static void parse_new_tag(const char *arg) "object %s\n" "type %s\n" "tag %s\n", - sha1_to_hex(sha1), typename(type), t->name); + oid_to_hex(&oid), typename(type), t->name); if (tagger) strbuf_addf(&new_data, "tagger %s\n", tagger); @@ -2920,7 +2922,7 @@ static void parse_new_tag(const char *arg) strbuf_addbuf(&new_data, &msg); free(tagger); - if (store_object(OBJ_TAG, &new_data, NULL, t->oid.hash, 0)) + if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, 0)) t->pack_id = MAX_PACK_ID; else t->pack_id = pack_id; @@ -2954,7 +2956,7 @@ static void cat_blob_write(const char *buf, unsigned long size) die_errno("Write to frontend failed"); } -static void cat_blob(struct object_entry *oe, unsigned char sha1[20]) +static void cat_blob(struct object_entry *oe, struct object_id *oid) { struct strbuf line = STRBUF_INIT; unsigned long size; @@ -2962,7 +2964,7 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20]) char *buf; if (!oe || oe->pack_id == MAX_PACK_ID) { - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid->hash, &type, &size); } else { type = oe->type; buf = gfi_unpack_entry(oe, &size); @@ -2973,19 +2975,19 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20]) */ if (type <= 0) { strbuf_reset(&line); - strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1)); + strbuf_addf(&line, "%s missing\n", oid_to_hex(oid)); cat_blob_write(line.buf, line.len); strbuf_release(&line); free(buf); return; } if (!buf) - die("Can't read object %s", sha1_to_hex(sha1)); + die("Can't read object %s", oid_to_hex(oid)); if (type != OBJ_BLOB) die("Object %s is a %s but a blob was expected.", - sha1_to_hex(sha1), typename(type)); + oid_to_hex(oid), typename(type)); strbuf_reset(&line); - strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1), + strbuf_addf(&line, "%s %s %lu\n", oid_to_hex(oid), typename(type), size); cat_blob_write(line.buf, line.len); strbuf_release(&line); @@ -3002,7 +3004,7 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20]) static void parse_get_mark(const char *p) { struct object_entry *oe = oe; - char output[42]; + char output[GIT_MAX_HEXSZ + 2]; /* get-mark SP LF */ if (*p != ':') @@ -3013,42 +3015,42 @@ static void parse_get_mark(const char *p) die("Unknown mark: %s", command_buf.buf); xsnprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1)); - cat_blob_write(output, 41); + cat_blob_write(output, GIT_SHA1_HEXSZ + 1); } static void parse_cat_blob(const char *p) { struct object_entry *oe = oe; - unsigned char sha1[20]; + struct object_id oid; /* cat-blob SP LF */ if (*p == ':') { oe = find_mark(parse_mark_ref_eol(p)); if (!oe) die("Unknown mark: %s", command_buf.buf); - hashcpy(sha1, oe->idx.sha1); + hashcpy(oid.hash, oe->idx.sha1); } else { - if (get_sha1_hex(p, sha1)) + if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); - if (p[40]) + if (*p) die("Garbage after SHA1: %s", command_buf.buf); - oe = find_object(sha1); + oe = find_object(&oid); } - cat_blob(oe, sha1); + cat_blob(oe, &oid); } static struct object_entry *dereference(struct object_entry *oe, - unsigned char sha1[20]) + struct object_id *oid) { unsigned long size; char *buf = NULL; if (!oe) { - enum object_type type = sha1_object_info(sha1, NULL); + enum object_type type = sha1_object_info(oid->hash, NULL); if (type < 0) - die("object not found: %s", sha1_to_hex(sha1)); + die("object not found: %s", oid_to_hex(oid)); /* cache it! */ - oe = insert_object(sha1); + oe = insert_object(oid); oe->type = type; oe->pack_id = MAX_PACK_ID; oe->idx.offset = 1; @@ -3067,49 +3069,48 @@ static struct object_entry *dereference(struct object_entry *oe, buf = gfi_unpack_entry(oe, &size); } else { enum object_type unused; - buf = read_sha1_file(sha1, &unused, &size); + buf = read_sha1_file(oid->hash, &unused, &size); } if (!buf) - die("Can't load object %s", sha1_to_hex(sha1)); + die("Can't load object %s", oid_to_hex(oid)); /* Peel one layer. */ switch (oe->type) { case OBJ_TAG: - if (size < 40 + strlen("object ") || - get_sha1_hex(buf + strlen("object "), sha1)) + if (size < GIT_SHA1_HEXSZ + strlen("object ") || + get_oid_hex(buf + strlen("object "), oid)) die("Invalid SHA1 in tag: %s", command_buf.buf); break; case OBJ_COMMIT: - if (size < 40 + strlen("tree ") || - get_sha1_hex(buf + strlen("tree "), sha1)) + if (size < GIT_SHA1_HEXSZ + strlen("tree ") || + get_oid_hex(buf + strlen("tree "), oid)) die("Invalid SHA1 in commit: %s", command_buf.buf); } free(buf); - return find_object(sha1); + return find_object(oid); } static struct object_entry *parse_treeish_dataref(const char **p) { - unsigned char sha1[20]; + struct object_id oid; struct object_entry *e; if (**p == ':') { /* */ e = find_mark(parse_mark_ref_space(p)); if (!e) die("Unknown mark: %s", command_buf.buf); - hashcpy(sha1, e->idx.sha1); + hashcpy(oid.hash, e->idx.sha1); } else { /* */ - if (get_sha1_hex(*p, sha1)) + if (parse_oid_hex(*p, &oid, p)) die("Invalid dataref: %s", command_buf.buf); - e = find_object(sha1); - *p += 40; + e = find_object(&oid); if (*(*p)++ != ' ') die("Missing space after tree-ish: %s", command_buf.buf); } while (!e || e->type != OBJ_TREE) - e = dereference(e, sha1); + e = dereference(e, &oid); return e; } From 71f35d5cbc0336ca5dca6b638c31d96611d301a7 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:09:57 +0000 Subject: [PATCH 136/566] submodule: convert merge_submodule to use struct object_id This is a caller of lookup_commit_reference, which we will convert later. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- merge-recursive.c | 8 ++++---- submodule.c | 24 ++++++++++++------------ submodule.h | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 9d6fd577e2..1315a45b93 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -994,11 +994,11 @@ static int merge_file_1(struct merge_options *o, return ret; result->clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result->clean = merge_submodule(result->oid.hash, + result->clean = merge_submodule(&result->oid, one->path, - one->oid.hash, - a->oid.hash, - b->oid.hash, + &one->oid, + &a->oid, + &b->oid, !o->call_depth); } else if (S_ISLNK(a->mode)) { oidcpy(&result->oid, &a->oid); diff --git a/submodule.c b/submodule.c index d3299e29c0..9bdd5f6055 100644 --- a/submodule.c +++ b/submodule.c @@ -1566,9 +1566,9 @@ static void print_commit(struct commit *commit) #define MERGE_WARNING(path, msg) \ warning("Failed to merge submodule %s (%s)", path, msg); -int merge_submodule(unsigned char result[20], const char *path, - const unsigned char base[20], const unsigned char a[20], - const unsigned char b[20], int search) +int merge_submodule(struct object_id *result, const char *path, + const struct object_id *base, const struct object_id *a, + const struct object_id *b, int search) { struct commit *commit_base, *commit_a, *commit_b; int parent_count; @@ -1577,14 +1577,14 @@ int merge_submodule(unsigned char result[20], const char *path, int i; /* store a in result in case we fail */ - hashcpy(result, a); + oidcpy(result, a); /* we can not handle deletion conflicts */ - if (is_null_sha1(base)) + if (is_null_oid(base)) return 0; - if (is_null_sha1(a)) + if (is_null_oid(a)) return 0; - if (is_null_sha1(b)) + if (is_null_oid(b)) return 0; if (add_submodule_odb(path)) { @@ -1592,9 +1592,9 @@ int merge_submodule(unsigned char result[20], const char *path, return 0; } - if (!(commit_base = lookup_commit_reference(base)) || - !(commit_a = lookup_commit_reference(a)) || - !(commit_b = lookup_commit_reference(b))) { + if (!(commit_base = lookup_commit_reference(base->hash)) || + !(commit_a = lookup_commit_reference(a->hash)) || + !(commit_b = lookup_commit_reference(b->hash))) { MERGE_WARNING(path, "commits not present"); return 0; } @@ -1608,11 +1608,11 @@ int merge_submodule(unsigned char result[20], const char *path, /* Case #1: a is contained in b or vice versa */ if (in_merge_bases(commit_a, commit_b)) { - hashcpy(result, b); + oidcpy(result, b); return 1; } if (in_merge_bases(commit_b, commit_a)) { - hashcpy(result, a); + oidcpy(result, a); return 1; } diff --git a/submodule.h b/submodule.h index 1277480add..89c2ed2191 100644 --- a/submodule.h +++ b/submodule.h @@ -84,10 +84,10 @@ extern int submodule_uses_gitfile(const char *path); #define SUBMODULE_REMOVAL_IGNORE_UNTRACKED (1<<1) #define SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED (1<<2) extern int bad_to_remove_submodule(const char *path, unsigned flags); -extern int merge_submodule(unsigned char result[20], const char *path, - const unsigned char base[20], - const unsigned char a[20], - const unsigned char b[20], int search); +extern int merge_submodule(struct object_id *result, const char *path, + const struct object_id *base, + const struct object_id *a, + const struct object_id *b, int search); extern int find_unpushed_submodules(struct oid_array *commits, const char *remotes_name, struct string_list *needs_pushing); From 569aa376ea2bb3f27f6248543f3df91c71e612d6 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:09:58 +0000 Subject: [PATCH 137/566] notes-cache: convert to struct object_id Convert as many instances of unsigned char [20] as possible. Update the callers of notes_cache_get and notes_cache_put to use the new interface. Among the functions updated are callers of lookup_commit_reference_gently, which we will soon convert. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- notes-cache.c | 29 ++++++++++++++--------------- notes-cache.h | 4 ++-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/diff.c b/diff.c index 11eef1c85d..3bd23ae4cd 100644 --- a/diff.c +++ b/diff.c @@ -5244,7 +5244,7 @@ size_t fill_textconv(struct userdiff_driver *driver, if (driver->textconv_cache && df->oid_valid) { *outbuf = notes_cache_get(driver->textconv_cache, - df->oid.hash, + &df->oid, &size); if (*outbuf) return size; @@ -5256,7 +5256,7 @@ size_t fill_textconv(struct userdiff_driver *driver, if (driver->textconv_cache && df->oid_valid) { /* ignore errors, as we might be in a readonly repository */ - notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf, + notes_cache_put(driver->textconv_cache, &df->oid, *outbuf, size); /* * we could save up changes and flush them all at the end, diff --git a/notes-cache.c b/notes-cache.c index 5dfc5cbd08..1cdd4984a4 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -5,16 +5,16 @@ static int notes_cache_match_validity(const char *ref, const char *validity) { - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; struct pretty_print_context pretty_ctx; struct strbuf msg = STRBUF_INIT; int ret; - if (read_ref(ref, sha1) < 0) + if (read_ref(ref, oid.hash) < 0) return 0; - commit = lookup_commit_reference_gently(sha1, 1); + commit = lookup_commit_reference_gently(oid.hash, 1); if (!commit) return 0; @@ -46,8 +46,7 @@ void notes_cache_init(struct notes_cache *c, const char *name, int notes_cache_write(struct notes_cache *c) { - unsigned char tree_sha1[20]; - unsigned char commit_sha1[20]; + struct object_id tree_oid, commit_oid; if (!c || !c->tree.initialized || !c->tree.update_ref || !*c->tree.update_ref) @@ -55,19 +54,19 @@ int notes_cache_write(struct notes_cache *c) if (!c->tree.dirty) return 0; - if (write_notes_tree(&c->tree, tree_sha1)) + if (write_notes_tree(&c->tree, tree_oid.hash)) return -1; - if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL, - commit_sha1, NULL, NULL) < 0) + if (commit_tree(c->validity, strlen(c->validity), tree_oid.hash, NULL, + commit_oid.hash, NULL, NULL) < 0) return -1; - if (update_ref("update notes cache", c->tree.update_ref, commit_sha1, + if (update_ref("update notes cache", c->tree.update_ref, commit_oid.hash, NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0) return -1; return 0; } -char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20], +char *notes_cache_get(struct notes_cache *c, struct object_id *key_oid, size_t *outsize) { const unsigned char *value_sha1; @@ -75,7 +74,7 @@ char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20], char *value; unsigned long size; - value_sha1 = get_note(&c->tree, key_sha1); + value_sha1 = get_note(&c->tree, key_oid->hash); if (!value_sha1) return NULL; value = read_sha1_file(value_sha1, &type, &size); @@ -84,12 +83,12 @@ char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20], return value; } -int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20], +int notes_cache_put(struct notes_cache *c, struct object_id *key_oid, const char *data, size_t size) { - unsigned char value_sha1[20]; + struct object_id value_oid; - if (write_sha1_file(data, size, "blob", value_sha1) < 0) + if (write_sha1_file(data, size, "blob", value_oid.hash) < 0) return -1; - return add_note(&c->tree, key_sha1, value_sha1, NULL); + return add_note(&c->tree, key_oid->hash, value_oid.hash, NULL); } diff --git a/notes-cache.h b/notes-cache.h index 356f88fb3c..aeeee8409d 100644 --- a/notes-cache.h +++ b/notes-cache.h @@ -12,9 +12,9 @@ void notes_cache_init(struct notes_cache *c, const char *name, const char *validity); int notes_cache_write(struct notes_cache *c); -char *notes_cache_get(struct notes_cache *c, unsigned char sha1[20], size_t +char *notes_cache_get(struct notes_cache *c, struct object_id *oid, size_t *outsize); -int notes_cache_put(struct notes_cache *c, unsigned char sha1[20], +int notes_cache_put(struct notes_cache *c, struct object_id *oid, const char *data, size_t size); #endif /* NOTES_CACHE_H */ From 9e31eafe7e98bab5013e598037e5581739fd4f42 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:09:59 +0000 Subject: [PATCH 138/566] parse-options-cb: convert to struct object_id This is a caller of lookup_commit_reference, which we will soon convert. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- parse-options-cb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parse-options-cb.c b/parse-options-cb.c index 7419780a9b..35a941fddc 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -80,14 +80,14 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg, int parse_opt_commits(const struct option *opt, const char *arg, int unset) { - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; if (!arg) return -1; - if (get_sha1(arg, sha1)) + if (get_oid(arg, &oid)) return error("malformed object name %s", arg); - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(oid.hash); if (!commit) return error("no such commit %s", arg); commit_list_insert(commit, opt->value); From 4322478a496a5b729a77792584e427d9e7132386 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:00 +0000 Subject: [PATCH 139/566] reflog_expire: convert to struct object_id Adjust the callback functions to take struct object_id * instead of unsigned char *, and modify related static functions accordingly. Introduce a temporary object_id instance into files_reflog_expire and copy the SHA-1 value passed in. This is necessary because the sha1 parameter can come indirectly from get_sha1. Without the temporary, it would require much more refactoring to be able to convert this function. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/reflog.c | 22 +++++++++++----------- refs.h | 6 +++--- refs/files-backend.c | 7 +++++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/builtin/reflog.c b/builtin/reflog.c index 7472775778..d6718d326c 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -186,13 +186,13 @@ static int commit_is_complete(struct commit *commit) return !is_incomplete; } -static int keep_entry(struct commit **it, unsigned char *sha1) +static int keep_entry(struct commit **it, struct object_id *oid) { struct commit *commit; - if (is_null_sha1(sha1)) + if (is_null_oid(oid)) return 1; - commit = lookup_commit_reference_gently(sha1, 1); + commit = lookup_commit_reference_gently(oid->hash, 1); if (!commit) return 0; @@ -251,17 +251,17 @@ static void mark_reachable(struct expire_reflog_policy_cb *cb) cb->mark_list = leftover; } -static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, unsigned char *sha1) +static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid) { /* * We may or may not have the commit yet - if not, look it * up using the supplied sha1. */ if (!commit) { - if (is_null_sha1(sha1)) + if (is_null_oid(oid)) return 0; - commit = lookup_commit_reference_gently(sha1, 1); + commit = lookup_commit_reference_gently(oid->hash, 1); /* Not a commit -- keep it */ if (!commit) @@ -283,7 +283,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit /* * Return true iff the specified reflog entry should be expired. */ -static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, +static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { @@ -295,13 +295,13 @@ static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, old = new = NULL; if (cb->cmd.stalefix && - (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1))) + (!keep_entry(&old, ooid) || !keep_entry(&new, noid))) return 1; if (timestamp < cb->cmd.expire_unreachable) { if (cb->unreachable_expire_kind == UE_ALWAYS) return 1; - if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1)) + if (unreachable(cb, old, ooid) || unreachable(cb, new, noid)) return 1; } @@ -326,7 +326,7 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid, } static void reflog_expiry_prepare(const char *refname, - const unsigned char *sha1, + const struct object_id *oid, void *cb_data) { struct expire_reflog_policy_cb *cb = cb_data; @@ -335,7 +335,7 @@ static void reflog_expiry_prepare(const char *refname, cb->tip_commit = NULL; cb->unreachable_expire_kind = UE_HEAD; } else { - cb->tip_commit = lookup_commit_reference_gently(sha1, 1); + cb->tip_commit = lookup_commit_reference_gently(oid->hash, 1); if (!cb->tip_commit) cb->unreachable_expire_kind = UE_ALWAYS; else diff --git a/refs.h b/refs.h index 07cf4cd41b..a22f696ddc 100644 --- a/refs.h +++ b/refs.h @@ -611,10 +611,10 @@ enum expire_reflog_flags { * unlocked again. */ typedef void reflog_expiry_prepare_fn(const char *refname, - const unsigned char *sha1, + const struct object_id *oid, void *cb_data); -typedef int reflog_expiry_should_prune_fn(unsigned char *osha1, - unsigned char *nsha1, +typedef int reflog_expiry_should_prune_fn(struct object_id *ooid, + struct object_id *noid, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data); diff --git a/refs/files-backend.c b/refs/files-backend.c index 83ea080e01..298a838c5e 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3207,7 +3207,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, if (cb->flags & EXPIRE_REFLOGS_REWRITE) ooid = &cb->last_kept_oid; - if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz, + if ((*cb->should_prune_fn)(ooid, noid, email, timestamp, tz, message, policy_cb)) { if (!cb->newlog) printf("would prune %s", message); @@ -3244,6 +3244,7 @@ static int files_reflog_expire(struct ref_store *ref_store, int status = 0; int type; struct strbuf err = STRBUF_INIT; + struct object_id oid; memset(&cb, 0, sizeof(cb)); cb.flags = flags; @@ -3293,7 +3294,9 @@ static int files_reflog_expire(struct ref_store *ref_store, } } - (*prepare_fn)(refname, sha1, cb.policy_cb); + hashcpy(oid.hash, sha1); + + (*prepare_fn)(refname, &oid, cb.policy_cb); refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb); (*cleanup_fn)(cb.policy_cb); From eee886cfb0de096a4b0144bba4fddaf1db0a4555 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:01 +0000 Subject: [PATCH 140/566] builtin/verify-commit: convert to struct object_id This is a prerequisite to convert to lookup_commit, which we will convert later. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/verify-commit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 38bedf8f9f..a5db1c427f 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -18,14 +18,14 @@ static const char * const verify_commit_usage[] = { NULL }; -static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, unsigned flags) +static int run_gpg_verify(const struct object_id *oid, const char *buf, unsigned long size, unsigned flags) { struct signature_check signature_check; int ret; memset(&signature_check, 0, sizeof(signature_check)); - ret = check_commit_signature(lookup_commit(sha1), &signature_check); + ret = check_commit_signature(lookup_commit(oid->hash), &signature_check); print_signature_buffer(&signature_check, flags); signature_check_clear(&signature_check); @@ -35,22 +35,22 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l static int verify_commit(const char *name, unsigned flags) { enum object_type type; - unsigned char sha1[20]; + struct object_id oid; char *buf; unsigned long size; int ret; - if (get_sha1(name, sha1)) + if (get_oid(name, &oid)) return error("commit '%s' not found.", name); - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid.hash, &type, &size); if (!buf) return error("%s: unable to read file.", name); if (type != OBJ_COMMIT) return error("%s: cannot verify a non-commit object of type %s.", name, typename(type)); - ret = run_gpg_verify(sha1, buf, size, flags); + ret = run_gpg_verify(&oid, buf, size, flags); free(buf); return ret; From 1e4085a05d7a486920afcd995bb0c1737d54c670 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:02 +0000 Subject: [PATCH 141/566] tag: convert parse_tag_buffer to struct object_id Specify some constants in terms of GIT_SHA1_HEXSZ, and convert a get_sha1_hex into parse_oid_hex to avoid needing to specify additional constants. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- tag.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tag.c b/tag.c index 243d1fdbbc..625f5cd71e 100644 --- a/tag.c +++ b/tag.c @@ -116,7 +116,7 @@ static unsigned long parse_tag_date(const char *buf, const char *tail) int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) { - unsigned char sha1[20]; + struct object_id oid; char type[20]; const char *bufptr = data; const char *tail = bufptr + size; @@ -126,11 +126,10 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) return 0; item->object.parsed = 1; - if (size < 64) + if (size < GIT_SHA1_HEXSZ + 24) return -1; - if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n') + if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n') return -1; - bufptr += 48; /* "object " + sha1 + "\n" */ if (!starts_with(bufptr, "type ")) return -1; @@ -143,13 +142,13 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) bufptr = nl + 1; if (!strcmp(type, blob_type)) { - item->tagged = &lookup_blob(sha1)->object; + item->tagged = &lookup_blob(oid.hash)->object; } else if (!strcmp(type, tree_type)) { - item->tagged = &lookup_tree(sha1)->object; + item->tagged = &lookup_tree(oid.hash)->object; } else if (!strcmp(type, commit_type)) { - item->tagged = &lookup_commit(sha1)->object; + item->tagged = &lookup_commit(oid.hash)->object; } else if (!strcmp(type, tag_type)) { - item->tagged = &lookup_tag(sha1)->object; + item->tagged = &lookup_tag(oid.hash)->object; } else { error("Unknown type %s", type); item->tagged = NULL; From 8eb9460045e44065a85c1dc735bbb8fcba776146 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:03 +0000 Subject: [PATCH 142/566] http-push: convert some static functions to struct object_id Among the functions converted is a caller of lookup_commit_or_die, which we will convert later on. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- http-push.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/http-push.c b/http-push.c index f0e3108f71..f3dd0a5606 100644 --- a/http-push.c +++ b/http-push.c @@ -1536,7 +1536,7 @@ static int remote_exists(const char *path) return ret; } -static void fetch_symref(const char *path, char **symref, unsigned char *sha1) +static void fetch_symref(const char *path, char **symref, struct object_id *oid) { char *url = xstrfmt("%s%s", repo->url, path); struct strbuf buffer = STRBUF_INIT; @@ -1549,7 +1549,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) free(*symref); *symref = NULL; - hashclr(sha1); + oidclr(oid); if (buffer.len == 0) return; @@ -1561,15 +1561,15 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) if (skip_prefix(buffer.buf, "ref: ", &name)) { *symref = xmemdupz(name, buffer.len - (name - buffer.buf)); } else { - get_sha1_hex(buffer.buf, sha1); + get_oid_hex(buffer.buf, oid); } strbuf_release(&buffer); } -static int verify_merge_base(unsigned char *head_sha1, struct ref *remote) +static int verify_merge_base(struct object_id *head_oid, struct ref *remote) { - struct commit *head = lookup_commit_or_die(head_sha1, "HEAD"); + struct commit *head = lookup_commit_or_die(head_oid->hash, "HEAD"); struct commit *branch = lookup_commit_or_die(remote->old_oid.hash, remote->name); return in_merge_bases(branch, head); @@ -1579,7 +1579,7 @@ static int delete_remote_branch(const char *pattern, int force) { struct ref *refs = remote_refs; struct ref *remote_ref = NULL; - unsigned char head_sha1[20]; + struct object_id head_oid; char *symref = NULL; int match; int patlen = strlen(pattern); @@ -1610,7 +1610,7 @@ static int delete_remote_branch(const char *pattern, int force) * Remote HEAD must be a symref (not exactly foolproof; a remote * symlink to a symref will look like a symref) */ - fetch_symref("HEAD", &symref, head_sha1); + fetch_symref("HEAD", &symref, &head_oid); if (!symref) return error("Remote HEAD is not a symref"); @@ -1619,7 +1619,7 @@ static int delete_remote_branch(const char *pattern, int force) if (!strcmp(remote_ref->name, symref)) return error("Remote branch %s is the current HEAD", remote_ref->name); - fetch_symref(symref, &symref, head_sha1); + fetch_symref(symref, &symref, &head_oid); } /* Run extra sanity checks if delete is not forced */ @@ -1627,10 +1627,10 @@ static int delete_remote_branch(const char *pattern, int force) /* Remote HEAD must resolve to a known object */ if (symref) return error("Remote HEAD symrefs too deep"); - if (is_null_sha1(head_sha1)) + if (is_null_oid(&head_oid)) return error("Unable to resolve remote HEAD"); - if (!has_sha1_file(head_sha1)) - return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", sha1_to_hex(head_sha1)); + if (!has_object_file(&head_oid)) + return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid)); /* Remote branch must resolve to a known object */ if (is_null_oid(&remote_ref->old_oid)) @@ -1640,7 +1640,7 @@ static int delete_remote_branch(const char *pattern, int force) return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid)); /* Remote branch must be an ancestor of remote HEAD */ - if (!verify_merge_base(head_sha1, remote_ref)) { + if (!verify_merge_base(&head_oid, remote_ref)) { return error("The branch '%s' is not an ancestor " "of your current HEAD.\n" "If you are sure you want to delete it," From 18b74e513d70830d4b30d0ef19ac225e1e081c73 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:04 +0000 Subject: [PATCH 143/566] notes-utils: convert internals to struct object_id Convert the internals of create_notes_comit and commit_notes to use struct object_id. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- notes-utils.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/notes-utils.c b/notes-utils.c index 24a33616a4..36c1490aa7 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -7,18 +7,18 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents, const char *msg, size_t msg_len, unsigned char *result_sha1) { - unsigned char tree_sha1[20]; + struct object_id tree_oid; assert(t->initialized); - if (write_notes_tree(t, tree_sha1)) + if (write_notes_tree(t, tree_oid.hash)) die("Failed to write notes tree to database"); if (!parents) { /* Deduce parent commit from t->ref */ - unsigned char parent_sha1[20]; - if (!read_ref(t->ref, parent_sha1)) { - struct commit *parent = lookup_commit(parent_sha1); + struct object_id parent_oid; + if (!read_ref(t->ref, parent_oid.hash)) { + struct commit *parent = lookup_commit(parent_oid.hash); if (parse_commit(parent)) die("Failed to find/parse commit %s", t->ref); commit_list_insert(parent, &parents); @@ -26,14 +26,14 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents, /* else: t->ref points to nothing, assume root/orphan commit */ } - if (commit_tree(msg, msg_len, tree_sha1, parents, result_sha1, NULL, NULL)) + if (commit_tree(msg, msg_len, tree_oid.hash, parents, result_sha1, NULL, NULL)) die("Failed to commit notes tree to database"); } void commit_notes(struct notes_tree *t, const char *msg) { struct strbuf buf = STRBUF_INIT; - unsigned char commit_sha1[20]; + struct object_id commit_oid; if (!t) t = &default_notes_tree; @@ -46,9 +46,9 @@ void commit_notes(struct notes_tree *t, const char *msg) strbuf_addstr(&buf, msg); strbuf_complete_line(&buf); - create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1); + create_notes_commit(t, NULL, buf.buf, buf.len, commit_oid.hash); strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */ - update_ref(buf.buf, t->update_ref, commit_sha1, NULL, 0, + update_ref(buf.buf, t->update_ref, commit_oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); strbuf_release(&buf); From 68ab61dd099a2ce9f700fa68b9b7cc7722304407 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:05 +0000 Subject: [PATCH 144/566] revision: convert prepare_show_merge to struct object_id This is a caller of lookup_commit_or_die, which we will convert later on. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- revision.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/revision.c b/revision.c index 2b56c3baf0..9453670341 100644 --- a/revision.c +++ b/revision.c @@ -1389,16 +1389,16 @@ static void prepare_show_merge(struct rev_info *revs) { struct commit_list *bases; struct commit *head, *other; - unsigned char sha1[20]; + struct object_id oid; const char **prune = NULL; int i, prune_num = 1; /* counting terminating NULL */ - if (get_sha1("HEAD", sha1)) + if (get_oid("HEAD", &oid)) die("--merge without HEAD?"); - head = lookup_commit_or_die(sha1, "HEAD"); - if (get_sha1("MERGE_HEAD", sha1)) + head = lookup_commit_or_die(oid.hash, "HEAD"); + if (get_oid("MERGE_HEAD", &oid)) die("--merge without MERGE_HEAD?"); - other = lookup_commit_or_die(sha1, "MERGE_HEAD"); + other = lookup_commit_or_die(oid.hash, "MERGE_HEAD"); add_pending_object(revs, &head->object, "HEAD"); add_pending_object(revs, &other->object, "MERGE_HEAD"); bases = get_merge_bases(head, other); From e92b848cb6a77172d2fbd2bda39a32e371d40eea Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:06 +0000 Subject: [PATCH 145/566] shallow: convert shallow registration functions to object_id Convert register_shallow and unregister_shallow to take struct object_id. register_shallow is a caller of lookup_commit, which we will convert later. It doesn't make sense for the registration and unregistration functions to have incompatible interfaces, so convert them both. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 6 +++--- builtin/receive-pack.c | 2 +- commit.c | 4 ++-- commit.h | 4 ++-- fetch-pack.c | 4 ++-- shallow.c | 12 ++++++------ upload-pack.c | 8 ++++---- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0fe35d1b5a..4770708063 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2777,10 +2777,10 @@ static void get_object_list(int ac, const char **av) continue; } if (starts_with(line, "--shallow ")) { - unsigned char sha1[20]; - if (get_sha1_hex(line + 10, sha1)) + struct object_id oid; + if (get_oid_hex(line + 10, &oid)) die("not an SHA-1 '%s'", line + 10); - register_shallow(sha1); + register_shallow(&oid); use_bitmap_index = 0; continue; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index f96834f42c..6f0f788b0c 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -858,7 +858,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si) * not lose these new roots.. */ for (i = 0; i < extra.nr; i++) - register_shallow(extra.oid[i].hash); + register_shallow(&extra.oid[i]); si->shallow_ref[cmd->index] = 0; oid_array_clear(&extra); diff --git a/commit.c b/commit.c index 73c78c2b80..ec41ba5e09 100644 --- a/commit.c +++ b/commit.c @@ -216,9 +216,9 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data) return ret; } -int unregister_shallow(const unsigned char *sha1) +int unregister_shallow(const struct object_id *oid) { - int pos = commit_graft_pos(sha1); + int pos = commit_graft_pos(oid->hash); if (pos < 0) return -1; if (pos + 1 < commit_graft_nr) diff --git a/commit.h b/commit.h index 7b1986d5c8..884177b8fa 100644 --- a/commit.h +++ b/commit.h @@ -263,8 +263,8 @@ extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct oid_array; struct ref; -extern int register_shallow(const unsigned char *sha1); -extern int unregister_shallow(const unsigned char *sha1); +extern int register_shallow(const struct object_id *oid); +extern int unregister_shallow(const struct object_id *oid); extern int for_each_commit_graft(each_commit_graft_fn, void *); extern int is_repository_shallow(void); extern struct commit_list *get_shallow_commits(struct object_array *heads, diff --git a/fetch-pack.c b/fetch-pack.c index b42d01f422..d455ef97af 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -417,7 +417,7 @@ static int find_common(struct fetch_pack_args *args, if (skip_prefix(line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), line); - register_shallow(oid.hash); + register_shallow(&oid); continue; } if (skip_prefix(line, "unshallow ", &arg)) { @@ -428,7 +428,7 @@ static int find_common(struct fetch_pack_args *args, /* make sure that it is parsed as shallow */ if (!parse_object(oid.hash)) die(_("error in object: %s"), line); - if (unregister_shallow(oid.hash)) + if (unregister_shallow(&oid)) die(_("no shallow found: %s"), line); continue; } diff --git a/shallow.c b/shallow.c index 25b6db989b..c520ae3aea 100644 --- a/shallow.c +++ b/shallow.c @@ -27,13 +27,13 @@ void set_alternate_shallow_file(const char *path, int override) alternate_shallow_file = xstrdup_or_null(path); } -int register_shallow(const unsigned char *sha1) +int register_shallow(const struct object_id *oid) { struct commit_graft *graft = xmalloc(sizeof(struct commit_graft)); - struct commit *commit = lookup_commit(sha1); + struct commit *commit = lookup_commit(oid->hash); - hashcpy(graft->oid.hash, sha1); + oidcpy(&graft->oid, oid); graft->nr_parent = -1; if (commit && commit->object.parsed) commit->parents = NULL; @@ -65,10 +65,10 @@ int is_repository_shallow(void) is_shallow = 1; while (fgets(buf, sizeof(buf), fp)) { - unsigned char sha1[20]; - if (get_sha1_hex(buf, sha1)) + struct object_id oid; + if (get_oid_hex(buf, &oid)) die("bad shallow line: %s", buf); - register_shallow(sha1); + register_shallow(&oid); } fclose(fp); return is_shallow; diff --git a/upload-pack.c b/upload-pack.c index ffb028d623..20f87cd383 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -642,7 +642,7 @@ static void send_shallow(struct commit_list *result) if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { packet_write_fmt(1, "shallow %s", oid_to_hex(&object->oid)); - register_shallow(object->oid.hash); + register_shallow(&object->oid); shallow_nr++; } result = result->next; @@ -667,7 +667,7 @@ static void send_unshallow(const struct object_array *shallows) * parse and add the parents to the want list, then * re-register it. */ - unregister_shallow(object->oid.hash); + unregister_shallow(&object->oid); object->parsed = 0; parse_commit_or_die((struct commit *)object); parents = ((struct commit *)object)->parents; @@ -679,7 +679,7 @@ static void send_unshallow(const struct object_array *shallows) add_object_array(object, NULL, &extra_edge_obj); } /* make sure commit traversal conforms to client */ - register_shallow(object->oid.hash); + register_shallow(&object->oid); } } @@ -883,7 +883,7 @@ static void receive_needs(void) if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) - register_shallow(shallows.objects[i].item->oid.hash); + register_shallow(&shallows.objects[i].item->oid); } shallow_nr += shallows.nr; From 33d66df34ece4f597590263df502ff3578b6becd Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:07 +0000 Subject: [PATCH 146/566] sequencer: convert some functions to struct object_id Convert update_squash_messages and is_index_unchanged to struct object_id. These are callers of lookup_commit and lookup_commit_reference, which we want to convert. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- sequencer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sequencer.c b/sequencer.c index b0f862b7bb..b94830cf9e 100644 --- a/sequencer.c +++ b/sequencer.c @@ -482,13 +482,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next, static int is_index_unchanged(void) { - unsigned char head_sha1[20]; + struct object_id head_oid; struct commit *head_commit; - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL)) + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) return error(_("could not resolve HEAD commit\n")); - head_commit = lookup_commit(head_sha1); + head_commit = lookup_commit(head_oid.hash); /* * If head_commit is NULL, check_commit, called from @@ -835,13 +835,13 @@ static int update_squash_messages(enum todo_command command, strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len); strbuf_release(&header); } else { - unsigned char head[20]; + struct object_id head; struct commit *head_commit; const char *head_message, *body; - if (get_sha1("HEAD", head)) + if (get_oid("HEAD", &head)) return error(_("need a HEAD to fixup")); - if (!(head_commit = lookup_commit_reference(head))) + if (!(head_commit = lookup_commit_reference(head.hash))) return error(_("could not read HEAD")); if (!(head_message = get_commit_buffer(head_commit, NULL))) return error(_("could not read HEAD's commit message")); From 7422ab50d1011b2b26b59bf11b91a1202618f3e5 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:08 +0000 Subject: [PATCH 147/566] builtin/tag: convert to struct object_id Parts of this module call lookup_commit_reference, which we want to convert. The module is small and mostly self-contained, so convert the rest of it while we're at it. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/tag.c | 66 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/builtin/tag.c b/builtin/tag.c index 222404522f..597c925e38 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -66,7 +66,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con } typedef int (*each_tag_name_fn)(const char *name, const char *ref, - const unsigned char *sha1, const void *cb_data); + const struct object_id *oid, const void *cb_data); static int for_each_tag_name(const char **argv, each_tag_name_fn fn, const void *cb_data) @@ -74,17 +74,17 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, const char **p; struct strbuf ref = STRBUF_INIT; int had_error = 0; - unsigned char sha1[20]; + struct object_id oid; for (p = argv; *p; p++) { strbuf_reset(&ref); strbuf_addf(&ref, "refs/tags/%s", *p); - if (read_ref(ref.buf, sha1)) { + if (read_ref(ref.buf, oid.hash)) { error(_("tag '%s' not found."), *p); had_error = 1; continue; } - if (fn(*p, ref.buf, sha1, cb_data)) + if (fn(*p, ref.buf, &oid, cb_data)) had_error = 1; } strbuf_release(&ref); @@ -92,16 +92,16 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, } static int delete_tag(const char *name, const char *ref, - const unsigned char *sha1, const void *cb_data) + const struct object_id *oid, const void *cb_data) { - if (delete_ref(NULL, ref, sha1, 0)) + if (delete_ref(NULL, ref, oid->hash, 0)) return 1; - printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV)); + printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV)); return 0; } static int verify_tag(const char *name, const char *ref, - const unsigned char *sha1, const void *cb_data) + const struct object_id *oid, const void *cb_data) { int flags; const char *fmt_pretty = cb_data; @@ -110,11 +110,11 @@ static int verify_tag(const char *name, const char *ref, if (fmt_pretty) flags = GPG_VERIFY_OMIT_STATUS; - if (gpg_verify_tag(sha1, name, flags)) + if (gpg_verify_tag(oid->hash, name, flags)) return -1; if (fmt_pretty) - pretty_print_ref(name, sha1, fmt_pretty); + pretty_print_ref(name, oid->hash, fmt_pretty); return 0; } @@ -182,13 +182,13 @@ static int git_tag_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static void write_tag_body(int fd, const unsigned char *sha1) +static void write_tag_body(int fd, const struct object_id *oid) { unsigned long size; enum object_type type; char *buf, *sp; - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid->hash, &type, &size); if (!buf) return; /* skip header */ @@ -204,11 +204,11 @@ static void write_tag_body(int fd, const unsigned char *sha1) free(buf); } -static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result) +static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result) { if (sign && do_sign(buf) < 0) return error(_("unable to sign the tag")); - if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0) + if (write_sha1_file(buf->buf, buf->len, tag_type, result->hash) < 0) return error(_("unable to write tag file")); return 0; } @@ -223,15 +223,15 @@ struct create_tag_options { } cleanup_mode; }; -static void create_tag(const unsigned char *object, const char *tag, +static void create_tag(const struct object_id *object, const char *tag, struct strbuf *buf, struct create_tag_options *opt, - unsigned char *prev, unsigned char *result) + struct object_id *prev, struct object_id *result) { enum object_type type; struct strbuf header = STRBUF_INIT; char *path = NULL; - type = sha1_object_info(object, NULL); + type = sha1_object_info(object->hash, NULL); if (type <= OBJ_NONE) die(_("bad object type.")); @@ -240,7 +240,7 @@ static void create_tag(const unsigned char *object, const char *tag, "type %s\n" "tag %s\n" "tagger %s\n\n", - sha1_to_hex(object), + oid_to_hex(object), typename(type), tag, git_committer_info(IDENT_STRICT)); @@ -254,7 +254,7 @@ static void create_tag(const unsigned char *object, const char *tag, if (fd < 0) die_errno(_("could not create file '%s'"), path); - if (!is_null_sha1(prev)) { + if (!is_null_oid(prev)) { write_tag_body(fd, prev); } else { struct strbuf buf = STRBUF_INIT; @@ -296,7 +296,7 @@ static void create_tag(const unsigned char *object, const char *tag, } } -static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb) +static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) { enum object_type type; struct commit *c; @@ -310,17 +310,17 @@ static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb) strbuf_addstr(sb, rla); } else { strbuf_addstr(sb, _("tag: tagging ")); - strbuf_add_unique_abbrev(sb, sha1, DEFAULT_ABBREV); + strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV); } strbuf_addstr(sb, " ("); - type = sha1_object_info(sha1, NULL); + type = sha1_object_info(oid->hash, NULL); switch (type) { default: strbuf_addstr(sb, _("object of unknown type")); break; case OBJ_COMMIT: - if ((buf = read_sha1_file(sha1, &type, &size)) != NULL) { + if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) { subject_len = find_commit_subject(buf, &subject_start); strbuf_insert(sb, sb->len, subject_start, subject_len); } else { @@ -328,7 +328,7 @@ static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb) } free(buf); - if ((c = lookup_commit_reference(sha1)) != NULL) + if ((c = lookup_commit_reference(oid->hash)) != NULL) strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT))); break; case OBJ_TREE: @@ -378,7 +378,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct strbuf buf = STRBUF_INIT; struct strbuf ref = STRBUF_INIT; struct strbuf reflog_msg = STRBUF_INIT; - unsigned char object[20], prev[20]; + struct object_id object, prev; const char *object_ref, *tag; struct create_tag_options opt; char *cleanup_arg = NULL; @@ -528,14 +528,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (argc > 2) die(_("too many params")); - if (get_sha1(object_ref, object)) + if (get_oid(object_ref, &object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); if (strbuf_check_tag_ref(&ref, tag)) die(_("'%s' is not a valid tag name."), tag); - if (read_ref(ref.buf, prev)) - hashclr(prev); + if (read_ref(ref.buf, prev.hash)) + oidclr(&prev); else if (!force) die(_("tag '%s' already exists"), tag); @@ -550,24 +550,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix) else die(_("Invalid cleanup mode %s"), cleanup_arg); - create_reflog_msg(object, &reflog_msg); + create_reflog_msg(&object, &reflog_msg); if (create_tag_object) { if (force_sign_annotate && !annotate) opt.sign = 1; - create_tag(object, tag, &buf, &opt, prev, object); + create_tag(&object, tag, &buf, &opt, &prev, &object); } transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, ref.buf, object, prev, + ref_transaction_update(transaction, ref.buf, object.hash, prev.hash, create_reflog ? REF_FORCE_CREATE_REFLOG : 0, reflog_msg.buf, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); ref_transaction_free(transaction); - if (force && !is_null_sha1(prev) && hashcmp(prev, object)) - printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV)); + if (force && !is_null_oid(&prev) && oidcmp(&prev, &object)) + printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV)); strbuf_release(&err); strbuf_release(&buf); From 1e43ed986775d8e8ecaef4dac8b98dcbae6298c1 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:09 +0000 Subject: [PATCH 148/566] Convert remaining callers of lookup_commit_reference* to object_id There are a small number of remaining callers of lookup_commit_reference and lookup_commit_reference_gently that still need to be converted to struct object_id. Convert these. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- notes-merge.c | 26 +++++++++++++------------- ref-filter.c | 6 +++--- sequencer.c | 20 ++++++++++---------- sha1_name.c | 12 ++++++------ shallow.c | 6 +++--- submodule.c | 8 ++++---- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/notes-merge.c b/notes-merge.c index 32caaaff74..06d8be9cbf 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -535,7 +535,7 @@ int notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, unsigned char *result_sha1) { - unsigned char local_sha1[20], remote_sha1[20]; + struct object_id local_oid, remote_oid; struct commit *local, *remote; struct commit_list *bases = NULL; const unsigned char *base_sha1, *base_tree_sha1; @@ -549,46 +549,46 @@ int notes_merge(struct notes_merge_options *o, o->local_ref, o->remote_ref); /* Dereference o->local_ref into local_sha1 */ - if (read_ref_full(o->local_ref, 0, local_sha1, NULL)) + if (read_ref_full(o->local_ref, 0, local_oid.hash, NULL)) die("Failed to resolve local notes ref '%s'", o->local_ref); else if (!check_refname_format(o->local_ref, 0) && - is_null_sha1(local_sha1)) + is_null_oid(&local_oid)) local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ - else if (!(local = lookup_commit_reference(local_sha1))) + else if (!(local = lookup_commit_reference(local_oid.hash))) die("Could not parse local commit %s (%s)", - sha1_to_hex(local_sha1), o->local_ref); - trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1)); + oid_to_hex(&local_oid), o->local_ref); + trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid)); /* Dereference o->remote_ref into remote_sha1 */ - if (get_sha1(o->remote_ref, remote_sha1)) { + if (get_oid(o->remote_ref, &remote_oid)) { /* * Failed to get remote_sha1. If o->remote_ref looks like an * unborn ref, perform the merge using an empty notes tree. */ if (!check_refname_format(o->remote_ref, 0)) { - hashclr(remote_sha1); + oidclr(&remote_oid); remote = NULL; } else { die("Failed to resolve remote notes ref '%s'", o->remote_ref); } - } else if (!(remote = lookup_commit_reference(remote_sha1))) { + } else if (!(remote = lookup_commit_reference(remote_oid.hash))) { die("Could not parse remote commit %s (%s)", - sha1_to_hex(remote_sha1), o->remote_ref); + oid_to_hex(&remote_oid), o->remote_ref); } - trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1)); + trace_printf("\tremote commit: %.7s\n", oid_to_hex(&remote_oid)); if (!local && !remote) die("Cannot merge empty notes ref (%s) into empty notes ref " "(%s)", o->remote_ref, o->local_ref); if (!local) { /* result == remote commit */ - hashcpy(result_sha1, remote_sha1); + hashcpy(result_sha1, remote_oid.hash); goto found_result; } if (!remote) { /* result == local commit */ - hashcpy(result_sha1, local_sha1); + hashcpy(result_sha1, local_oid.hash); goto found_result; } assert(local && remote); diff --git a/ref-filter.c b/ref-filter.c index 3a640448fd..47cce0a184 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2090,7 +2090,7 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset) int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) { struct ref_filter *rf = opt->value; - unsigned char sha1[20]; + struct object_id oid; int no_merged = starts_with(opt->long_name, "no"); if (rf->merge) { @@ -2105,10 +2105,10 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) ? REF_FILTER_MERGED_OMIT : REF_FILTER_MERGED_INCLUDE; - if (get_sha1(arg, sha1)) + if (get_oid(arg, &oid)) die(_("malformed object name %s"), arg); - rf->merge_commit = lookup_commit_reference_gently(sha1, 0); + rf->merge_commit = lookup_commit_reference_gently(oid.hash, 0); if (!rf->merge_commit) return opterror(opt, "must point to a commit", 0); diff --git a/sequencer.c b/sequencer.c index b94830cf9e..e44c015b2c 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1222,7 +1222,7 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list) static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) { - unsigned char commit_sha1[20]; + struct object_id commit_oid; char *end_of_object_name; int i, saved, status, padding; @@ -1271,7 +1271,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) end_of_object_name = (char *) bol + strcspn(bol, " \t\n"); saved = *end_of_object_name; *end_of_object_name = '\0'; - status = get_sha1(bol, commit_sha1); + status = get_oid(bol, &commit_oid); *end_of_object_name = saved; item->arg = end_of_object_name + strspn(end_of_object_name, " \t"); @@ -1280,7 +1280,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) if (status < 0) return -1; - item->commit = lookup_commit_reference(commit_sha1); + item->commit = lookup_commit_reference(commit_oid.hash); return !item->commit; } @@ -2281,7 +2281,7 @@ static int single_pick(struct commit *cmit, struct replay_opts *opts) int sequencer_pick_revisions(struct replay_opts *opts) { struct todo_list todo_list = TODO_LIST_INIT; - unsigned char sha1[20]; + struct object_id oid; int i, res; assert(opts->revs); @@ -2289,16 +2289,16 @@ int sequencer_pick_revisions(struct replay_opts *opts) return -1; for (i = 0; i < opts->revs->pending.nr; i++) { - unsigned char sha1[20]; + struct object_id oid; const char *name = opts->revs->pending.objects[i].name; /* This happens when using --stdin. */ if (!strlen(name)) continue; - if (!get_sha1(name, sha1)) { - if (!lookup_commit_reference_gently(sha1, 1)) { - enum object_type type = sha1_object_info(sha1, NULL); + if (!get_oid(name, &oid)) { + if (!lookup_commit_reference_gently(oid.hash, 1)) { + enum object_type type = sha1_object_info(oid.hash, NULL); return error(_("%s: can't cherry-pick a %s"), name, typename(type)); } @@ -2335,9 +2335,9 @@ int sequencer_pick_revisions(struct replay_opts *opts) if (walk_revs_populate_todo(&todo_list, opts) || create_seq_dir() < 0) return -1; - if (get_sha1("HEAD", sha1) && (opts->action == REPLAY_REVERT)) + if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT)) return error(_("can't revert as initial commit")); - if (save_head(sha1_to_hex(sha1))) + if (save_head(oid_to_hex(&oid))) return -1; if (save_opts(opts)) return -1; diff --git a/sha1_name.c b/sha1_name.c index 8eec9f7c1b..8889190a90 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -722,14 +722,14 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1, static int get_parent(const char *name, int len, unsigned char *result, int idx) { - unsigned char sha1[20]; - int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH); + struct object_id oid; + int ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH); struct commit *commit; struct commit_list *p; if (ret) return ret; - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(oid.hash); if (parse_commit(commit)) return -1; if (!idx) { @@ -750,14 +750,14 @@ static int get_parent(const char *name, int len, static int get_nth_ancestor(const char *name, int len, unsigned char *result, int generation) { - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; int ret; - ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH); + ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH); if (ret) return ret; - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(oid.hash); if (!commit) return -1; diff --git a/shallow.c b/shallow.c index c520ae3aea..1327ee16fc 100644 --- a/shallow.c +++ b/shallow.c @@ -466,7 +466,7 @@ static uint32_t *paint_alloc(struct paint_info *info) * UNINTERESTING or BOTTOM is hit. Set the id-th bit in ref_bitmap for * all walked commits. */ -static void paint_down(struct paint_info *info, const unsigned char *sha1, +static void paint_down(struct paint_info *info, const struct object_id *oid, unsigned int id) { unsigned int i, nr; @@ -475,7 +475,7 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1, size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr); uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */ uint32_t *bitmap = paint_alloc(info); - struct commit *c = lookup_commit_reference_gently(sha1, 1); + struct commit *c = lookup_commit_reference_gently(oid->hash, 1); if (!c) return; memset(bitmap, 0, bitmap_size); @@ -604,7 +604,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info, } for (i = 0; i < ref->nr; i++) - paint_down(&pi, ref->oid[i].hash, i); + paint_down(&pi, ref->oid + i, i); if (used) { int bitmap_size = ((pi.nr_bits + 31) / 32) * sizeof(uint32_t); diff --git a/submodule.c b/submodule.c index 9bdd5f6055..d5c28b9f1b 100644 --- a/submodule.c +++ b/submodule.c @@ -896,17 +896,17 @@ int push_unpushed_submodules(struct oid_array *commits, return ret; } -static int is_submodule_commit_present(const char *path, unsigned char sha1[20]) +static int is_submodule_commit_present(const char *path, struct object_id *oid) { int is_present = 0; - if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) { + if (!add_submodule_odb(path) && lookup_commit_reference(oid->hash)) { /* Even if the submodule is checked out and the commit is * present, make sure it is reachable from a ref. */ struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL}; struct strbuf buf = STRBUF_INIT; - argv[3] = sha1_to_hex(sha1); + argv[3] = oid_to_hex(oid); cp.argv = argv; prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; @@ -937,7 +937,7 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q, * being moved around. */ struct string_list_item *path; path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path); - if (!path && !is_submodule_commit_present(p->two->path, p->two->oid.hash)) + if (!path && !is_submodule_commit_present(p->two->path, &p->two->oid)) string_list_append(&changed_submodule_paths, xstrdup(p->two->path)); } else { /* Submodule is new or was moved here */ From bc83266abe36905cade4719cbaeb8a62d0a382da Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:10 +0000 Subject: [PATCH 149/566] Convert lookup_commit* to struct object_id Convert lookup_commit, lookup_commit_or_die, lookup_commit_reference, and lookup_commit_reference_gently to take struct object_id arguments. Introduce a temporary in parse_object buffer in order to convert this function. This is required since in order to convert parse_object and parse_object_buffer, lookup_commit_reference_gently and lookup_commit_or_die would need to be converted. Not introducing a temporary would therefore require that lookup_commit_or_die take a struct object_id *, but lookup_commit would take unsigned char *, leaving a confusing and hard-to-use interface. parse_object_buffer will lose this temporary in a later patch. This commit was created with manual changes to commit.c, commit.h, and object.c, plus the following semantic patch: @@ expression E1, E2; @@ - lookup_commit_reference_gently(E1.hash, E2) + lookup_commit_reference_gently(&E1, E2) @@ expression E1, E2; @@ - lookup_commit_reference_gently(E1->hash, E2) + lookup_commit_reference_gently(E1, E2) @@ expression E1; @@ - lookup_commit_reference(E1.hash) + lookup_commit_reference(&E1) @@ expression E1; @@ - lookup_commit_reference(E1->hash) + lookup_commit_reference(E1) @@ expression E1; @@ - lookup_commit(E1.hash) + lookup_commit(&E1) @@ expression E1; @@ - lookup_commit(E1->hash) + lookup_commit(E1) @@ expression E1, E2; @@ - lookup_commit_or_die(E1.hash, E2) + lookup_commit_or_die(&E1, E2) @@ expression E1, E2; @@ - lookup_commit_or_die(E1->hash, E2) + lookup_commit_or_die(E1, E2) Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- archive.c | 2 +- bisect.c | 2 +- branch.c | 2 +- builtin/am.c | 4 ++-- builtin/blame.c | 4 ++-- builtin/branch.c | 6 +++--- builtin/checkout.c | 6 +++--- builtin/clone.c | 2 +- builtin/commit-tree.c | 2 +- builtin/commit.c | 4 ++-- builtin/describe.c | 4 ++-- builtin/diff-tree.c | 4 ++-- builtin/fast-export.c | 2 +- builtin/fetch.c | 7 ++++--- builtin/fmt-merge-msg.c | 4 ++-- builtin/log.c | 8 ++++---- builtin/merge-base.c | 6 +++--- builtin/merge.c | 2 +- builtin/notes.c | 2 +- builtin/pull.c | 10 +++++----- builtin/reflog.c | 8 ++++---- builtin/replace.c | 4 ++-- builtin/reset.c | 4 ++-- builtin/rev-parse.c | 6 +++--- builtin/show-branch.c | 4 ++-- builtin/tag.c | 2 +- builtin/verify-commit.c | 2 +- bundle.c | 2 +- commit.c | 30 +++++++++++++++--------------- commit.h | 12 ++++++------ fast-import.c | 4 ++-- fetch-pack.c | 2 +- http-push.c | 5 +++-- log-tree.c | 2 +- notes-cache.c | 2 +- notes-merge.c | 4 ++-- notes-utils.c | 2 +- object.c | 5 ++++- parse-options-cb.c | 2 +- ref-filter.c | 4 ++-- remote.c | 13 +++++++------ revision.c | 8 ++++---- sequencer.c | 8 ++++---- sha1_name.c | 10 +++++----- shallow.c | 20 ++++++++++---------- submodule.c | 14 +++++++------- tag.c | 2 +- tree.c | 2 +- walker.c | 2 +- wt-status.c | 2 +- 50 files changed, 138 insertions(+), 132 deletions(-) diff --git a/archive.c b/archive.c index 60b8891986..54701e8bb2 100644 --- a/archive.c +++ b/archive.c @@ -360,7 +360,7 @@ static void parse_treeish_arg(const char **argv, if (get_sha1(name, oid.hash)) die("Not a valid object name"); - commit = lookup_commit_reference_gently(oid.hash, 1); + commit = lookup_commit_reference_gently(&oid, 1); if (commit) { commit_sha1 = commit->object.oid.hash; archive_time = commit->date; diff --git a/bisect.c b/bisect.c index 08c9fb7266..bb5af3ea3d 100644 --- a/bisect.c +++ b/bisect.c @@ -705,7 +705,7 @@ static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout) static struct commit *get_commit_reference(const struct object_id *oid) { - struct commit *r = lookup_commit_reference(oid->hash); + struct commit *r = lookup_commit_reference(oid); if (!r) die(_("Not a valid commit name %s"), oid_to_hex(oid)); return r; diff --git a/branch.c b/branch.c index 1758c97086..4899144f5d 100644 --- a/branch.c +++ b/branch.c @@ -286,7 +286,7 @@ void create_branch(const char *name, const char *start_name, break; } - if ((commit = lookup_commit_reference(oid.hash)) == NULL) + if ((commit = lookup_commit_reference(&oid)) == NULL) die(_("Not a valid branch point: '%s'."), start_name); oidcpy(&oid, &commit->object.oid); diff --git a/builtin/am.c b/builtin/am.c index a95dd8b4e6..650269ac5e 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1488,7 +1488,7 @@ static int parse_mail_rebase(struct am_state *state, const char *mail) if (get_mail_commit_oid(&commit_oid, mail) < 0) die(_("could not parse %s"), mail); - commit = lookup_commit_or_die(commit_oid.hash, mail); + commit = lookup_commit_or_die(&commit_oid, mail); get_commit_info(state, commit); @@ -1683,7 +1683,7 @@ static void do_commit(const struct am_state *state) if (!get_sha1_commit("HEAD", parent.hash)) { old_oid = &parent; - commit_list_insert(lookup_commit(parent.hash), &parents); + commit_list_insert(lookup_commit(&parent), &parents); } else { old_oid = NULL; say(state, stderr, _("applying to an empty history")); diff --git a/builtin/blame.c b/builtin/blame.c index 7d644d0925..58bb274d02 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2253,7 +2253,7 @@ static struct commit_list **append_parent(struct commit_list **tail, const struc { struct commit *parent; - parent = lookup_commit_reference(oid->hash); + parent = lookup_commit_reference(oid); if (!parent) die("no such commit %s", oid_to_hex(oid)); return &commit_list_insert(parent, tail)->next; @@ -2475,7 +2475,7 @@ static const char *dwim_reverse_initial(struct scoreboard *sb) /* Do we have HEAD? */ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) return NULL; - head_commit = lookup_commit_reference_gently(head_oid.hash, 1); + head_commit = lookup_commit_reference_gently(&head_oid, 1); if (!head_commit) return NULL; diff --git a/builtin/branch.c b/builtin/branch.c index 48a513a84d..83fcda43dc 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -124,7 +124,7 @@ static int branch_merged(int kind, const char *name, (reference_name = reference_name_to_free = resolve_refdup(upstream, RESOLVE_REF_READING, oid.hash, NULL)) != NULL) - reference_rev = lookup_commit_reference(oid.hash); + reference_rev = lookup_commit_reference(&oid); } if (!reference_rev) reference_rev = head_rev; @@ -157,7 +157,7 @@ static int check_branch_commit(const char *branchname, const char *refname, const struct object_id *oid, struct commit *head_rev, int kinds, int force) { - struct commit *rev = lookup_commit_reference(oid->hash); + struct commit *rev = lookup_commit_reference(oid); if (!rev) { error(_("Couldn't look up commit object for '%s'"), refname); return -1; @@ -211,7 +211,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, } if (!force) { - head_rev = lookup_commit_reference(head_oid.hash); + head_rev = lookup_commit_reference(&head_oid); if (!head_rev) die(_("Couldn't look up commit object for HEAD")); } diff --git a/builtin/checkout.c b/builtin/checkout.c index bfa5419f33..afa99fb8a0 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -393,7 +393,7 @@ static int checkout_paths(const struct checkout_opts *opts, die(_("unable to write new index file")); read_ref_full("HEAD", 0, rev.hash, NULL); - head = lookup_commit_reference_gently(rev.hash, 1); + head = lookup_commit_reference_gently(&rev, 1); errs |= post_checkout_hook(head, head, 0); return errs; @@ -833,7 +833,7 @@ static int switch_branches(const struct checkout_opts *opts, int flag, writeout_error = 0; memset(&old, 0, sizeof(old)); old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag); - old.commit = lookup_commit_reference_gently(rev.hash, 1); + old.commit = lookup_commit_reference_gently(&rev, 1); if (!(flag & REF_ISSYMREF)) old.path = NULL; @@ -1047,7 +1047,7 @@ static int parse_branchname_arg(int argc, const char **argv, else new->path = NULL; /* not an existing branch */ - new->commit = lookup_commit_reference_gently(rev->hash, 1); + new->commit = lookup_commit_reference_gently(rev, 1); if (!new->commit) { /* not a commit */ *source_tree = parse_tree_indirect(rev->hash); diff --git a/builtin/clone.c b/builtin/clone.c index de85b85254..646f287925 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -682,7 +682,7 @@ static void update_head(const struct ref *our, const struct ref *remote, install_branch_config(0, head, option_origin, our->name); } } else if (our) { - struct commit *c = lookup_commit_reference(our->old_oid.hash); + struct commit *c = lookup_commit_reference(&our->old_oid); /* --branch specifies a non-branch (i.e. tags), detach HEAD */ update_ref(msg, "HEAD", c->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 605017261c..f39c2b2737 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -58,7 +58,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) if (get_sha1_commit(argv[i], oid.hash)) die("Not a valid object name %s", argv[i]); assert_sha1_type(oid.hash, OBJ_COMMIT); - new_parent(lookup_commit(oid.hash), &parents); + new_parent(lookup_commit(&oid), &parents); continue; } diff --git a/builtin/commit.c b/builtin/commit.c index 8685c888f0..e69f466d5b 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1430,7 +1430,7 @@ static void print_summary(const char *prefix, const struct object_id *oid, struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; - commit = lookup_commit(oid->hash); + commit = lookup_commit(oid); if (!commit) die(_("couldn't look up newly created commit")); if (parse_commit(commit)) @@ -1654,7 +1654,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (get_sha1("HEAD", oid.hash)) current_head = NULL; else { - current_head = lookup_commit_or_die(oid.hash, "HEAD"); + current_head = lookup_commit_or_die(&oid, "HEAD"); if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } diff --git a/builtin/describe.c b/builtin/describe.c index a5cd8c513f..f6032f593c 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -281,7 +281,7 @@ static void describe(const char *arg, int last_one) if (get_oid(arg, &oid)) die(_("Not a valid object name %s"), arg); - cmit = lookup_commit_reference(oid.hash); + cmit = lookup_commit_reference(&oid); if (!cmit) die(_("%s is not a valid '%s' object"), arg, commit_type); @@ -309,7 +309,7 @@ static void describe(const char *arg, int last_one) struct commit *c; struct commit_name *n = hashmap_iter_first(&names, &iter); for (; n; n = hashmap_iter_next(&iter)) { - c = lookup_commit_reference_gently(n->peeled.hash, 1); + c = lookup_commit_reference_gently(&n->peeled, 1); if (c) c->util = n; } diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 326f88b657..e85a449df1 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -9,7 +9,7 @@ static struct rev_info log_tree_opt; static int diff_tree_commit_sha1(const struct object_id *oid) { - struct commit *commit = lookup_commit_reference(oid->hash); + struct commit *commit = lookup_commit_reference(oid); if (!commit) return -1; return log_tree_commit(&log_tree_opt, commit); @@ -23,7 +23,7 @@ static int stdin_diff_commit(struct commit *commit, const char *p) /* Graft the fake parents locally to the commit */ while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) { - struct commit *parent = lookup_commit(oid.hash); + struct commit *parent = lookup_commit(&oid); if (!pptr) { /* Free the real parent list */ free_commit_list(commit->parents); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e0220630d0..b4521cb627 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -938,7 +938,7 @@ static void import_marks(char *input_file) /* only commits */ continue; - commit = lookup_commit(oid.hash); + commit = lookup_commit(&oid); if (!commit) die("not a commit? can't happen: %s", oid_to_hex(&oid)); diff --git a/builtin/fetch.c b/builtin/fetch.c index 5f2c2ab23e..d4d573b985 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -636,8 +636,8 @@ static int update_local_ref(struct ref *ref, return r; } - current = lookup_commit_reference_gently(ref->old_oid.hash, 1); - updated = lookup_commit_reference_gently(ref->new_oid.hash, 1); + current = lookup_commit_reference_gently(&ref->old_oid, 1); + updated = lookup_commit_reference_gently(&ref->new_oid, 1); if (!current || !updated) { const char *msg; const char *what; @@ -770,7 +770,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, continue; } - commit = lookup_commit_reference_gently(rm->old_oid.hash, 1); + commit = lookup_commit_reference_gently(&rm->old_oid, + 1); if (!commit) rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE; diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 6faa3c0d24..91dd753dd9 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -566,7 +566,7 @@ static void find_merge_parents(struct merge_parents *result, commit_list_insert(parent, &parents); add_merge_parent(result, &obj->oid, &parent->object.oid); } - head_commit = lookup_commit(head->hash); + head_commit = lookup_commit(head); if (head_commit) commit_list_insert(head_commit, &parents); parents = reduce_heads(parents); @@ -633,7 +633,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, struct commit *head; struct rev_info rev; - head = lookup_commit_or_die(head_oid.hash, "HEAD"); + head = lookup_commit_or_die(&head_oid, "HEAD"); init_revisions(&rev, NULL); rev.commit_format = CMIT_FMT_ONELINE; rev.ignore_merges = 1; diff --git a/builtin/log.c b/builtin/log.c index b3b10cc1ed..d8b56ea410 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -878,8 +878,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) o2 = rev->pending.objects[1].item; flags1 = o1->flags; flags2 = o2->flags; - c1 = lookup_commit_reference(o1->oid.hash); - c2 = lookup_commit_reference(o2->oid.hash); + c1 = lookup_commit_reference(&o1->oid); + c2 = lookup_commit_reference(&o2->oid); if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) die(_("Not a range.")); @@ -1263,7 +1263,7 @@ static struct commit *get_base_commit(const char *base_commit, if (get_oid(upstream, &oid)) die(_("Failed to resolve '%s' as a valid ref."), upstream); - commit = lookup_commit_or_die(oid.hash, "upstream base"); + commit = lookup_commit_or_die(&oid, "upstream base"); base_list = get_merge_bases_many(commit, total, list); /* There should be one and only one merge base. */ if (!base_list || base_list->next) @@ -1819,7 +1819,7 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) { struct object_id oid; if (get_oid(arg, &oid) == 0) { - struct commit *commit = lookup_commit_reference(oid.hash); + struct commit *commit = lookup_commit_reference(&oid); if (commit) { commit->object.flags |= flags; add_pending_object(revs, &commit->object, arg); diff --git a/builtin/merge-base.c b/builtin/merge-base.c index cfe2a796f8..5c74ce249b 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -41,7 +41,7 @@ static struct commit *get_commit_reference(const char *arg) if (get_oid(arg, &revkey)) die("Not a valid object name %s", arg); - r = lookup_commit_reference(revkey.hash); + r = lookup_commit_reference(&revkey); if (!r) die("Not a valid commit name %s", arg); @@ -120,7 +120,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs) if (is_null_oid(oid)) return; - commit = lookup_commit(oid->hash); + commit = lookup_commit(oid); if (!commit || (commit->object.flags & TMP_MARK) || parse_commit(commit)) @@ -168,7 +168,7 @@ static int handle_fork_point(int argc, const char **argv) if (get_oid(commitname, &oid)) die("Not a valid object name: '%s'", commitname); - derived = lookup_commit_reference(oid.hash); + derived = lookup_commit_reference(&oid); memset(&revs, 0, sizeof(revs)); revs.initial = 1; for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); diff --git a/builtin/merge.c b/builtin/merge.c index 703827f006..f11b5f3de4 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -1123,7 +1123,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (!branch || is_null_oid(&head_oid)) head_commit = NULL; else - head_commit = lookup_commit_or_die(head_oid.hash, "HEAD"); + head_commit = lookup_commit_or_die(&head_oid, "HEAD"); init_diff_ui_defaults(); git_config(git_merge_config, NULL); diff --git a/builtin/notes.c b/builtin/notes.c index 7b891471c4..f2847c41e0 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -706,7 +706,7 @@ static int merge_commit(struct notes_merge_options *o) if (get_oid("NOTES_MERGE_PARTIAL", &oid)) die(_("failed to read ref NOTES_MERGE_PARTIAL")); - else if (!(partial = lookup_commit_reference(oid.hash))) + else if (!(partial = lookup_commit_reference(&oid))) die(_("could not find commit from NOTES_MERGE_PARTIAL.")); else if (parse_commit(partial)) die(_("could not parse commit from NOTES_MERGE_PARTIAL.")); diff --git a/builtin/pull.c b/builtin/pull.c index dd1a4a94e4..2ffb6569a9 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -698,10 +698,10 @@ static int get_octopus_merge_base(struct object_id *merge_base, { struct commit_list *revs = NULL, *result; - commit_list_insert(lookup_commit_reference(curr_head->hash), &revs); - commit_list_insert(lookup_commit_reference(merge_head->hash), &revs); + commit_list_insert(lookup_commit_reference(curr_head), &revs); + commit_list_insert(lookup_commit_reference(merge_head), &revs); if (!is_null_oid(fork_point)) - commit_list_insert(lookup_commit_reference(fork_point->hash), &revs); + commit_list_insert(lookup_commit_reference(fork_point), &revs); result = reduce_heads(get_octopus_merge_bases(revs)); free_commit_list(revs); @@ -865,9 +865,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix) struct commit_list *list = NULL; struct commit *merge_head, *head; - head = lookup_commit_reference(orig_head.hash); + head = lookup_commit_reference(&orig_head); commit_list_insert(head, &list); - merge_head = lookup_commit_reference(merge_heads.oid[0].hash); + merge_head = lookup_commit_reference(&merge_heads.oid[0]); if (is_descendant_of(merge_head, list)) { /* we can fast-forward this without invoking rebase */ opt_ff = "--ff-only"; diff --git a/builtin/reflog.c b/builtin/reflog.c index d6718d326c..4831116ea3 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -192,7 +192,7 @@ static int keep_entry(struct commit **it, struct object_id *oid) if (is_null_oid(oid)) return 1; - commit = lookup_commit_reference_gently(oid->hash, 1); + commit = lookup_commit_reference_gently(oid, 1); if (!commit) return 0; @@ -261,7 +261,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit if (is_null_oid(oid)) return 0; - commit = lookup_commit_reference_gently(oid->hash, 1); + commit = lookup_commit_reference_gently(oid, 1); /* Not a commit -- keep it */ if (!commit) @@ -318,7 +318,7 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid, struct commit *tip_commit; if (flags & REF_ISSYMREF) return 0; - tip_commit = lookup_commit_reference_gently(oid->hash, 1); + tip_commit = lookup_commit_reference_gently(oid, 1); if (!tip_commit) return 0; commit_list_insert(tip_commit, list); @@ -335,7 +335,7 @@ static void reflog_expiry_prepare(const char *refname, cb->tip_commit = NULL; cb->unreachable_expire_kind = UE_HEAD; } else { - cb->tip_commit = lookup_commit_reference_gently(oid->hash, 1); + cb->tip_commit = lookup_commit_reference_gently(oid, 1); if (!cb->tip_commit) cb->unreachable_expire_kind = UE_ALWAYS; else diff --git a/builtin/replace.c b/builtin/replace.c index ab17668f43..3c44ef750e 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -328,7 +328,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv) struct object_id oid; if (get_oid(argv[i], &oid) < 0) die(_("Not a valid object name: '%s'"), argv[i]); - lookup_commit_or_die(oid.hash, argv[i]); + lookup_commit_or_die(&oid, argv[i]); strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid)); } @@ -394,7 +394,7 @@ static int create_graft(int argc, const char **argv, int force) if (get_oid(old_ref, &old) < 0) die(_("Not a valid object name: '%s'"), old_ref); - commit = lookup_commit_or_die(old.hash, old_ref); + commit = lookup_commit_or_die(&old, old_ref); buffer = get_commit_buffer(commit, &size); strbuf_add(&buf, buffer, size); diff --git a/builtin/reset.c b/builtin/reset.c index fc3b906c47..0be52fa36a 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -303,7 +303,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) struct commit *commit; if (get_sha1_committish(rev, oid.hash)) die(_("Failed to resolve '%s' as a valid revision."), rev); - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (!commit) die(_("Could not parse object '%s'."), rev); oidcpy(&oid, &commit->object.oid); @@ -380,7 +380,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) update_ref_status = reset_refs(rev, &oid); if (reset_type == HARD && !update_ref_status && !quiet) - print_new_head_line(lookup_commit_reference(oid.hash)); + print_new_head_line(lookup_commit_reference(&oid)); } if (!pathspec.nr) remove_branch_state(); diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 4af1222644..f650188ef3 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -279,8 +279,8 @@ static int try_difference(const char *arg) if (symmetric) { struct commit_list *exclude; struct commit *a, *b; - a = lookup_commit_reference(oid.hash); - b = lookup_commit_reference(end.hash); + a = lookup_commit_reference(&oid); + b = lookup_commit_reference(&end); exclude = get_merge_bases(a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); @@ -332,7 +332,7 @@ static int try_parent_shorthands(const char *arg) return 0; } - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (exclude_parent && exclude_parent > commit_list_count(commit->parents)) { *dotdot = '^'; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 19756595d5..71b6f3c179 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -358,7 +358,7 @@ static void sort_ref_range(int bottom, int top) static int append_ref(const char *refname, const struct object_id *oid, int allow_dups) { - struct commit *commit = lookup_commit_reference_gently(oid->hash, 1); + struct commit *commit = lookup_commit_reference_gently(oid, 1); int i; if (!commit) @@ -816,7 +816,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) MAX_REVS), MAX_REVS); if (get_sha1(ref_name[num_rev], revkey.hash)) die(_("'%s' is not a valid ref."), ref_name[num_rev]); - commit = lookup_commit_reference(revkey.hash); + commit = lookup_commit_reference(&revkey); if (!commit) die(_("cannot find commit %s (%s)"), ref_name[num_rev], oid_to_hex(&revkey)); diff --git a/builtin/tag.c b/builtin/tag.c index 597c925e38..d0070b37c2 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -328,7 +328,7 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) } free(buf); - if ((c = lookup_commit_reference(oid->hash)) != NULL) + if ((c = lookup_commit_reference(oid)) != NULL) strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT))); break; case OBJ_TREE: diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index a5db1c427f..05b734e6d1 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -25,7 +25,7 @@ static int run_gpg_verify(const struct object_id *oid, const char *buf, unsigned memset(&signature_check, 0, sizeof(signature_check)); - ret = check_commit_signature(lookup_commit(oid->hash), &signature_check); + ret = check_commit_signature(lookup_commit(oid), &signature_check); print_signature_buffer(&signature_check, flags); signature_check_clear(&signature_check); diff --git a/bundle.c b/bundle.c index 6e181bb3d8..3386dba3be 100644 --- a/bundle.c +++ b/bundle.c @@ -367,7 +367,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ - struct commit *one = lookup_commit_reference(oid.hash); + struct commit *one = lookup_commit_reference(&oid); struct object *obj; if (e->item == &(one->object)) { diff --git a/commit.c b/commit.c index ec41ba5e09..0f6c9b6bf3 100644 --- a/commit.c +++ b/commit.c @@ -18,38 +18,38 @@ int save_commit_buffer = 1; const char *commit_type = "commit"; -struct commit *lookup_commit_reference_gently(const unsigned char *sha1, +struct commit *lookup_commit_reference_gently(const struct object_id *oid, int quiet) { - struct object *obj = deref_tag(parse_object(sha1), NULL, 0); + struct object *obj = deref_tag(parse_object(oid->hash), NULL, 0); if (!obj) return NULL; return object_as_type(obj, OBJ_COMMIT, quiet); } -struct commit *lookup_commit_reference(const unsigned char *sha1) +struct commit *lookup_commit_reference(const struct object_id *oid) { - return lookup_commit_reference_gently(sha1, 0); + return lookup_commit_reference_gently(oid, 0); } -struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name) +struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name) { - struct commit *c = lookup_commit_reference(sha1); + struct commit *c = lookup_commit_reference(oid); if (!c) die(_("could not parse %s"), ref_name); - if (hashcmp(sha1, c->object.oid.hash)) { + if (oidcmp(oid, &c->object.oid)) { warning(_("%s %s is not a commit!"), - ref_name, sha1_to_hex(sha1)); + ref_name, oid_to_hex(oid)); } return c; } -struct commit *lookup_commit(const unsigned char *sha1) +struct commit *lookup_commit(const struct object_id *oid) { - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (!obj) - return create_object(sha1, alloc_commit_node()); + return create_object(oid->hash, alloc_commit_node()); return object_as_type(obj, OBJ_COMMIT, 0); } @@ -60,7 +60,7 @@ struct commit *lookup_commit_reference_by_name(const char *name) if (get_sha1_committish(name, oid.hash)) return NULL; - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (parse_commit(commit)) return NULL; return commit; @@ -350,7 +350,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s */ if (graft && (graft->nr_parent < 0 || grafts_replace_parents)) continue; - new_parent = lookup_commit(parent.hash); + new_parent = lookup_commit(&parent); if (new_parent) pptr = &commit_list_insert(new_parent, pptr)->next; } @@ -358,7 +358,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s int i; struct commit *new_parent; for (i = 0; i < graft->nr_parent; i++) { - new_parent = lookup_commit(graft->parent[i].hash); + new_parent = lookup_commit(&graft->parent[i]); if (!new_parent) continue; pptr = &commit_list_insert(new_parent, pptr)->next; @@ -562,7 +562,7 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark) for (i = 0; i < a->nr; i++) { object = a->objects[i].item; - commit = lookup_commit_reference_gently(object->oid.hash, 1); + commit = lookup_commit_reference_gently(&object->oid, 1); if (commit) clear_commit_marks(commit, mark); } diff --git a/commit.h b/commit.h index 884177b8fa..3488a285b1 100644 --- a/commit.h +++ b/commit.h @@ -45,18 +45,18 @@ enum decoration_type { void add_name_decoration(enum decoration_type type, const char *name, struct object *obj); const struct name_decoration *get_name_decoration(const struct object *obj); -struct commit *lookup_commit(const unsigned char *sha1); -struct commit *lookup_commit_reference(const unsigned char *sha1); -struct commit *lookup_commit_reference_gently(const unsigned char *sha1, +struct commit *lookup_commit(const struct object_id *oid); +struct commit *lookup_commit_reference(const struct object_id *oid); +struct commit *lookup_commit_reference_gently(const struct object_id *oid, int quiet); struct commit *lookup_commit_reference_by_name(const char *name); /* - * Look up object named by "sha1", dereference tag as necessary, - * get a commit and return it. If "sha1" does not dereference to + * Look up object named by "oid", dereference tag as necessary, + * get a commit and return it. If "oid" does not dereference to * a commit, use ref_name to report an error and die. */ -struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name); +struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name); int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size); int parse_commit_gently(struct commit *item, int quiet_on_missing); diff --git a/fast-import.c b/fast-import.c index ba886c9e7e..585d5d6e85 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1763,8 +1763,8 @@ static int update_branch(struct branch *b) if (!force_update && !is_null_oid(&old_oid)) { struct commit *old_cmit, *new_cmit; - old_cmit = lookup_commit_reference_gently(old_oid.hash, 0); - new_cmit = lookup_commit_reference_gently(b->oid.hash, 0); + old_cmit = lookup_commit_reference_gently(&old_oid, 0); + new_cmit = lookup_commit_reference_gently(&b->oid, 0); if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); diff --git a/fetch-pack.c b/fetch-pack.c index d455ef97af..7ec75f2782 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -483,7 +483,7 @@ static int find_common(struct fetch_pack_args *args, case ACK_ready: case ACK_continue: { struct commit *commit = - lookup_commit(result_oid->hash); + lookup_commit(result_oid); if (!commit) die(_("invalid commit %s"), oid_to_hex(result_oid)); if (args->stateless_rpc diff --git a/http-push.c b/http-push.c index f3dd0a5606..04568e4fbd 100644 --- a/http-push.c +++ b/http-push.c @@ -1569,8 +1569,9 @@ static void fetch_symref(const char *path, char **symref, struct object_id *oid) static int verify_merge_base(struct object_id *head_oid, struct ref *remote) { - struct commit *head = lookup_commit_or_die(head_oid->hash, "HEAD"); - struct commit *branch = lookup_commit_or_die(remote->old_oid.hash, remote->name); + struct commit *head = lookup_commit_or_die(head_oid, "HEAD"); + struct commit *branch = lookup_commit_or_die(&remote->old_oid, + remote->name); return in_merge_bases(branch, head); } diff --git a/log-tree.c b/log-tree.c index 4618dd04ca..7fb1a85d2d 100644 --- a/log-tree.c +++ b/log-tree.c @@ -140,7 +140,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, static int add_graft_decoration(const struct commit_graft *graft, void *cb_data) { - struct commit *commit = lookup_commit(graft->oid.hash); + struct commit *commit = lookup_commit(&graft->oid); if (!commit) return 0; add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object); diff --git a/notes-cache.c b/notes-cache.c index 1cdd4984a4..2843e98576 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -14,7 +14,7 @@ static int notes_cache_match_validity(const char *ref, const char *validity) if (read_ref(ref, oid.hash) < 0) return 0; - commit = lookup_commit_reference_gently(oid.hash, 1); + commit = lookup_commit_reference_gently(&oid, 1); if (!commit) return 0; diff --git a/notes-merge.c b/notes-merge.c index 06d8be9cbf..6244f6af9c 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -554,7 +554,7 @@ int notes_merge(struct notes_merge_options *o, else if (!check_refname_format(o->local_ref, 0) && is_null_oid(&local_oid)) local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ - else if (!(local = lookup_commit_reference(local_oid.hash))) + else if (!(local = lookup_commit_reference(&local_oid))) die("Could not parse local commit %s (%s)", oid_to_hex(&local_oid), o->local_ref); trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid)); @@ -572,7 +572,7 @@ int notes_merge(struct notes_merge_options *o, die("Failed to resolve remote notes ref '%s'", o->remote_ref); } - } else if (!(remote = lookup_commit_reference(remote_oid.hash))) { + } else if (!(remote = lookup_commit_reference(&remote_oid))) { die("Could not parse remote commit %s (%s)", oid_to_hex(&remote_oid), o->remote_ref); } diff --git a/notes-utils.c b/notes-utils.c index 36c1490aa7..325ff3daa3 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -18,7 +18,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents, /* Deduce parent commit from t->ref */ struct object_id parent_oid; if (!read_ref(t->ref, parent_oid.hash)) { - struct commit *parent = lookup_commit(parent_oid.hash); + struct commit *parent = lookup_commit(&parent_oid); if (parse_commit(parent)) die("Failed to find/parse commit %s", t->ref); commit_list_insert(parent, &parents); diff --git a/object.c b/object.c index e680d881a4..fe22223566 100644 --- a/object.c +++ b/object.c @@ -182,9 +182,12 @@ struct object *lookup_unknown_object(const unsigned char *sha1) struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p) { + struct object_id oid; struct object *obj; *eaten_p = 0; + hashcpy(oid.hash, sha1); + obj = NULL; if (type == OBJ_BLOB) { struct blob *blob = lookup_blob(sha1); @@ -206,7 +209,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t } } } else if (type == OBJ_COMMIT) { - struct commit *commit = lookup_commit(sha1); + struct commit *commit = lookup_commit(&oid); if (commit) { if (parse_commit_buffer(commit, buffer, size)) return NULL; diff --git a/parse-options-cb.c b/parse-options-cb.c index 35a941fddc..8dd57cf6ea 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -87,7 +87,7 @@ int parse_opt_commits(const struct option *opt, const char *arg, int unset) return -1; if (get_oid(arg, &oid)) return error("malformed object name %s", arg); - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (!commit) return error("no such commit %s", arg); commit_list_insert(commit, opt->value); diff --git a/ref-filter.c b/ref-filter.c index 47cce0a184..e1d18ac0d1 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1782,7 +1782,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, * non-commits early. The actual filtering is done later. */ if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) { - commit = lookup_commit_reference_gently(oid->hash, 1); + commit = lookup_commit_reference_gently(oid, 1); if (!commit) return 0; /* We perform the filtering for the '--contains' option... */ @@ -2108,7 +2108,7 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) if (get_oid(arg, &oid)) die(_("malformed object name %s"), arg); - rf->merge_commit = lookup_commit_reference_gently(oid.hash, 0); + rf->merge_commit = lookup_commit_reference_gently(&oid, 0); if (!rf->merge_commit) return opterror(opt, "must point to a commit", 0); diff --git a/remote.c b/remote.c index 801137c72e..bf9a47d95b 100644 --- a/remote.c +++ b/remote.c @@ -1296,7 +1296,7 @@ static void add_to_tips(struct tips *tips, const struct object_id *oid) if (is_null_oid(oid)) return; - commit = lookup_commit_reference_gently(oid->hash, 1); + commit = lookup_commit_reference_gently(oid, 1); if (!commit || (commit->object.flags & TMP_MARK)) return; commit->object.flags |= TMP_MARK; @@ -1358,7 +1358,8 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds if (is_null_oid(&ref->new_oid)) continue; - commit = lookup_commit_reference_gently(ref->new_oid.hash, 1); + commit = lookup_commit_reference_gently(&ref->new_oid, + 1); if (!commit) /* not pushing a commit, which is not an error */ continue; @@ -1585,8 +1586,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_object_file(&ref->old_oid)) reject_reason = REF_STATUS_REJECT_FETCH_FIRST; - else if (!lookup_commit_reference_gently(ref->old_oid.hash, 1) || - !lookup_commit_reference_gently(ref->new_oid.hash, 1)) + else if (!lookup_commit_reference_gently(&ref->old_oid, 1) || + !lookup_commit_reference_gently(&ref->new_oid, 1)) reject_reason = REF_STATUS_REJECT_NEEDS_FORCE; else if (!ref_newer(&ref->new_oid, &ref->old_oid)) reject_reason = REF_STATUS_REJECT_NONFASTFORWARD; @@ -2009,13 +2010,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, /* Cannot stat if what we used to build on no longer exists */ if (read_ref(base, oid.hash)) return -1; - theirs = lookup_commit_reference(oid.hash); + theirs = lookup_commit_reference(&oid); if (!theirs) return -1; if (read_ref(branch->refname, oid.hash)) return -1; - ours = lookup_commit_reference(oid.hash); + ours = lookup_commit_reference(&oid); if (!ours) return -1; diff --git a/revision.c b/revision.c index 9453670341..f8e0dee6d5 100644 --- a/revision.c +++ b/revision.c @@ -1395,10 +1395,10 @@ static void prepare_show_merge(struct rev_info *revs) if (get_oid("HEAD", &oid)) die("--merge without HEAD?"); - head = lookup_commit_or_die(oid.hash, "HEAD"); + head = lookup_commit_or_die(&oid, "HEAD"); if (get_oid("MERGE_HEAD", &oid)) die("--merge without MERGE_HEAD?"); - other = lookup_commit_or_die(oid.hash, "MERGE_HEAD"); + other = lookup_commit_or_die(&oid, "MERGE_HEAD"); add_pending_object(revs, &head->object, "HEAD"); add_pending_object(revs, &other->object, "MERGE_HEAD"); bases = get_merge_bases(head, other); @@ -1500,10 +1500,10 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi a = (a_obj->type == OBJ_COMMIT ? (struct commit *)a_obj - : lookup_commit_reference(a_obj->oid.hash)); + : lookup_commit_reference(&a_obj->oid)); b = (b_obj->type == OBJ_COMMIT ? (struct commit *)b_obj - : lookup_commit_reference(b_obj->oid.hash)); + : lookup_commit_reference(&b_obj->oid)); if (!a || !b) goto missing; exclude = get_merge_bases(a, b); diff --git a/sequencer.c b/sequencer.c index e44c015b2c..0867633602 100644 --- a/sequencer.c +++ b/sequencer.c @@ -488,7 +488,7 @@ static int is_index_unchanged(void) if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) return error(_("could not resolve HEAD commit\n")); - head_commit = lookup_commit(head_oid.hash); + head_commit = lookup_commit(&head_oid); /* * If head_commit is NULL, check_commit, called from @@ -841,7 +841,7 @@ static int update_squash_messages(enum todo_command command, if (get_oid("HEAD", &head)) return error(_("need a HEAD to fixup")); - if (!(head_commit = lookup_commit_reference(head.hash))) + if (!(head_commit = lookup_commit_reference(&head))) return error(_("could not read HEAD")); if (!(head_message = get_commit_buffer(head_commit, NULL))) return error(_("could not read HEAD's commit message")); @@ -1280,7 +1280,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) if (status < 0) return -1; - item->commit = lookup_commit_reference(commit_oid.hash); + item->commit = lookup_commit_reference(&commit_oid); return !item->commit; } @@ -2297,7 +2297,7 @@ int sequencer_pick_revisions(struct replay_opts *opts) continue; if (!get_oid(name, &oid)) { - if (!lookup_commit_reference_gently(oid.hash, 1)) { + if (!lookup_commit_reference_gently(&oid, 1)) { enum object_type type = sha1_object_info(oid.hash, NULL); return error(_("%s: can't cherry-pick a %s"), name, typename(type)); diff --git a/sha1_name.c b/sha1_name.c index 8889190a90..390a09c41a 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -354,7 +354,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) type = sha1_object_info(oid->hash, NULL); if (type == OBJ_COMMIT) { - struct commit *commit = lookup_commit(oid->hash); + struct commit *commit = lookup_commit(oid); if (commit) { struct pretty_print_context pp = {0}; pp.date_mode.type = DATE_SHORT; @@ -729,7 +729,7 @@ static int get_parent(const char *name, int len, if (ret) return ret; - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (parse_commit(commit)) return -1; if (!idx) { @@ -757,7 +757,7 @@ static int get_nth_ancestor(const char *name, int len, ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH); if (ret) return ret; - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (!commit) return -1; @@ -1136,13 +1136,13 @@ int get_oid_mb(const char *name, struct object_id *oid) } if (st) return st; - one = lookup_commit_reference_gently(oid_tmp.hash, 0); + one = lookup_commit_reference_gently(&oid_tmp, 0); if (!one) return -1; if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", oid_tmp.hash)) return -1; - two = lookup_commit_reference_gently(oid_tmp.hash, 0); + two = lookup_commit_reference_gently(&oid_tmp, 0); if (!two) return -1; mbs = get_merge_bases(one, two); diff --git a/shallow.c b/shallow.c index 1327ee16fc..6950cc24f0 100644 --- a/shallow.c +++ b/shallow.c @@ -31,7 +31,7 @@ int register_shallow(const struct object_id *oid) { struct commit_graft *graft = xmalloc(sizeof(struct commit_graft)); - struct commit *commit = lookup_commit(oid->hash); + struct commit *commit = lookup_commit(oid); oidcpy(&graft->oid, oid); graft->nr_parent = -1; @@ -241,7 +241,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data) if (graft->nr_parent != -1) return 0; if (data->flags & SEEN_ONLY) { - struct commit *c = lookup_commit(graft->oid.hash); + struct commit *c = lookup_commit(&graft->oid); if (!c || !(c->object.flags & SEEN)) { if (data->flags & VERBOSE) printf("Removing %s from .git/shallow\n", @@ -475,7 +475,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid, size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr); uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */ uint32_t *bitmap = paint_alloc(info); - struct commit *c = lookup_commit_reference_gently(oid->hash, 1); + struct commit *c = lookup_commit_reference_gently(oid, 1); if (!c) return; memset(bitmap, 0, bitmap_size); @@ -531,7 +531,7 @@ static void paint_down(struct paint_info *info, const struct object_id *oid, static int mark_uninteresting(const char *refname, const struct object_id *oid, int flags, void *cb_data) { - struct commit *commit = lookup_commit_reference_gently(oid->hash, 1); + struct commit *commit = lookup_commit_reference_gently(oid, 1); if (!commit) return 0; commit->object.flags |= UNINTERESTING; @@ -599,7 +599,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info, /* Mark potential bottoms so we won't go out of bound */ for (i = 0; i < nr_shallow; i++) { - struct commit *c = lookup_commit(oid[shallow[i]].hash); + struct commit *c = lookup_commit(&oid[shallow[i]]); c->object.flags |= BOTTOM; } @@ -610,7 +610,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info, int bitmap_size = ((pi.nr_bits + 31) / 32) * sizeof(uint32_t); memset(used, 0, sizeof(*used) * info->shallow->nr); for (i = 0; i < nr_shallow; i++) { - const struct commit *c = lookup_commit(oid[shallow[i]].hash); + const struct commit *c = lookup_commit(&oid[shallow[i]]); uint32_t **map = ref_bitmap_at(&pi.ref_bitmap, c); if (*map) used[shallow[i]] = xmemdupz(*map, bitmap_size); @@ -641,7 +641,7 @@ static int add_ref(const char *refname, const struct object_id *oid, { struct commit_array *ca = cb_data; ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc); - ca->commits[ca->nr] = lookup_commit_reference_gently(oid->hash, 1); + ca->commits[ca->nr] = lookup_commit_reference_gently(oid, 1); if (ca->commits[ca->nr]) ca->nr++; return 0; @@ -679,7 +679,7 @@ static void post_assign_shallow(struct shallow_info *info, for (i = dst = 0; i < info->nr_theirs; i++) { if (i != dst) info->theirs[dst] = info->theirs[i]; - c = lookup_commit(oid[info->theirs[i]].hash); + c = lookup_commit(&oid[info->theirs[i]]); bitmap = ref_bitmap_at(ref_bitmap, c); if (!*bitmap) continue; @@ -700,7 +700,7 @@ static void post_assign_shallow(struct shallow_info *info, for (i = dst = 0; i < info->nr_ours; i++) { if (i != dst) info->ours[dst] = info->ours[i]; - c = lookup_commit(oid[info->ours[i]].hash); + c = lookup_commit(&oid[info->ours[i]]); bitmap = ref_bitmap_at(ref_bitmap, c); if (!*bitmap) continue; @@ -722,7 +722,7 @@ static void post_assign_shallow(struct shallow_info *info, int delayed_reachability_test(struct shallow_info *si, int c) { if (si->need_reachability_test[c]) { - struct commit *commit = lookup_commit(si->shallow->oid[c].hash); + struct commit *commit = lookup_commit(&si->shallow->oid[c]); if (!si->commits) { struct commit_array ca; diff --git a/submodule.c b/submodule.c index d5c28b9f1b..ba5429be15 100644 --- a/submodule.c +++ b/submodule.c @@ -447,8 +447,8 @@ static void show_submodule_header(FILE *f, const char *path, * Attempt to lookup the commit references, and determine if this is * a fast forward or fast backwards update. */ - *left = lookup_commit_reference(one->hash); - *right = lookup_commit_reference(two->hash); + *left = lookup_commit_reference(one); + *right = lookup_commit_reference(two); /* * Warn about missing commits in the submodule project, but only if @@ -634,7 +634,7 @@ static int check_has_commit(const struct object_id *oid, void *data) { int *has_commit = data; - if (!lookup_commit_reference(oid->hash)) + if (!lookup_commit_reference(oid)) *has_commit = 0; return 0; @@ -899,7 +899,7 @@ int push_unpushed_submodules(struct oid_array *commits, static int is_submodule_commit_present(const char *path, struct object_id *oid) { int is_present = 0; - if (!add_submodule_odb(path) && lookup_commit_reference(oid->hash)) { + if (!add_submodule_odb(path) && lookup_commit_reference(oid)) { /* Even if the submodule is checked out and the commit is * present, make sure it is reachable from a ref. */ struct child_process cp = CHILD_PROCESS_INIT; @@ -1592,9 +1592,9 @@ int merge_submodule(struct object_id *result, const char *path, return 0; } - if (!(commit_base = lookup_commit_reference(base->hash)) || - !(commit_a = lookup_commit_reference(a->hash)) || - !(commit_b = lookup_commit_reference(b->hash))) { + if (!(commit_base = lookup_commit_reference(base)) || + !(commit_a = lookup_commit_reference(a)) || + !(commit_b = lookup_commit_reference(b))) { MERGE_WARNING(path, "commits not present"); return 0; } diff --git a/tag.c b/tag.c index 625f5cd71e..79b78d3583 100644 --- a/tag.c +++ b/tag.c @@ -146,7 +146,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) } else if (!strcmp(type, tree_type)) { item->tagged = &lookup_tree(oid.hash)->object; } else if (!strcmp(type, commit_type)) { - item->tagged = &lookup_commit(oid.hash)->object; + item->tagged = &lookup_commit(&oid)->object; } else if (!strcmp(type, tag_type)) { item->tagged = &lookup_tag(oid.hash)->object; } else { diff --git a/tree.c b/tree.c index ce345c5511..33fa7ee717 100644 --- a/tree.c +++ b/tree.c @@ -91,7 +91,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, else if (S_ISGITLINK(entry.mode)) { struct commit *commit; - commit = lookup_commit(entry.oid->hash); + commit = lookup_commit(entry.oid); if (!commit) die("Commit %s in submodule path %s%s not found", oid_to_hex(entry.oid), diff --git a/walker.c b/walker.c index 2c86e406f9..b499fcb729 100644 --- a/walker.c +++ b/walker.c @@ -206,7 +206,7 @@ static int interpret_target(struct walker *walker, char *target, unsigned char * static int mark_complete(const char *path, const struct object_id *oid, int flag, void *cb_data) { - struct commit *commit = lookup_commit_reference_gently(oid->hash, 1); + struct commit *commit = lookup_commit_reference_gently(oid, 1); if (commit) { commit->object.flags |= COMPLETE; diff --git a/wt-status.c b/wt-status.c index 0375484962..e5854ba6f8 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1428,7 +1428,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state) /* sha1 is a commit? match without further lookup */ (!oidcmp(&cb.noid, &oid) || /* perhaps sha1 is a tag, try to dereference to a commit */ - ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL && + ((commit = lookup_commit_reference_gently(&oid, 1)) != NULL && !oidcmp(&cb.noid, &commit->object.oid)))) { const char *from = ref; if (!skip_prefix(from, "refs/tags/", &from)) From e6a492b7beca9dc8b656f2be3aec23fc1a35e4de Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:11 +0000 Subject: [PATCH 150/566] pack: convert struct pack_idx_entry to struct object_id Convert struct pack_idx_entry to use struct object_id by changing the definition and applying the following semantic patch, plus the standard object_id transforms: @@ struct pack_idx_entry E1; @@ - E1.sha1 + E1.oid.hash @@ struct pack_idx_entry *E1; @@ - E1->sha1 + E1->oid.hash Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/index-pack.c | 33 ++++++++++++---------- builtin/pack-objects.c | 63 +++++++++++++++++++++++++----------------- bulk-checkin.c | 4 +-- fast-import.c | 30 ++++++++++---------- pack-bitmap-write.c | 8 ++++-- pack-objects.c | 8 ++++-- pack-write.c | 10 +++---- pack.h | 2 +- 8 files changed, 89 insertions(+), 69 deletions(-) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 4ff567db47..fef0025e49 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -747,13 +747,13 @@ static int compare_objects(const unsigned char *buf, unsigned long size, ssize_t len = read_istream(data->st, data->buf, size); if (len == 0) die(_("SHA1 COLLISION FOUND WITH %s !"), - sha1_to_hex(data->entry->idx.sha1)); + oid_to_hex(&data->entry->idx.oid)); if (len < 0) die(_("unable to read %s"), - sha1_to_hex(data->entry->idx.sha1)); + oid_to_hex(&data->entry->idx.oid)); if (memcmp(buf, data->buf, len)) die(_("SHA1 COLLISION FOUND WITH %s !"), - sha1_to_hex(data->entry->idx.sha1)); + oid_to_hex(&data->entry->idx.oid)); size -= len; buf += len; } @@ -771,12 +771,12 @@ static int check_collison(struct object_entry *entry) memset(&data, 0, sizeof(data)); data.entry = entry; - data.st = open_istream(entry->idx.sha1, &type, &size, NULL); + data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL); if (!data.st) return -1; if (size != entry->size || type != entry->type) die(_("SHA1 COLLISION FOUND WITH %s !"), - sha1_to_hex(entry->idx.sha1)); + oid_to_hex(&entry->idx.oid)); unpack_data(entry, compare_objects, &data); close_istream(data.st); free(data.buf); @@ -957,9 +957,10 @@ static void resolve_delta(struct object_entry *delta_obj, if (!result->data) bad_object(delta_obj->idx.offset, _("failed to apply delta")); hash_sha1_file(result->data, result->size, - typename(delta_obj->real_type), delta_obj->idx.sha1); + typename(delta_obj->real_type), + delta_obj->idx.oid.hash); sha1_object(result->data, NULL, result->size, delta_obj->real_type, - delta_obj->idx.sha1); + delta_obj->idx.oid.hash); counter_lock(); nr_resolved_deltas++; counter_unlock(); @@ -989,7 +990,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base, struct base_data *prev_base) { if (base->ref_last == -1 && base->ofs_last == -1) { - find_ref_delta_children(base->obj->idx.sha1, + find_ref_delta_children(base->obj->idx.oid.hash, &base->ref_first, &base->ref_last, OBJ_REF_DELTA); @@ -1130,7 +1131,8 @@ static void parse_pack_objects(unsigned char *sha1) for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; void *data = unpack_raw_entry(obj, &ofs_delta->offset, - ref_delta_sha1, obj->idx.sha1); + ref_delta_sha1, + obj->idx.oid.hash); obj->real_type = obj->type; if (obj->type == OBJ_OFS_DELTA) { nr_ofs_deltas++; @@ -1146,7 +1148,8 @@ static void parse_pack_objects(unsigned char *sha1) obj->real_type = OBJ_BAD; nr_delays++; } else - sha1_object(data, NULL, obj->size, obj->type, obj->idx.sha1); + sha1_object(data, NULL, obj->size, obj->type, + obj->idx.oid.hash); free(data); display_progress(progress, i+1); } @@ -1172,7 +1175,8 @@ static void parse_pack_objects(unsigned char *sha1) if (obj->real_type != OBJ_BAD) continue; obj->real_type = obj->type; - sha1_object(NULL, obj, obj->size, obj->type, obj->idx.sha1); + sha1_object(NULL, obj, obj->size, obj->type, + obj->idx.oid.hash); nr_delays--; } if (nr_delays) @@ -1330,7 +1334,7 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f, obj[1].idx.offset += write_compressed(f, buf, size); obj[0].idx.crc32 = crc32_end(f); sha1flush(f); - hashcpy(obj->idx.sha1, sha1); + hashcpy(obj->idx.oid.hash, sha1); return obj; } @@ -1581,13 +1585,14 @@ static void show_pack_info(int stat_only) if (stat_only) continue; printf("%s %-6s %lu %lu %"PRIuMAX, - sha1_to_hex(obj->idx.sha1), + oid_to_hex(&obj->idx.oid), typename(obj->real_type), obj->size, (unsigned long)(obj[1].idx.offset - obj->idx.offset), (uintmax_t)obj->idx.offset); if (is_delta_type(obj->type)) { struct object_entry *bobj = &objects[obj_stat[i].base_object_no]; - printf(" %u %s", obj_stat[i].delta_depth, sha1_to_hex(bobj->idx.sha1)); + printf(" %u %s", obj_stat[i].delta_depth, + oid_to_hex(&bobj->idx.oid)); } putchar('\n'); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 4770708063..d76ff05426 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -106,12 +106,14 @@ static void *get_delta(struct object_entry *entry) void *buf, *base_buf, *delta_buf; enum object_type type; - buf = read_sha1_file(entry->idx.sha1, &type, &size); + buf = read_sha1_file(entry->idx.oid.hash, &type, &size); if (!buf) - die("unable to read %s", sha1_to_hex(entry->idx.sha1)); - base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size); + die("unable to read %s", oid_to_hex(&entry->idx.oid)); + base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type, + &base_size); if (!base_buf) - die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1)); + die("unable to read %s", + oid_to_hex(&entry->delta->idx.oid)); delta_buf = diff_delta(base_buf, base_size, buf, size, &delta_size, 0); if (!delta_buf || delta_size != entry->delta_size) @@ -249,12 +251,14 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent if (!usable_delta) { if (entry->type == OBJ_BLOB && entry->size > big_file_threshold && - (st = open_istream(entry->idx.sha1, &type, &size, NULL)) != NULL) + (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL) buf = NULL; else { - buf = read_sha1_file(entry->idx.sha1, &type, &size); + buf = read_sha1_file(entry->idx.oid.hash, &type, + &size); if (!buf) - die(_("unable to read %s"), sha1_to_hex(entry->idx.sha1)); + die(_("unable to read %s"), + oid_to_hex(&entry->idx.oid)); } /* * make sure no cached delta data remains from a @@ -322,7 +326,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent return 0; } sha1write(f, header, hdrlen); - sha1write(f, entry->delta->idx.sha1, 20); + sha1write(f, entry->delta->idx.oid.hash, 20); hdrlen += 20; } else { if (limit && hdrlen + datalen + 20 >= limit) { @@ -334,7 +338,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent sha1write(f, header, hdrlen); } if (st) { - datalen = write_large_blob_data(st, f, entry->idx.sha1); + datalen = write_large_blob_data(st, f, entry->idx.oid.hash); close_istream(st); } else { sha1write(f, buf, datalen); @@ -369,7 +373,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, datalen = revidx[1].offset - offset; if (!pack_to_stdout && p->index_version > 1 && check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) { - error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1)); + error("bad packed object CRC for %s", + oid_to_hex(&entry->idx.oid)); unuse_pack(&w_curs); return write_no_reuse_object(f, entry, limit, usable_delta); } @@ -379,7 +384,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, if (!pack_to_stdout && p->index_version == 1 && check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) { - error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1)); + error("corrupt packed object for %s", + oid_to_hex(&entry->idx.oid)); unuse_pack(&w_curs); return write_no_reuse_object(f, entry, limit, usable_delta); } @@ -404,7 +410,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry, return 0; } sha1write(f, header, hdrlen); - sha1write(f, entry->delta->idx.sha1, 20); + sha1write(f, entry->delta->idx.oid.hash, 20); hdrlen += 20; reused_delta++; } else { @@ -509,7 +515,7 @@ static enum write_one_status write_one(struct sha1file *f, recursing = (e->idx.offset == 1); if (recursing) { warning("recursive delta detected for object %s", - sha1_to_hex(e->idx.sha1)); + oid_to_hex(&e->idx.oid)); return WRITE_ONE_RECURSIVE; } else if (e->idx.offset || e->preferred_base) { /* offset is non zero if object is written already. */ @@ -1432,7 +1438,7 @@ static void check_object(struct object_entry *entry) ofs += 1; if (!ofs || MSB(ofs, 7)) { error("delta base offset overflow in pack for %s", - sha1_to_hex(entry->idx.sha1)); + oid_to_hex(&entry->idx.oid)); goto give_up; } c = buf[used_0++]; @@ -1441,7 +1447,7 @@ static void check_object(struct object_entry *entry) ofs = entry->in_pack_offset - ofs; if (ofs <= 0 || ofs >= entry->in_pack_offset) { error("delta base offset out of bound for %s", - sha1_to_hex(entry->idx.sha1)); + oid_to_hex(&entry->idx.oid)); goto give_up; } if (reuse_delta && !entry->preferred_base) { @@ -1498,7 +1504,7 @@ static void check_object(struct object_entry *entry) unuse_pack(&w_curs); } - entry->type = sha1_object_info(entry->idx.sha1, &entry->size); + entry->type = sha1_object_info(entry->idx.oid.hash, &entry->size); /* * The error condition is checked in prepare_pack(). This is * to permit a missing preferred base object to be ignored @@ -1514,7 +1520,7 @@ static int pack_offset_sort(const void *_a, const void *_b) /* avoid filesystem trashing with loose objects */ if (!a->in_pack && !b->in_pack) - return hashcmp(a->idx.sha1, b->idx.sha1); + return oidcmp(&a->idx.oid, &b->idx.oid); if (a->in_pack < b->in_pack) return -1; @@ -1560,7 +1566,8 @@ static void drop_reused_delta(struct object_entry *entry) * And if that fails, the error will be recorded in entry->type * and dealt with in prepare_pack(). */ - entry->type = sha1_object_info(entry->idx.sha1, &entry->size); + entry->type = sha1_object_info(entry->idx.oid.hash, + &entry->size); } } @@ -1852,26 +1859,29 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Load data if not already done */ if (!trg->data) { read_lock(); - trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz); + trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type, + &sz); read_unlock(); if (!trg->data) die("object %s cannot be read", - sha1_to_hex(trg_entry->idx.sha1)); + oid_to_hex(&trg_entry->idx.oid)); if (sz != trg_size) die("object %s inconsistent object length (%lu vs %lu)", - sha1_to_hex(trg_entry->idx.sha1), sz, trg_size); + oid_to_hex(&trg_entry->idx.oid), sz, + trg_size); *mem_usage += sz; } if (!src->data) { read_lock(); - src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz); + src->data = read_sha1_file(src_entry->idx.oid.hash, &type, + &sz); read_unlock(); if (!src->data) { if (src_entry->preferred_base) { static int warned = 0; if (!warned++) warning("object %s cannot be read", - sha1_to_hex(src_entry->idx.sha1)); + oid_to_hex(&src_entry->idx.oid)); /* * Those objects are not included in the * resulting pack. Be resilient and ignore @@ -1881,11 +1891,12 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, return 0; } die("object %s cannot be read", - sha1_to_hex(src_entry->idx.sha1)); + oid_to_hex(&src_entry->idx.oid)); } if (sz != src_size) die("object %s inconsistent object length (%lu vs %lu)", - sha1_to_hex(src_entry->idx.sha1), sz, src_size); + oid_to_hex(&src_entry->idx.oid), sz, + src_size); *mem_usage += sz; } if (!src->index) { @@ -2406,7 +2417,7 @@ static void prepare_pack(int window, int depth) nr_deltas++; if (entry->type < 0) die("unable to get type of object %s", - sha1_to_hex(entry->idx.sha1)); + oid_to_hex(&entry->idx.oid)); } else { if (entry->type < 0) { /* diff --git a/bulk-checkin.c b/bulk-checkin.c index ddb6070c4c..5be7ce5c73 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -69,7 +69,7 @@ static int already_written(struct bulk_checkin_state *state, unsigned char sha1[ /* Might want to keep the list sorted */ for (i = 0; i < state->nr_written; i++) - if (!hashcmp(state->written[i]->sha1, sha1)) + if (!hashcmp(state->written[i]->oid.hash, sha1)) return 1; /* This is a new object we need to keep */ @@ -242,7 +242,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state, state->offset = checkpoint.offset; free(idx); } else { - hashcpy(idx->sha1, result_sha1); + hashcpy(idx->oid.hash, result_sha1); ALLOC_GROW(state->written, state->nr_written + 1, state->alloc_written); diff --git a/fast-import.c b/fast-import.c index 585d5d6e85..e69d219682 100644 --- a/fast-import.c +++ b/fast-import.c @@ -565,7 +565,7 @@ static struct object_entry *new_object(struct object_id *oid) alloc_objects(object_entry_alloc); e = blocks->next_free++; - hashcpy(e->idx.sha1, oid->hash); + oidcpy(&e->idx.oid, oid); return e; } @@ -574,7 +574,7 @@ static struct object_entry *find_object(struct object_id *oid) unsigned int h = oid->hash[0] << 8 | oid->hash[1]; struct object_entry *e; for (e = object_table[h]; e; e = e->next) - if (!hashcmp(oid->hash, e->idx.sha1)) + if (!oidcmp(oid, &e->idx.oid)) return e; return NULL; } @@ -585,7 +585,7 @@ static struct object_entry *insert_object(struct object_id *oid) struct object_entry *e = object_table[h]; while (e) { - if (!hashcmp(oid->hash, e->idx.sha1)) + if (!oidcmp(oid, &e->idx.oid)) return e; e = e->next; } @@ -1849,7 +1849,7 @@ static void dump_marks_helper(FILE *f, for (k = 0; k < 1024; k++) { if (m->data.marked[k]) fprintf(f, ":%" PRIuMAX " %s\n", base + k, - sha1_to_hex(m->data.marked[k]->idx.sha1)); + oid_to_hex(&m->data.marked[k]->idx.oid)); } } } @@ -2389,7 +2389,7 @@ static void file_change_m(const char *p, struct branch *b) if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); - hashcpy(oid.hash, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else if (skip_prefix(p, "inline ", &p)) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ @@ -2555,7 +2555,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa /* or 'inline' */ if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); - hashcpy(oid.hash, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else if (skip_prefix(p, "inline ", &p)) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ @@ -2578,7 +2578,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa struct object_entry *commit_oe = find_mark(commit_mark); if (commit_oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", commit_mark); - hashcpy(commit_oid.hash, commit_oe->idx.sha1); + oidcpy(&commit_oid, &commit_oe->idx.oid); } else if (!get_oid(p, &commit_oid)) { unsigned long size; char *buf = read_object_with_reference(commit_oid.hash, @@ -2683,8 +2683,8 @@ static int parse_from(struct branch *b) struct object_entry *oe = find_mark(idnum); if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); - if (hashcmp(b->oid.hash, oe->idx.sha1)) { - hashcpy(b->oid.hash, oe->idx.sha1); + if (oidcmp(&b->oid, &oe->idx.oid)) { + oidcpy(&b->oid, &oe->idx.oid); if (oe->pack_id != MAX_PACK_ID) { unsigned long size; char *buf = gfi_unpack_entry(oe, &size); @@ -2727,7 +2727,7 @@ static struct hash_list *parse_merge(unsigned int *count) struct object_entry *oe = find_mark(idnum); if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); - hashcpy(n->oid.hash, oe->idx.sha1); + oidcpy(&n->oid, &oe->idx.oid); } else if (!get_oid(from, &n->oid)) { unsigned long size; char *buf = read_object_with_reference(n->oid.hash, @@ -2884,7 +2884,7 @@ static void parse_new_tag(const char *arg) from_mark = parse_mark_ref_eol(from); oe = find_mark(from_mark); type = oe->type; - hashcpy(oid.hash, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else if (!get_oid(from, &oid)) { struct object_entry *oe = find_object(&oid); if (!oe) { @@ -3014,7 +3014,7 @@ static void parse_get_mark(const char *p) if (!oe) die("Unknown mark: %s", command_buf.buf); - xsnprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1)); + xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid)); cat_blob_write(output, GIT_SHA1_HEXSZ + 1); } @@ -3028,7 +3028,7 @@ static void parse_cat_blob(const char *p) oe = find_mark(parse_mark_ref_eol(p)); if (!oe) die("Unknown mark: %s", command_buf.buf); - hashcpy(oid.hash, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else { if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); @@ -3100,7 +3100,7 @@ static struct object_entry *parse_treeish_dataref(const char **p) e = find_mark(parse_mark_ref_space(p)); if (!e) die("Unknown mark: %s", command_buf.buf); - hashcpy(oid.hash, e->idx.sha1); + oidcpy(&oid, &e->idx.oid); } else { /* */ if (parse_oid_hex(*p, &oid, p)) die("Invalid dataref: %s", command_buf.buf); @@ -3154,7 +3154,7 @@ static void parse_ls(const char *p, struct branch *b) } else { struct object_entry *e = parse_treeish_dataref(&p); root = new_tree_entry(); - hashcpy(root->versions[1].oid.hash, e->idx.sha1); + oidcpy(&root->versions[1].oid, &e->idx.oid); if (!is_null_oid(&root->versions[1].oid)) root->versions[1].mode = S_IFDIR; load_tree(root); diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index e313f4f2bc..8e47a96b3b 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -73,7 +73,8 @@ void bitmap_writer_build_type_index(struct pack_idx_entry **index, break; default: - real_type = sha1_object_info(entry->idx.sha1, NULL); + real_type = sha1_object_info(entry->idx.oid.hash, + NULL); break; } @@ -96,7 +97,8 @@ void bitmap_writer_build_type_index(struct pack_idx_entry **index, default: die("Missing type information for %s (%d/%d)", - sha1_to_hex(entry->idx.sha1), real_type, entry->type); + oid_to_hex(&entry->idx.oid), real_type, + entry->type); } } } @@ -459,7 +461,7 @@ static inline void dump_bitmap(struct sha1file *f, struct ewah_bitmap *bitmap) static const unsigned char *sha1_access(size_t pos, void *table) { struct pack_idx_entry **index = table; - return index[pos]->sha1; + return index[pos]->oid.hash; } static void write_selected_commits_v1(struct sha1file *f, diff --git a/pack-objects.c b/pack-objects.c index 6398a8aa96..9558d13834 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -14,7 +14,7 @@ static uint32_t locate_object_entry_hash(struct packing_data *pdata, while (pdata->index[i] > 0) { uint32_t pos = pdata->index[i] - 1; - if (!hashcmp(sha1, pdata->objects[pos].idx.sha1)) { + if (!hashcmp(sha1, pdata->objects[pos].idx.oid.hash)) { *found = 1; return i; } @@ -53,7 +53,9 @@ static void rehash_objects(struct packing_data *pdata) for (i = 0; i < pdata->nr_objects; i++) { int found; - uint32_t ix = locate_object_entry_hash(pdata, entry->idx.sha1, &found); + uint32_t ix = locate_object_entry_hash(pdata, + entry->idx.oid.hash, + &found); if (found) die("BUG: Duplicate object in hash"); @@ -98,7 +100,7 @@ struct object_entry *packlist_alloc(struct packing_data *pdata, new_entry = pdata->objects + pdata->nr_objects++; memset(new_entry, 0, sizeof(*new_entry)); - hashcpy(new_entry->idx.sha1, sha1); + hashcpy(new_entry->idx.oid.hash, sha1); if (pdata->index_size * 3 <= pdata->nr_objects * 4) rehash_objects(pdata); diff --git a/pack-write.c b/pack-write.c index fa97b72559..a333ec6754 100644 --- a/pack-write.c +++ b/pack-write.c @@ -13,7 +13,7 @@ static int sha1_compare(const void *_a, const void *_b) { struct pack_idx_entry *a = *(struct pack_idx_entry **)_a; struct pack_idx_entry *b = *(struct pack_idx_entry **)_b; - return hashcmp(a->sha1, b->sha1); + return oidcmp(&a->oid, &b->oid); } static int cmp_uint32(const void *a_, const void *b_) @@ -103,7 +103,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec struct pack_idx_entry **next = list; while (next < last) { struct pack_idx_entry *obj = *next; - if (obj->sha1[0] != i) + if (obj->oid.hash[0] != i) break; next++; } @@ -122,11 +122,11 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec uint32_t offset = htonl(obj->offset); sha1write(f, &offset, 4); } - sha1write(f, obj->sha1, 20); + sha1write(f, obj->oid.hash, 20); if ((opts->flags & WRITE_IDX_STRICT) && - (i && !hashcmp(list[-2]->sha1, obj->sha1))) + (i && !oidcmp(&list[-2]->oid, &obj->oid))) die("The same object %s appears twice in the pack", - sha1_to_hex(obj->sha1)); + oid_to_hex(&obj->oid)); } if (index_version >= 2) { diff --git a/pack.h b/pack.h index 5c2158746e..c7de42ef34 100644 --- a/pack.h +++ b/pack.h @@ -67,7 +67,7 @@ struct pack_idx_header { * Common part of object structure used for write_idx_file */ struct pack_idx_entry { - unsigned char sha1[20]; + struct object_id oid; uint32_t crc32; off_t offset; }; From 834bc47b42926dac7bb4d9df43bd1ea58ac794ce Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:12 +0000 Subject: [PATCH 151/566] builtin/unpack-objects: convert to struct object_id Convert struct delta_info and struct object_info, as well as the various functions, to use struct object_id. Convert several hard-coded 20 values to GIT_SHA1_RAWSZ. Among the functions converted is a caller of lookup_blob, which we will convert shortly. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/unpack-objects.c | 64 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 4532aa0831..3dc5e56918 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -127,7 +127,7 @@ static void *get_data(unsigned long size) } struct delta_info { - unsigned char base_sha1[20]; + struct object_id base_oid; unsigned nr; off_t base_offset; unsigned long size; @@ -137,13 +137,13 @@ struct delta_info { static struct delta_info *delta_list; -static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1, +static void add_delta_to_list(unsigned nr, const struct object_id *base_oid, off_t base_offset, void *delta, unsigned long size) { struct delta_info *info = xmalloc(sizeof(*info)); - hashcpy(info->base_sha1, base_sha1); + oidcpy(&info->base_oid, base_oid); info->base_offset = base_offset; info->size = size; info->delta = delta; @@ -154,7 +154,7 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1, struct obj_info { off_t offset; - unsigned char sha1[20]; + struct object_id oid; struct object *obj; }; @@ -170,9 +170,9 @@ static unsigned nr_objects; */ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) { - unsigned char sha1[20]; + struct object_id oid; - if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0) + if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), oid.hash) < 0) die("failed to write object %s", oid_to_hex(&obj->oid)); obj->flags |= FLAG_WRITTEN; } @@ -237,19 +237,19 @@ static void write_object(unsigned nr, enum object_type type, void *buf, unsigned long size) { if (!strict) { - if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) + if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0) die("failed to write object"); added_object(nr, type, buf, size); free(buf); obj_list[nr].obj = NULL; } else if (type == OBJ_BLOB) { struct blob *blob; - if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) + if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0) die("failed to write object"); added_object(nr, type, buf, size); free(buf); - blob = lookup_blob(obj_list[nr].sha1); + blob = lookup_blob(obj_list[nr].oid.hash); if (blob) blob->object.flags |= FLAG_WRITTEN; else @@ -258,9 +258,9 @@ static void write_object(unsigned nr, enum object_type type, } else { struct object *obj; int eaten; - hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1); + hash_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash); added_object(nr, type, buf, size); - obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten); + obj = parse_object_buffer(obj_list[nr].oid.hash, type, size, buf, &eaten); if (!obj) die("invalid %s", typename(type)); add_object_buffer(obj, buf, size); @@ -296,7 +296,7 @@ static void added_object(unsigned nr, enum object_type type, struct delta_info *info; while ((info = *p) != NULL) { - if (!hashcmp(info->base_sha1, obj_list[nr].sha1) || + if (!oidcmp(&info->base_oid, &obj_list[nr].oid) || info->base_offset == obj_list[nr].offset) { *p = info->next; p = &delta_list; @@ -320,12 +320,12 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size, free(buf); } -static int resolve_against_held(unsigned nr, const unsigned char *base, +static int resolve_against_held(unsigned nr, const struct object_id *base, void *delta_data, unsigned long delta_size) { struct object *obj; struct obj_buffer *obj_buffer; - obj = lookup_object(base); + obj = lookup_object(base->hash); if (!obj) return 0; obj_buffer = lookup_object_buffer(obj); @@ -341,25 +341,25 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, { void *delta_data, *base; unsigned long base_size; - unsigned char base_sha1[20]; + struct object_id base_oid; if (type == OBJ_REF_DELTA) { - hashcpy(base_sha1, fill(20)); - use(20); + hashcpy(base_oid.hash, fill(GIT_SHA1_RAWSZ)); + use(GIT_SHA1_RAWSZ); delta_data = get_data(delta_size); if (dry_run || !delta_data) { free(delta_data); return; } - if (has_sha1_file(base_sha1)) + if (has_object_file(&base_oid)) ; /* Ok we have this one */ - else if (resolve_against_held(nr, base_sha1, + else if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) return; /* we are done */ else { /* cannot resolve yet --- queue it */ - hashclr(obj_list[nr].sha1); - add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size); + oidclr(&obj_list[nr].oid); + add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size); return; } } else { @@ -399,8 +399,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, } else if (base_offset > obj_list[mid].offset) { lo = mid + 1; } else { - hashcpy(base_sha1, obj_list[mid].sha1); - base_found = !is_null_sha1(base_sha1); + oidcpy(&base_oid, &obj_list[mid].oid); + base_found = !is_null_oid(&base_oid); break; } } @@ -409,19 +409,19 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, * The delta base object is itself a delta that * has not been resolved yet. */ - hashclr(obj_list[nr].sha1); - add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size); + oidclr(&obj_list[nr].oid); + add_delta_to_list(nr, &null_oid, base_offset, delta_data, delta_size); return; } } - if (resolve_against_held(nr, base_sha1, delta_data, delta_size)) + if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) return; - base = read_sha1_file(base_sha1, &type, &base_size); + base = read_sha1_file(base_oid.hash, &type, &base_size); if (!base) { error("failed to read delta-pack base object %s", - sha1_to_hex(base_sha1)); + oid_to_hex(&base_oid)); if (!recover) exit(1); has_errors = 1; @@ -505,7 +505,7 @@ static void unpack_all(void) int cmd_unpack_objects(int argc, const char **argv, const char *prefix) { int i; - unsigned char sha1[20]; + struct object_id oid; check_replace_refs = 0; @@ -566,12 +566,12 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) git_SHA1_Init(&ctx); unpack_all(); git_SHA1_Update(&ctx, buffer, offset); - git_SHA1_Final(sha1, &ctx); + git_SHA1_Final(oid.hash, &ctx); if (strict) write_rest(); - if (hashcmp(fill(20), sha1)) + if (hashcmp(fill(GIT_SHA1_RAWSZ), oid.hash)) die("final sha1 did not match"); - use(20); + use(GIT_SHA1_RAWSZ); /* Write the last part of the buffer to stdout */ while (len) { From 3e9309815da9aab4e13195b7c6a52c1f7161562a Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:13 +0000 Subject: [PATCH 152/566] Convert remaining callers of lookup_blob to object_id All but a few callers of lookup_blob have been converted to struct object_id. Introduce a temporary, which will be removed later, into parse_object to ease the transition, and convert the remaining callers so that we can update lookup_blob to take struct object_id *. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/index-pack.c | 28 ++++++++++++++-------------- builtin/merge-tree.c | 10 +++++----- object.c | 9 ++++++--- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index fef0025e49..2241ee68ed 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -785,7 +785,7 @@ static int check_collison(struct object_entry *entry) static void sha1_object(const void *data, struct object_entry *obj_entry, unsigned long size, enum object_type type, - const unsigned char *sha1) + const struct object_id *oid) { void *new_data = NULL; int collision_test_needed = 0; @@ -794,7 +794,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (startup_info->have_repository) { read_lock(); - collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK); + collision_test_needed = has_sha1_file_with_flags(oid->hash, HAS_SHA1_QUICK); read_unlock(); } @@ -809,31 +809,31 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, enum object_type has_type; unsigned long has_size; read_lock(); - has_type = sha1_object_info(sha1, &has_size); + has_type = sha1_object_info(oid->hash, &has_size); if (has_type < 0) - die(_("cannot read existing object info %s"), sha1_to_hex(sha1)); + die(_("cannot read existing object info %s"), oid_to_hex(oid)); if (has_type != type || has_size != size) - die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1)); - has_data = read_sha1_file(sha1, &has_type, &has_size); + die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); + has_data = read_sha1_file(oid->hash, &has_type, &has_size); read_unlock(); if (!data) data = new_data = get_data_from_pack(obj_entry); if (!has_data) - die(_("cannot read existing object %s"), sha1_to_hex(sha1)); + die(_("cannot read existing object %s"), oid_to_hex(oid)); if (size != has_size || type != has_type || memcmp(data, has_data, size) != 0) - die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1)); + die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); free(has_data); } if (strict) { read_lock(); if (type == OBJ_BLOB) { - struct blob *blob = lookup_blob(sha1); + struct blob *blob = lookup_blob(oid->hash); if (blob) blob->object.flags |= FLAG_CHECKED; else - die(_("invalid blob object %s"), sha1_to_hex(sha1)); + die(_("invalid blob object %s"), oid_to_hex(oid)); } else { struct object *obj; int eaten; @@ -845,7 +845,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, * we do not need to free the memory here, as the * buf is deleted by the caller. */ - obj = parse_object_buffer(sha1, type, size, buf, &eaten); + obj = parse_object_buffer(oid->hash, type, size, buf, &eaten); if (!obj) die(_("invalid %s"), typename(type)); if (do_fsck_object && @@ -960,7 +960,7 @@ static void resolve_delta(struct object_entry *delta_obj, typename(delta_obj->real_type), delta_obj->idx.oid.hash); sha1_object(result->data, NULL, result->size, delta_obj->real_type, - delta_obj->idx.oid.hash); + &delta_obj->idx.oid); counter_lock(); nr_resolved_deltas++; counter_unlock(); @@ -1149,7 +1149,7 @@ static void parse_pack_objects(unsigned char *sha1) nr_delays++; } else sha1_object(data, NULL, obj->size, obj->type, - obj->idx.oid.hash); + &obj->idx.oid); free(data); display_progress(progress, i+1); } @@ -1176,7 +1176,7 @@ static void parse_pack_objects(unsigned char *sha1) continue; obj->real_type = obj->type; sha1_object(NULL, obj, obj->size, obj->type, - obj->idx.oid.hash); + &obj->idx.oid); nr_delays--; } if (nr_delays) diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 5b7ab9b967..cdeb6562da 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -161,14 +161,14 @@ static int both_empty(struct name_entry *a, struct name_entry *b) return !(a->oid || b->oid); } -static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path) +static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path) { struct merge_list *res = xcalloc(1, sizeof(*res)); res->stage = stage; res->path = path; res->mode = mode; - res->blob = lookup_blob(sha1); + res->blob = lookup_blob(oid->hash); return res; } @@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s return; path = traverse_path(info, result); - orig = create_entry(2, ours->mode, ours->oid->hash, path); - final = create_entry(0, result->mode, result->oid->hash, path); + orig = create_entry(2, ours->mode, ours->oid, path); + final = create_entry(0, result->mode, result->oid, path); final->link = orig; @@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info path = entry->path; else path = traverse_path(info, n); - link = create_entry(stage, n->mode, n->oid->hash, path); + link = create_entry(stage, n->mode, n->oid, path); link->link = entry; return link; } diff --git a/object.c b/object.c index fe22223566..0208c407a6 100644 --- a/object.c +++ b/object.c @@ -190,7 +190,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t obj = NULL; if (type == OBJ_BLOB) { - struct blob *blob = lookup_blob(sha1); + struct blob *blob = lookup_blob(oid.hash); if (blob) { if (parse_blob_buffer(blob, buffer, size)) return NULL; @@ -251,8 +251,11 @@ struct object *parse_object(const unsigned char *sha1) const unsigned char *repl = lookup_replace_object(sha1); void *buffer; struct object *obj; + struct object_id oid; - obj = lookup_object(sha1); + hashcpy(oid.hash, sha1); + + obj = lookup_object(oid.hash); if (obj && obj->parsed) return obj; @@ -263,7 +266,7 @@ struct object *parse_object(const unsigned char *sha1) error("sha1 mismatch %s", sha1_to_hex(repl)); return NULL; } - parse_blob_buffer(lookup_blob(sha1), NULL, 0); + parse_blob_buffer(lookup_blob(oid.hash), NULL, 0); return lookup_object(sha1); } From 3aca1fc6c9c69fbfce0e6312fc8e3087cb6334a4 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:14 +0000 Subject: [PATCH 153/566] Convert lookup_blob to struct object_id Convert lookup_blob to take a pointer to struct object_id. The commit was created with manual changes to blob.c and blob.h, plus the following semantic patch: @@ expression E1; @@ - lookup_blob(E1.hash) + lookup_blob(&E1) @@ expression E1; @@ - lookup_blob(E1->hash) + lookup_blob(E1) Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- blob.c | 6 +++--- blob.h | 2 +- builtin/fast-export.c | 2 +- builtin/fsck.c | 2 +- builtin/index-pack.c | 2 +- builtin/merge-tree.c | 2 +- builtin/unpack-objects.c | 2 +- fsck.c | 2 +- http-push.c | 2 +- list-objects.c | 2 +- object.c | 4 ++-- reachable.c | 2 +- revision.c | 4 ++-- tag.c | 2 +- walker.c | 2 +- 15 files changed, 19 insertions(+), 19 deletions(-) diff --git a/blob.c b/blob.c index 1fcb8e44b0..fa2ab4f7a7 100644 --- a/blob.c +++ b/blob.c @@ -3,11 +3,11 @@ const char *blob_type = "blob"; -struct blob *lookup_blob(const unsigned char *sha1) +struct blob *lookup_blob(const struct object_id *oid) { - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (!obj) - return create_object(sha1, alloc_blob_node()); + return create_object(oid->hash, alloc_blob_node()); return object_as_type(obj, OBJ_BLOB, 0); } diff --git a/blob.h b/blob.h index 59b394eea3..4460616831 100644 --- a/blob.h +++ b/blob.h @@ -9,7 +9,7 @@ struct blob { struct object object; }; -struct blob *lookup_blob(const unsigned char *sha1); +struct blob *lookup_blob(const struct object_id *oid); int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index b4521cb627..ae36b14db7 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -232,7 +232,7 @@ static void export_blob(const struct object_id *oid) if (anonymize) { buf = anonymize_blob(&size); - object = (struct object *)lookup_blob(oid->hash); + object = (struct object *)lookup_blob(oid); eaten = 0; } else { buf = read_sha1_file(oid->hash, &type, &size); diff --git a/builtin/fsck.c b/builtin/fsck.c index c40e14de65..2f67e8217e 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -781,7 +781,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) mode = active_cache[i]->ce_mode; if (S_ISGITLINK(mode)) continue; - blob = lookup_blob(active_cache[i]->oid.hash); + blob = lookup_blob(&active_cache[i]->oid); if (!blob) continue; obj = &blob->object; diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 2241ee68ed..b75133f625 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -829,7 +829,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (strict) { read_lock(); if (type == OBJ_BLOB) { - struct blob *blob = lookup_blob(oid->hash); + struct blob *blob = lookup_blob(oid); if (blob) blob->object.flags |= FLAG_CHECKED; else diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index cdeb6562da..bad6735c76 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -168,7 +168,7 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const stru res->stage = stage; res->path = path; res->mode = mode; - res->blob = lookup_blob(oid->hash); + res->blob = lookup_blob(oid); return res; } diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 3dc5e56918..7d5efa2b3b 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -249,7 +249,7 @@ static void write_object(unsigned nr, enum object_type type, added_object(nr, type, buf, size); free(buf); - blob = lookup_blob(obj_list[nr].oid.hash); + blob = lookup_blob(&obj_list[nr].oid); if (blob) blob->object.flags |= FLAG_WRITTEN; else diff --git a/fsck.c b/fsck.c index e6152e4e6d..ab3016c0e1 100644 --- a/fsck.c +++ b/fsck.c @@ -365,7 +365,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op result = options->walk(obj, OBJ_TREE, data, options); } else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) { - obj = &lookup_blob(entry.oid->hash)->object; + obj = &lookup_blob(entry.oid)->object; if (name) put_object_name(options, obj, "%s%s", name, entry.path); diff --git a/http-push.c b/http-push.c index 04568e4fbd..9bb5e06482 100644 --- a/http-push.c +++ b/http-push.c @@ -1315,7 +1315,7 @@ static struct object_list **process_tree(struct tree *tree, p = process_tree(lookup_tree(entry.oid->hash), p); break; case OBJ_BLOB: - p = process_blob(lookup_blob(entry.oid->hash), p); + p = process_blob(lookup_blob(entry.oid), p); break; default: /* Subproject commit - not in this repository */ diff --git a/list-objects.c b/list-objects.c index f3ca6aafb7..721e5fb08b 100644 --- a/list-objects.c +++ b/list-objects.c @@ -119,7 +119,7 @@ static void process_tree(struct rev_info *revs, cb_data); else process_blob(revs, - lookup_blob(entry.oid->hash), + lookup_blob(entry.oid), show, base, entry.path, cb_data); } diff --git a/object.c b/object.c index 0208c407a6..2c8d1e5d31 100644 --- a/object.c +++ b/object.c @@ -190,7 +190,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t obj = NULL; if (type == OBJ_BLOB) { - struct blob *blob = lookup_blob(oid.hash); + struct blob *blob = lookup_blob(&oid); if (blob) { if (parse_blob_buffer(blob, buffer, size)) return NULL; @@ -266,7 +266,7 @@ struct object *parse_object(const unsigned char *sha1) error("sha1 mismatch %s", sha1_to_hex(repl)); return NULL; } - parse_blob_buffer(lookup_blob(oid.hash), NULL, 0); + parse_blob_buffer(lookup_blob(&oid), NULL, 0); return lookup_object(sha1); } diff --git a/reachable.c b/reachable.c index a8a979bd4f..8ea0bdd7c0 100644 --- a/reachable.c +++ b/reachable.c @@ -88,7 +88,7 @@ static void add_recent_object(const struct object_id *oid, obj = (struct object *)lookup_tree(oid->hash); break; case OBJ_BLOB: - obj = (struct object *)lookup_blob(oid->hash); + obj = (struct object *)lookup_blob(oid); break; default: die("unknown object type for %s: %s", diff --git a/revision.c b/revision.c index f8e0dee6d5..db2de7a7a8 100644 --- a/revision.c +++ b/revision.c @@ -62,7 +62,7 @@ static void mark_tree_contents_uninteresting(struct tree *tree) mark_tree_uninteresting(lookup_tree(entry.oid->hash)); break; case OBJ_BLOB: - mark_blob_uninteresting(lookup_blob(entry.oid->hash)); + mark_blob_uninteresting(lookup_blob(entry.oid)); break; default: /* Subproject commit - not in this repository */ @@ -1275,7 +1275,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags) if (S_ISGITLINK(ce->ce_mode)) continue; - blob = lookup_blob(ce->oid.hash); + blob = lookup_blob(&ce->oid); if (!blob) die("unable to add index blob to traversal"); add_pending_object_with_path(revs, &blob->object, "", diff --git a/tag.c b/tag.c index 79b78d3583..dff251673e 100644 --- a/tag.c +++ b/tag.c @@ -142,7 +142,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) bufptr = nl + 1; if (!strcmp(type, blob_type)) { - item->tagged = &lookup_blob(oid.hash)->object; + item->tagged = &lookup_blob(&oid)->object; } else if (!strcmp(type, tree_type)) { item->tagged = &lookup_tree(oid.hash)->object; } else if (!strcmp(type, commit_type)) { diff --git a/walker.c b/walker.c index b499fcb729..3d6029c8e2 100644 --- a/walker.c +++ b/walker.c @@ -52,7 +52,7 @@ static int process_tree(struct walker *walker, struct tree *tree) obj = &tree->object; } else { - struct blob *blob = lookup_blob(entry.oid->hash); + struct blob *blob = lookup_blob(entry.oid); if (blob) obj = &blob->object; } From f26efc58c881ff71be0c96ffc66eabf5ac75ba84 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:15 +0000 Subject: [PATCH 154/566] tree: convert read_tree_1 to use struct object_id internally Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- tree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tree.c b/tree.c index 33fa7ee717..21fd80b801 100644 --- a/tree.c +++ b/tree.c @@ -58,7 +58,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, { struct tree_desc desc; struct name_entry entry; - unsigned char sha1[20]; + struct object_id oid; int len, oldlen = base->len; enum interesting retval = entry_not_interesting; @@ -87,7 +87,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, } if (S_ISDIR(entry.mode)) - hashcpy(sha1, entry.oid->hash); + oidcpy(&oid, entry.oid); else if (S_ISGITLINK(entry.mode)) { struct commit *commit; @@ -102,7 +102,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, oid_to_hex(entry.oid), base->buf, entry.path); - hashcpy(sha1, commit->tree->object.oid.hash); + oidcpy(&oid, &commit->tree->object.oid); } else continue; @@ -110,7 +110,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, len = tree_entry_len(&entry); strbuf_add(base, entry.path, len); strbuf_addch(base, '/'); - retval = read_tree_1(lookup_tree(sha1), + retval = read_tree_1(lookup_tree(oid.hash), base, stage, pathspec, fn, context); strbuf_setlen(base, oldlen); From 49a09e74a413cfccee2c249407b46cdd0ea699cd Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:16 +0000 Subject: [PATCH 155/566] builtin/reflog: convert tree_is_complete to take struct object_id Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/reflog.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/reflog.c b/builtin/reflog.c index 4831116ea3..7866a03412 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -55,14 +55,14 @@ struct collect_reflog_cb { #define STUDYING (1u<<11) #define REACHABLE (1u<<12) -static int tree_is_complete(const unsigned char *sha1) +static int tree_is_complete(const struct object_id *oid) { struct tree_desc desc; struct name_entry entry; int complete; struct tree *tree; - tree = lookup_tree(sha1); + tree = lookup_tree(oid->hash); if (!tree) return 0; if (tree->object.flags & SEEN) @@ -73,7 +73,7 @@ static int tree_is_complete(const unsigned char *sha1) if (!tree->buffer) { enum object_type type; unsigned long size; - void *data = read_sha1_file(sha1, &type, &size); + void *data = read_sha1_file(oid->hash, &type, &size); if (!data) { tree->object.flags |= INCOMPLETE; return 0; @@ -85,7 +85,7 @@ static int tree_is_complete(const unsigned char *sha1) complete = 1; while (tree_entry(&desc, &entry)) { if (!has_sha1_file(entry.oid->hash) || - (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) { + (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid))) { tree->object.flags |= INCOMPLETE; complete = 0; } @@ -152,7 +152,7 @@ static int commit_is_complete(struct commit *commit) for (i = 0; i < found.nr; i++) { struct commit *c = (struct commit *)found.objects[i].item; - if (!tree_is_complete(c->tree->object.oid.hash)) { + if (!tree_is_complete(&c->tree->object.oid)) { is_incomplete = 1; c->object.flags |= INCOMPLETE; } From 740ee055c6178fc2dd43c5ccfbd367c4c64d6e0d Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:17 +0000 Subject: [PATCH 156/566] Convert lookup_tree to struct object_id Convert the lookup_tree function to take a pointer to struct object_id. The commit was created with manual changes to tree.c, tree.h, and object.c, plus the following semantic patch: @@ @@ - lookup_tree(EMPTY_TREE_SHA1_BIN) + lookup_tree(&empty_tree_oid) @@ expression E1; @@ - lookup_tree(E1.hash) + lookup_tree(&E1) @@ expression E1; @@ - lookup_tree(E1->hash) + lookup_tree(E1) Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/am.c | 4 ++-- builtin/diff-tree.c | 2 +- builtin/diff.c | 2 +- builtin/reflog.c | 2 +- cache-tree.c | 2 +- commit.c | 2 +- fsck.c | 2 +- http-push.c | 2 +- list-objects.c | 2 +- merge-recursive.c | 6 +++--- object.c | 2 +- reachable.c | 2 +- revision.c | 4 ++-- sequencer.c | 2 +- tag.c | 2 +- tree.c | 8 ++++---- tree.h | 2 +- walker.c | 2 +- 18 files changed, 25 insertions(+), 25 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 650269ac5e..7663f12e64 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1453,9 +1453,9 @@ static void write_index_patch(const struct am_state *state) FILE *fp; if (!get_sha1_tree("HEAD", head.hash)) - tree = lookup_tree(head.hash); + tree = lookup_tree(&head); else - tree = lookup_tree(EMPTY_TREE_SHA1_BIN); + tree = lookup_tree(&empty_tree_oid); fp = xfopen(am_path(state, "patch"), "w"); init_revisions(&rev_info, NULL); diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index e85a449df1..95b8d1ba7c 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -44,7 +44,7 @@ static int stdin_diff_trees(struct tree *tree1, const char *p) struct tree *tree2; if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p) return error("Need exactly two trees, separated by a space"); - tree2 = lookup_tree(oid.hash); + tree2 = lookup_tree(&oid); if (!tree2 || parse_tree(tree2)) return -1; printf("%s %s\n", oid_to_hex(&tree1->object.oid), diff --git a/builtin/diff.c b/builtin/diff.c index a25b4e4ae6..895f928978 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -381,7 +381,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; - tree = lookup_tree(EMPTY_TREE_SHA1_BIN); + tree = lookup_tree(&empty_tree_oid); add_pending_object(&rev, &tree->object, "HEAD"); } break; diff --git a/builtin/reflog.c b/builtin/reflog.c index 7866a03412..7e89e84dce 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -62,7 +62,7 @@ static int tree_is_complete(const struct object_id *oid) int complete; struct tree *tree; - tree = lookup_tree(oid->hash); + tree = lookup_tree(oid); if (!tree) return 0; if (tree->object.flags & SEEN) diff --git a/cache-tree.c b/cache-tree.c index 35d507ed79..4892410212 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -672,7 +672,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) cnt++; else { struct cache_tree_sub *sub; - struct tree *subtree = lookup_tree(entry.oid->hash); + struct tree *subtree = lookup_tree(entry.oid); if (!subtree->object.parsed) parse_tree(subtree); sub = cache_tree_sub(it, entry.path); diff --git a/commit.c b/commit.c index 0f6c9b6bf3..8004008bc3 100644 --- a/commit.c +++ b/commit.c @@ -331,7 +331,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s if (get_sha1_hex(bufptr + 5, parent.hash) < 0) return error("bad tree pointer in commit %s", oid_to_hex(&item->object.oid)); - item->tree = lookup_tree(parent.hash); + item->tree = lookup_tree(&parent); bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; diff --git a/fsck.c b/fsck.c index ab3016c0e1..ee5224aea9 100644 --- a/fsck.c +++ b/fsck.c @@ -358,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op continue; if (S_ISDIR(entry.mode)) { - obj = &lookup_tree(entry.oid->hash)->object; + obj = &lookup_tree(entry.oid)->object; if (name) put_object_name(options, obj, "%s%s/", name, entry.path); diff --git a/http-push.c b/http-push.c index 9bb5e06482..7781f40788 100644 --- a/http-push.c +++ b/http-push.c @@ -1312,7 +1312,7 @@ static struct object_list **process_tree(struct tree *tree, while (tree_entry(&desc, &entry)) switch (object_type(entry.mode)) { case OBJ_TREE: - p = process_tree(lookup_tree(entry.oid->hash), p); + p = process_tree(lookup_tree(entry.oid), p); break; case OBJ_BLOB: p = process_blob(lookup_blob(entry.oid), p); diff --git a/list-objects.c b/list-objects.c index 721e5fb08b..b3931fa434 100644 --- a/list-objects.c +++ b/list-objects.c @@ -110,7 +110,7 @@ static void process_tree(struct rev_info *revs, if (S_ISDIR(entry.mode)) process_tree(revs, - lookup_tree(entry.oid->hash), + lookup_tree(entry.oid), show, base, entry.path, cb_data); else if (S_ISGITLINK(entry.mode)) diff --git a/merge-recursive.c b/merge-recursive.c index 1315a45b93..92e0a63dcc 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -67,7 +67,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two, } if (!oidcmp(&two->object.oid, &shifted)) return two; - return lookup_tree(shifted.hash); + return lookup_tree(&shifted); } static struct commit *make_virtual_commit(struct tree *tree, const char *comment) @@ -304,7 +304,7 @@ struct tree *write_tree_from_memory(struct merge_options *o) return NULL; } - result = lookup_tree(active_cache_tree->oid.hash); + result = lookup_tree(&active_cache_tree->oid); return result; } @@ -2042,7 +2042,7 @@ int merge_recursive(struct merge_options *o, /* if there is no common ancestor, use an empty tree */ struct tree *tree; - tree = lookup_tree(EMPTY_TREE_SHA1_BIN); + tree = lookup_tree(&empty_tree_oid); merged_common_ancestors = make_virtual_commit(tree, "ancestor"); } diff --git a/object.c b/object.c index 2c8d1e5d31..d23c5fad38 100644 --- a/object.c +++ b/object.c @@ -197,7 +197,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t obj = &blob->object; } } else if (type == OBJ_TREE) { - struct tree *tree = lookup_tree(sha1); + struct tree *tree = lookup_tree(&oid); if (tree) { obj = &tree->object; if (!tree->buffer) diff --git a/reachable.c b/reachable.c index 8ea0bdd7c0..3bbc84417f 100644 --- a/reachable.c +++ b/reachable.c @@ -85,7 +85,7 @@ static void add_recent_object(const struct object_id *oid, obj = parse_object_or_die(oid->hash, NULL); break; case OBJ_TREE: - obj = (struct object *)lookup_tree(oid->hash); + obj = (struct object *)lookup_tree(oid); break; case OBJ_BLOB: obj = (struct object *)lookup_blob(oid); diff --git a/revision.c b/revision.c index db2de7a7a8..c2091b6de7 100644 --- a/revision.c +++ b/revision.c @@ -59,7 +59,7 @@ static void mark_tree_contents_uninteresting(struct tree *tree) while (tree_entry(&desc, &entry)) { switch (object_type(entry.mode)) { case OBJ_TREE: - mark_tree_uninteresting(lookup_tree(entry.oid->hash)); + mark_tree_uninteresting(lookup_tree(entry.oid)); break; case OBJ_BLOB: mark_blob_uninteresting(lookup_blob(entry.oid)); @@ -1249,7 +1249,7 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs, int i; if (it->entry_count >= 0) { - struct tree *tree = lookup_tree(it->oid.hash); + struct tree *tree = lookup_tree(&it->oid); add_pending_object_with_path(revs, &tree->object, "", 040000, path->buf); } diff --git a/sequencer.c b/sequencer.c index 0867633602..218895fde1 100644 --- a/sequencer.c +++ b/sequencer.c @@ -344,7 +344,7 @@ static int read_oneliner(struct strbuf *buf, static struct tree *empty_tree(void) { - return lookup_tree(EMPTY_TREE_SHA1_BIN); + return lookup_tree(&empty_tree_oid); } static int error_dirty_index(struct replay_opts *opts) diff --git a/tag.c b/tag.c index dff251673e..062516b405 100644 --- a/tag.c +++ b/tag.c @@ -144,7 +144,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) if (!strcmp(type, blob_type)) { item->tagged = &lookup_blob(&oid)->object; } else if (!strcmp(type, tree_type)) { - item->tagged = &lookup_tree(oid.hash)->object; + item->tagged = &lookup_tree(&oid)->object; } else if (!strcmp(type, commit_type)) { item->tagged = &lookup_commit(&oid)->object; } else if (!strcmp(type, tag_type)) { diff --git a/tree.c b/tree.c index 21fd80b801..28ce930b95 100644 --- a/tree.c +++ b/tree.c @@ -110,7 +110,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, len = tree_entry_len(&entry); strbuf_add(base, entry.path, len); strbuf_addch(base, '/'); - retval = read_tree_1(lookup_tree(oid.hash), + retval = read_tree_1(lookup_tree(&oid), base, stage, pathspec, fn, context); strbuf_setlen(base, oldlen); @@ -184,11 +184,11 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match) return 0; } -struct tree *lookup_tree(const unsigned char *sha1) +struct tree *lookup_tree(const struct object_id *oid) { - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (!obj) - return create_object(sha1, alloc_tree_node()); + return create_object(oid->hash, alloc_tree_node()); return object_as_type(obj, OBJ_TREE, 0); } diff --git a/tree.h b/tree.h index d24786cba2..2b2c8dbbe9 100644 --- a/tree.h +++ b/tree.h @@ -12,7 +12,7 @@ struct tree { unsigned long size; }; -struct tree *lookup_tree(const unsigned char *sha1); +struct tree *lookup_tree(const struct object_id *oid); int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size); diff --git a/walker.c b/walker.c index 3d6029c8e2..eae9fb974f 100644 --- a/walker.c +++ b/walker.c @@ -47,7 +47,7 @@ static int process_tree(struct walker *walker, struct tree *tree) if (S_ISGITLINK(entry.mode)) continue; if (S_ISDIR(entry.mode)) { - struct tree *tree = lookup_tree(entry.oid->hash); + struct tree *tree = lookup_tree(entry.oid); if (tree) obj = &tree->object; } From a92ea68fefe2b7ce61363da3f5e0ae09dd6edba4 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:18 +0000 Subject: [PATCH 157/566] log-tree: convert to struct object_id Convert the remaining functions to take pointers to struct object_id instead of pointers to unsigned char, and update the internals of these functions as well. Among these functions is a caller of lookup_tag, which we will convert shortly. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- log-tree.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/log-tree.c b/log-tree.c index 7fb1a85d2d..169fd039fd 100644 --- a/log-tree.c +++ b/log-tree.c @@ -184,7 +184,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d { const struct name_decoration *list, *head = NULL; const char *branch_name = NULL; - unsigned char unused[20]; + struct object_id unused; int rru_flags; /* First find HEAD */ @@ -197,7 +197,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d return NULL; /* Now resolve and find the matching current branch */ - branch_name = resolve_ref_unsafe("HEAD", 0, unused, &rru_flags); + branch_name = resolve_ref_unsafe("HEAD", 0, unused.hash, &rru_flags); if (!(rru_flags & REF_ISSYMREF)) return NULL; @@ -456,13 +456,13 @@ static void show_signature(struct rev_info *opt, struct commit *commit) strbuf_release(&signature); } -static int which_parent(const unsigned char *sha1, const struct commit *commit) +static int which_parent(const struct object_id *oid, const struct commit *commit) { int nth; const struct commit_list *parent; for (nth = 0, parent = commit->parents; parent; parent = parent->next) { - if (!hashcmp(parent->item->object.oid.hash, sha1)) + if (!oidcmp(&parent->item->object.oid, oid)) return nth; nth++; } @@ -481,14 +481,14 @@ static void show_one_mergetag(struct commit *commit, void *data) { struct rev_info *opt = (struct rev_info *)data; - unsigned char sha1[20]; + struct object_id oid; struct tag *tag; struct strbuf verify_message; int status, nth; size_t payload_size, gpg_message_offset; - hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), sha1); - tag = lookup_tag(sha1); + hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), oid.hash); + tag = lookup_tag(oid.hash); if (!tag) return; /* error message already given */ @@ -500,7 +500,7 @@ static void show_one_mergetag(struct commit *commit, &commit->parents->next->item->object.oid)) strbuf_addf(&verify_message, "merged tag '%s'\n", tag->tag); - else if ((nth = which_parent(tag->tagged->oid.hash, commit)) < 0) + else if ((nth = which_parent(&tag->tagged->oid, commit)) < 0) strbuf_addf(&verify_message, "tag %s names a non-parent %s\n", tag->tag, tag->tagged->oid.hash); else @@ -536,7 +536,7 @@ void show_log(struct rev_info *opt) struct strbuf msgbuf = STRBUF_INIT; struct log_info *log = opt->loginfo; struct commit *commit = log->commit, *parent = log->parent; - int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; + int abbrev_commit = opt->abbrev_commit ? opt->abbrev : GIT_SHA1_HEXSZ; const char *extra_headers = opt->extra_headers; struct pretty_print_context ctx = {0}; From d3101b533d365825f52d8e5e52328702acbc8e3a Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:19 +0000 Subject: [PATCH 158/566] Convert lookup_tag to struct object_id Convert lookup_tag to take a pointer to struct object_id. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/describe.c | 6 +++--- builtin/pack-objects.c | 2 +- builtin/replace.c | 2 +- log-tree.c | 2 +- object.c | 2 +- sha1_name.c | 2 +- tag.c | 8 ++++---- tag.h | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin/describe.c b/builtin/describe.c index f6032f593c..893c8789f4 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -79,13 +79,13 @@ static int replace_name(struct commit_name *e, struct tag *t; if (!e->tag) { - t = lookup_tag(e->oid.hash); + t = lookup_tag(&e->oid); if (!t || parse_tag(t)) return 1; e->tag = t; } - t = lookup_tag(oid->hash); + t = lookup_tag(oid); if (!t || parse_tag(t)) return 0; *tag = t; @@ -245,7 +245,7 @@ static unsigned long finish_depth_computation( static void display_name(struct commit_name *n) { if (n->prio == 2 && !n->tag) { - n->tag = lookup_tag(n->oid.hash); + n->tag = lookup_tag(&n->oid); if (!n->tag || parse_tag(n->tag)) die(_("annotated tag %s not available"), n->path); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index d76ff05426..7cebb5a7f1 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2348,7 +2348,7 @@ static void add_tag_chain(const struct object_id *oid) if (packlist_find(&to_pack, oid->hash, NULL)) return; - tag = lookup_tag(oid->hash); + tag = lookup_tag(oid); while (1) { if (!tag || parse_tag(tag) || !tag->tagged) die("unable to pack objects reachable from tag %s", diff --git a/builtin/replace.c b/builtin/replace.c index 3c44ef750e..c921bc976f 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -355,7 +355,7 @@ static void check_one_mergetag(struct commit *commit, int i; hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash); - tag = lookup_tag(tag_oid.hash); + tag = lookup_tag(&tag_oid); if (!tag) die(_("bad mergetag in commit '%s'"), ref); if (parse_tag_buffer(tag, extra->value, extra->len)) diff --git a/log-tree.c b/log-tree.c index 169fd039fd..6532c892c2 100644 --- a/log-tree.c +++ b/log-tree.c @@ -488,7 +488,7 @@ static void show_one_mergetag(struct commit *commit, size_t payload_size, gpg_message_offset; hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), oid.hash); - tag = lookup_tag(oid.hash); + tag = lookup_tag(&oid); if (!tag) return; /* error message already given */ diff --git a/object.c b/object.c index d23c5fad38..dd4d3a1ea7 100644 --- a/object.c +++ b/object.c @@ -220,7 +220,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t obj = &commit->object; } } else if (type == OBJ_TAG) { - struct tag *tag = lookup_tag(sha1); + struct tag *tag = lookup_tag(&oid); if (tag) { if (parse_tag_buffer(tag, buffer, size)) return NULL; diff --git a/sha1_name.c b/sha1_name.c index 390a09c41a..b7e09ac139 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -361,7 +361,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) format_commit_message(commit, " %ad - %s", &desc, &pp); } } else if (type == OBJ_TAG) { - struct tag *tag = lookup_tag(oid->hash); + struct tag *tag = lookup_tag(oid); if (!parse_tag(tag) && tag->tag) strbuf_addf(&desc, " %s", tag->tag); } diff --git a/tag.c b/tag.c index 062516b405..571798519f 100644 --- a/tag.c +++ b/tag.c @@ -89,11 +89,11 @@ struct object *deref_tag_noverify(struct object *o) return o; } -struct tag *lookup_tag(const unsigned char *sha1) +struct tag *lookup_tag(const struct object_id *oid) { - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (!obj) - return create_object(sha1, alloc_tag_node()); + return create_object(oid->hash, alloc_tag_node()); return object_as_type(obj, OBJ_TAG, 0); } @@ -148,7 +148,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) } else if (!strcmp(type, commit_type)) { item->tagged = &lookup_commit(&oid)->object; } else if (!strcmp(type, tag_type)) { - item->tagged = &lookup_tag(oid.hash)->object; + item->tagged = &lookup_tag(&oid)->object; } else { error("Unknown type %s", type); item->tagged = NULL; diff --git a/tag.h b/tag.h index a5721b6731..8d6fc28145 100644 --- a/tag.h +++ b/tag.h @@ -12,7 +12,7 @@ struct tag { unsigned long date; }; -extern struct tag *lookup_tag(const unsigned char *sha1); +extern struct tag *lookup_tag(const struct object_id *oid); extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long size); extern int parse_tag(struct tag *item); extern struct object *deref_tag(struct object *, const char *, int); From 9fd750461befcaf984d5966606308c8cd6912f3c Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:20 +0000 Subject: [PATCH 159/566] Convert the verify_pack callback to struct object_id Make the verify_pack_callback take a pointer to struct object_id. Change the pack checksum to use GIT_MAX_RAWSZ, even though it is not strictly an object ID. Doing so ensures resilience against future hash size changes, and allows us to remove hard-coded assumptions about how big the buffer needs to be. Also, use a union to convert the pointer from nth_packed_object_sha1 to to a pointer to struct object_id. This behavior is compatible with GCC and clang and explicitly sanctioned by C11. The alternatives are to just perform a cast, which would run afoul of strict aliasing rules, but should just work, and changing the pointer into an instance of struct object_id and copying the value. The latter operation could seriously bloat memory usage on fsck, which already uses a lot of memory on some repositories. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/fsck.c | 6 +++--- pack-check.c | 25 ++++++++++++++----------- pack.h | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/builtin/fsck.c b/builtin/fsck.c index 2f67e8217e..a187054dac 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -377,7 +377,7 @@ static int fsck_obj(struct object *obj) return 0; } -static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type, +static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten) { /* @@ -385,10 +385,10 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type, * verify_packfile(), data_valid variable for details. */ struct object *obj; - obj = parse_object_buffer(sha1, type, size, buffer, eaten); + obj = parse_object_buffer(oid->hash, type, size, buffer, eaten); if (!obj) { errors_found |= ERROR_OBJECT; - return error("%s: object corrupt or missing", sha1_to_hex(sha1)); + return error("%s: object corrupt or missing", oid_to_hex(oid)); } obj->flags = HAS_OBJ; return fsck_obj(obj); diff --git a/pack-check.c b/pack-check.c index 27f70d345f..e1fcb228fa 100644 --- a/pack-check.c +++ b/pack-check.c @@ -5,7 +5,10 @@ struct idx_entry { off_t offset; - const unsigned char *sha1; + union idx_entry_object { + const unsigned char *hash; + struct object_id *oid; + } oid; unsigned int nr; }; @@ -51,7 +54,7 @@ static int verify_packfile(struct packed_git *p, off_t index_size = p->index_size; const unsigned char *index_base = p->index_data; git_SHA_CTX ctx; - unsigned char sha1[20], *pack_sig; + unsigned char hash[GIT_MAX_RAWSZ], *pack_sig; off_t offset = 0, pack_sig_ofs = 0; uint32_t nr_objects, i; int err = 0; @@ -71,9 +74,9 @@ static int verify_packfile(struct packed_git *p, remaining -= (unsigned int)(offset - pack_sig_ofs); git_SHA1_Update(&ctx, in, remaining); } while (offset < pack_sig_ofs); - git_SHA1_Final(sha1, &ctx); + git_SHA1_Final(hash, &ctx); pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL); - if (hashcmp(sha1, pack_sig)) + if (hashcmp(hash, pack_sig)) err = error("%s SHA1 checksum mismatch", p->pack_name); if (hashcmp(index_base + index_size - 40, pack_sig)) @@ -90,8 +93,8 @@ static int verify_packfile(struct packed_git *p, entries[nr_objects].offset = pack_sig_ofs; /* first sort entries by pack offset, since unpacking them is more efficient that way */ for (i = 0; i < nr_objects; i++) { - entries[i].sha1 = nth_packed_object_sha1(p, i); - if (!entries[i].sha1) + entries[i].oid.hash = nth_packed_object_sha1(p, i); + if (!entries[i].oid.hash) die("internal error pack-check nth-packed-object"); entries[i].offset = nth_packed_object_offset(p, i); entries[i].nr = i; @@ -112,7 +115,7 @@ static int verify_packfile(struct packed_git *p, if (check_pack_crc(p, w_curs, offset, len, nr)) err = error("index CRC mismatch for object %s " "from %s at offset %"PRIuMAX"", - sha1_to_hex(entries[i].sha1), + oid_to_hex(entries[i].oid.oid), p->pack_name, (uintmax_t)offset); } @@ -135,14 +138,14 @@ static int verify_packfile(struct packed_git *p, if (data_valid && !data) err = error("cannot unpack %s from %s at offset %"PRIuMAX"", - sha1_to_hex(entries[i].sha1), p->pack_name, + oid_to_hex(entries[i].oid.oid), p->pack_name, (uintmax_t)entries[i].offset); - else if (check_sha1_signature(entries[i].sha1, data, size, typename(type))) + else if (check_sha1_signature(entries[i].oid.hash, data, size, typename(type))) err = error("packed %s from %s is corrupt", - sha1_to_hex(entries[i].sha1), p->pack_name); + oid_to_hex(entries[i].oid.oid), p->pack_name); else if (fn) { int eaten = 0; - err |= fn(entries[i].sha1, type, size, data, &eaten); + err |= fn(entries[i].oid.oid, type, size, data, &eaten); if (eaten) data = NULL; } diff --git a/pack.h b/pack.h index c7de42ef34..8294341af1 100644 --- a/pack.h +++ b/pack.h @@ -75,7 +75,7 @@ struct pack_idx_entry { struct progress; /* Note, the data argument could be NULL if object type is blob */ -typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*); +typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*); extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1); extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr); From cedfc41ac62c691557263a8db41f7f693914d68d Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:21 +0000 Subject: [PATCH 160/566] Convert struct ref_array_item to struct object_id Convert struct ref_array_item to use struct object_id by changing the definition and applying the following semantic patch, plus the standard object_id transforms: @@ struct ref_array_item E1; @@ - E1.objectname + E1.objectname.hash @@ struct ref_array_item *E1; @@ - E1->objectname + E1->objectname.hash This transformation allows us to convert get_obj, which is needed to convert parse_object_buffer. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- ref-filter.c | 10 +++++----- ref-filter.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ref-filter.c b/ref-filter.c index e1d18ac0d1..77aee273f8 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1366,7 +1366,7 @@ static void populate_value(struct ref_array_item *ref) v->s = xstrdup(buf + 1); } continue; - } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) { + } else if (!deref && grab_objectname(name, ref->objectname.hash, v, atom)) { continue; } else if (!strcmp(name, "HEAD")) { const char *head; @@ -1415,13 +1415,13 @@ static void populate_value(struct ref_array_item *ref) return; need_obj: - buf = get_obj(ref->objectname, &obj, &size, &eaten); + buf = get_obj(ref->objectname.hash, &obj, &size, &eaten); if (!buf) die(_("missing object %s for %s"), - sha1_to_hex(ref->objectname), ref->refname); + oid_to_hex(&ref->objectname), ref->refname); if (!obj) die(_("parse_object_buffer failed on %s for %s"), - sha1_to_hex(ref->objectname), ref->refname); + oid_to_hex(&ref->objectname), ref->refname); grab_values(ref->value, 0, obj, buf, size); if (!eaten) @@ -1704,7 +1704,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname, { struct ref_array_item *ref; FLEX_ALLOC_STR(ref, refname, refname); - hashcpy(ref->objectname, objectname); + hashcpy(ref->objectname.hash, objectname); ref->flag = flag; return ref; diff --git a/ref-filter.h b/ref-filter.h index c20167aa3c..6552024f09 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -34,7 +34,7 @@ struct ref_sorting { }; struct ref_array_item { - unsigned char objectname[20]; + struct object_id objectname; int flag; unsigned int kind; const char *symref; From 9850fe5d95ac14585998e6ebeaf158c976954b1b Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:22 +0000 Subject: [PATCH 161/566] ref-filter: convert some static functions to struct object_id Among the converted functions is a caller of parse_object_buffer, which we will convert later. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- ref-filter.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ref-filter.c b/ref-filter.c index 77aee273f8..56fc990a52 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -677,13 +677,13 @@ int verify_ref_format(const char *format) * by the "struct object" representation, set *eaten as well---it is a * signal from parse_object_buffer to us not to free the buffer. */ -static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten) +static void *get_obj(const struct object_id *oid, struct object **obj, unsigned long *sz, int *eaten) { enum object_type type; - void *buf = read_sha1_file(sha1, &type, sz); + void *buf = read_sha1_file(oid->hash, &type, sz); if (buf) - *obj = parse_object_buffer(sha1, type, *sz, buf, eaten); + *obj = parse_object_buffer(oid->hash, type, *sz, buf, eaten); else *obj = NULL; return buf; @@ -1293,7 +1293,7 @@ static void populate_value(struct ref_array_item *ref) struct object *obj; int eaten, i; unsigned long size; - const unsigned char *tagged; + const struct object_id *tagged; ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); @@ -1370,10 +1370,10 @@ static void populate_value(struct ref_array_item *ref) continue; } else if (!strcmp(name, "HEAD")) { const char *head; - unsigned char sha1[20]; + struct object_id oid; head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - sha1, NULL); + oid.hash, NULL); if (head && !strcmp(ref->refname, head)) v->s = "*"; else @@ -1415,7 +1415,7 @@ static void populate_value(struct ref_array_item *ref) return; need_obj: - buf = get_obj(ref->objectname.hash, &obj, &size, &eaten); + buf = get_obj(&ref->objectname, &obj, &size, &eaten); if (!buf) die(_("missing object %s for %s"), oid_to_hex(&ref->objectname), ref->refname); @@ -1438,7 +1438,7 @@ static void populate_value(struct ref_array_item *ref) * If it is a tag object, see if we use a value that derefs * the object, and if we do grab the object it refers to. */ - tagged = ((struct tag *)obj)->tagged->oid.hash; + tagged = &((struct tag *)obj)->tagged->oid; /* * NEEDSWORK: This derefs tag only once, which @@ -1449,10 +1449,10 @@ static void populate_value(struct ref_array_item *ref) buf = get_obj(tagged, &obj, &size, &eaten); if (!buf) die(_("missing object %s for %s"), - sha1_to_hex(tagged), ref->refname); + oid_to_hex(tagged), ref->refname); if (!obj) die(_("parse_object_buffer failed on %s for %s"), - sha1_to_hex(tagged), ref->refname); + oid_to_hex(tagged), ref->refname); grab_values(ref->value, 1, obj, buf, size); if (!eaten) free(buf); From 984912989d6c4bc4c34bcf4296173ed96b22d272 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:23 +0000 Subject: [PATCH 162/566] refs: convert struct ref_update to use struct object_id Convert struct ref_array_item to use struct object_id by changing the definition and applying the following semantic patch, plus the standard object_id transforms: @@ struct ref_update E1; @@ - E1.new_sha1 + E1.new_oid.hash @@ struct ref_update *E1; @@ - E1->new_sha1 + E1->new_oid.hash @@ struct ref_update E1; @@ - E1.old_sha1 + E1.old_oid.hash @@ struct ref_update *E1; @@ - E1->old_sha1 + E1->old_oid.hash This transformation allows us to convert write_ref_to_lockfile, which is required to convert parse_object. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- refs.c | 4 ++-- refs/files-backend.c | 29 +++++++++++++++-------------- refs/refs-internal.h | 4 ++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/refs.c b/refs.c index df75f8e0d6..c738f59116 100644 --- a/refs.c +++ b/refs.c @@ -882,9 +882,9 @@ struct ref_update *ref_transaction_add_update( update->flags = flags; if (flags & REF_HAVE_NEW) - hashcpy(update->new_sha1, new_sha1); + hashcpy(update->new_oid.hash, new_sha1); if (flags & REF_HAVE_OLD) - hashcpy(update->old_sha1, old_sha1); + hashcpy(update->old_oid.hash, old_sha1); update->msg = xstrdup_or_null(msg); return update; } diff --git a/refs/files-backend.c b/refs/files-backend.c index 298a838c5e..413505ff07 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2633,7 +2633,7 @@ static int split_head_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, "HEAD", update->flags | REF_LOG_ONLY | REF_NODEREF, - update->new_sha1, update->old_sha1, + update->new_oid.hash, update->old_oid.hash, update->msg); item->util = new_update; @@ -2690,7 +2690,7 @@ static int split_symref_update(struct files_ref_store *refs, new_update = ref_transaction_add_update( transaction, referent, new_flags, - update->new_sha1, update->old_sha1, + update->new_oid.hash, update->old_oid.hash, update->msg); new_update->parent_update = update; @@ -2729,10 +2729,10 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, struct strbuf *err) { if (!(update->flags & REF_HAVE_OLD) || - !hashcmp(oid->hash, update->old_sha1)) + !oidcmp(oid, &update->old_oid)) return 0; - if (is_null_sha1(update->old_sha1)) + if (is_null_oid(&update->old_oid)) strbuf_addf(err, "cannot lock ref '%s': " "reference already exists", original_update_refname(update)); @@ -2740,13 +2740,13 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, strbuf_addf(err, "cannot lock ref '%s': " "reference is missing but expected %s", original_update_refname(update), - sha1_to_hex(update->old_sha1)); + oid_to_hex(&update->old_oid)); else strbuf_addf(err, "cannot lock ref '%s': " "is at %s but expected %s", original_update_refname(update), oid_to_hex(oid), - sha1_to_hex(update->old_sha1)); + oid_to_hex(&update->old_oid)); return -1; } @@ -2773,13 +2773,13 @@ static int lock_ref_for_update(struct files_ref_store *refs, { struct strbuf referent = STRBUF_INIT; int mustexist = (update->flags & REF_HAVE_OLD) && - !is_null_sha1(update->old_sha1); + !is_null_oid(&update->old_oid); int ret; struct ref_lock *lock; files_assert_main_repository(refs, "lock_ref_for_update"); - if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) + if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid)) update->flags |= REF_DELETING; if (head_ref) { @@ -2861,12 +2861,12 @@ static int lock_ref_for_update(struct files_ref_store *refs, !(update->flags & REF_DELETING) && !(update->flags & REF_LOG_ONLY)) { if (!(update->type & REF_ISSYMREF) && - !hashcmp(lock->old_oid.hash, update->new_sha1)) { + !oidcmp(&lock->old_oid, &update->new_oid)) { /* * The reference already has the desired * value, so we don't need to write it. */ - } else if (write_ref_to_lockfile(lock, update->new_sha1, + } else if (write_ref_to_lockfile(lock, update->new_oid.hash, err)) { char *write_err = strbuf_detach(err, NULL); @@ -3002,7 +3002,7 @@ static int files_transaction_commit(struct ref_store *ref_store, if (files_log_ref_write(refs, lock->ref_name, lock->old_oid.hash, - update->new_sha1, + update->new_oid.hash, update->msg, update->flags, err)) { char *old_msg = strbuf_detach(err, NULL); @@ -3151,7 +3151,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, struct ref_update *update = transaction->updates[i]; if ((update->flags & REF_HAVE_OLD) && - !is_null_sha1(update->old_sha1)) + !is_null_oid(&update->old_oid)) die("BUG: initial ref transaction with old_sha1 set"); if (refs_verify_refname_available(&refs->base, update->refname, &affected_refnames, NULL, @@ -3172,8 +3172,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, struct ref_update *update = transaction->updates[i]; if ((update->flags & REF_HAVE_NEW) && - !is_null_sha1(update->new_sha1)) - add_packed_ref(refs, update->refname, update->new_sha1); + !is_null_oid(&update->new_oid)) + add_packed_ref(refs, update->refname, + update->new_oid.hash); } if (commit_packed_refs(refs)) { diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 3d46131efb..b267d5ca9e 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -130,13 +130,13 @@ struct ref_update { /* * If (flags & REF_HAVE_NEW), set the reference to this value: */ - unsigned char new_sha1[20]; + struct object_id new_oid; /* * If (flags & REF_HAVE_OLD), check that the reference * previously had this value: */ - unsigned char old_sha1[20]; + struct object_id old_oid; /* * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF, From 4417df8c492489613d031478ba2e3604f65a5f84 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:24 +0000 Subject: [PATCH 163/566] refs/files-backend: convert many internals to struct object_id Convert many of the internals of the files backend to use struct object_id. Avoid converting public APIs (except one change to refs/ref-cache.c) to limit the scope of the changes. Convert one use of get_sha1_hex to parse_oid_hex, and rely on the fact that a strbuf will be NUL-terminated and that parse_oid_hex will fail on truncated input to avoid the need to check the length. This is a requirement to convert parse_object later on. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- refs/files-backend.c | 128 ++++++++++++++++++++----------------------- refs/ref-cache.c | 4 +- refs/ref-cache.h | 2 +- 3 files changed, 61 insertions(+), 73 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 413505ff07..2c360a4107 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -195,27 +195,15 @@ static const char PACKED_REFS_HEADER[] = * Return a pointer to the refname within the line (null-terminated), * or NULL if there was a problem. */ -static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1) +static const char *parse_ref_line(struct strbuf *line, struct object_id *oid) { const char *ref; - /* - * 42: the answer to everything. - * - * In this case, it happens to be the answer to - * 40 (length of sha1 hex representation) - * +1 (space in between hex and name) - * +1 (newline at the end of the line) - */ - if (line->len <= 42) + if (parse_oid_hex(line->buf, oid, &ref) < 0) + return NULL; + if (!isspace(*ref++)) return NULL; - if (get_sha1_hex(line->buf, sha1) < 0) - return NULL; - if (!isspace(line->buf[40])) - return NULL; - - ref = line->buf + 41; if (isspace(*ref)) return NULL; @@ -260,7 +248,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE; while (strbuf_getwholeline(&line, f, '\n') != EOF) { - unsigned char sha1[20]; + struct object_id oid; const char *refname; const char *traits; @@ -273,17 +261,17 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) continue; } - refname = parse_ref_line(&line, sha1); + refname = parse_ref_line(&line, &oid); if (refname) { int flag = REF_ISPACKED; if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { if (!refname_is_safe(refname)) die("packed refname is dangerous: %s", refname); - hashclr(sha1); + oidclr(&oid); flag |= REF_BAD_NAME | REF_ISBROKEN; } - last = create_ref_entry(refname, sha1, flag, 0); + last = create_ref_entry(refname, &oid, flag, 0); if (peeled == PEELED_FULLY || (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/"))) last->flag |= REF_KNOWS_PEELED; @@ -294,8 +282,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) line.buf[0] == '^' && line.len == PEELED_LINE_LENGTH && line.buf[PEELED_LINE_LENGTH - 1] == '\n' && - !get_sha1_hex(line.buf + 1, sha1)) { - hashcpy(last->u.value.peeled.hash, sha1); + !get_oid_hex(line.buf + 1, &oid)) { + oidcpy(&last->u.value.peeled, &oid); /* * Regardless of what the file header said, * we definitely know the value of *this* @@ -404,14 +392,14 @@ static struct ref_dir *get_packed_refs(struct files_ref_store *refs) * commit_packed_refs(). */ static void add_packed_ref(struct files_ref_store *refs, - const char *refname, const unsigned char *sha1) + const char *refname, const struct object_id *oid) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs); if (!packed_ref_cache->lock) die("internal error: packed refs not locked"); add_ref_entry(get_packed_ref_dir(packed_ref_cache), - create_ref_entry(refname, sha1, REF_ISPACKED, 1)); + create_ref_entry(refname, oid, REF_ISPACKED, 1)); } /* @@ -444,7 +432,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_add(&refname, dirname, dirnamelen); while ((de = readdir(d)) != NULL) { - unsigned char sha1[20]; + struct object_id oid; struct stat st; int flag; @@ -465,10 +453,10 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, if (!refs_resolve_ref_unsafe(&refs->base, refname.buf, RESOLVE_REF_READING, - sha1, &flag)) { - hashclr(sha1); + oid.hash, &flag)) { + oidclr(&oid); flag |= REF_ISBROKEN; - } else if (is_null_sha1(sha1)) { + } else if (is_null_oid(&oid)) { /* * It is so astronomically unlikely * that NULL_SHA1 is the SHA-1 of an @@ -484,11 +472,11 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, REFNAME_ALLOW_ONELEVEL)) { if (!refname_is_safe(refname.buf)) die("loose refname is dangerous: %s", refname.buf); - hashclr(sha1); + oidclr(&oid); flag |= REF_BAD_NAME | REF_ISBROKEN; } add_entry_to_dir(dir, - create_ref_entry(refname.buf, sha1, flag, 0)); + create_ref_entry(refname.buf, &oid, flag, 0)); } strbuf_setlen(&refname, dirnamelen); strbuf_setlen(&path, path_baselen); @@ -1526,7 +1514,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) packed_entry->flag = REF_ISPACKED; oidcpy(&packed_entry->u.value.oid, iter->oid); } else { - packed_entry = create_ref_entry(iter->refname, iter->oid->hash, + packed_entry = create_ref_entry(iter->refname, iter->oid, REF_ISPACKED, 0); add_ref_entry(packed_refs, packed_entry); } @@ -1709,10 +1697,10 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname) } static int write_ref_to_lockfile(struct ref_lock *lock, - const unsigned char *sha1, struct strbuf *err); + const struct object_id *oid, struct strbuf *err); static int commit_ref_update(struct files_ref_store *refs, struct ref_lock *lock, - const unsigned char *sha1, const char *logmsg, + const struct object_id *oid, const char *logmsg, struct strbuf *err); static int files_rename_ref(struct ref_store *ref_store, @@ -1721,7 +1709,7 @@ static int files_rename_ref(struct ref_store *ref_store, { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "rename_ref"); - unsigned char sha1[20], orig_sha1[20]; + struct object_id oid, orig_oid; int flag = 0, logmoved = 0; struct ref_lock *lock; struct stat loginfo; @@ -1743,7 +1731,7 @@ static int files_rename_ref(struct ref_store *ref_store, if (!refs_resolve_ref_unsafe(&refs->base, oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, - orig_sha1, &flag)) { + orig_oid.hash, &flag)) { ret = error("refname %s not found", oldrefname); goto out; } @@ -1765,21 +1753,21 @@ static int files_rename_ref(struct ref_store *ref_store, } if (refs_delete_ref(&refs->base, logmsg, oldrefname, - orig_sha1, REF_NODEREF)) { + orig_oid.hash, REF_NODEREF)) { error("unable to delete old %s", oldrefname); goto rollback; } /* - * Since we are doing a shallow lookup, sha1 is not the - * correct value to pass to delete_ref as old_sha1. But that - * doesn't matter, because an old_sha1 check wouldn't add to + * Since we are doing a shallow lookup, oid is not the + * correct value to pass to delete_ref as old_oid. But that + * doesn't matter, because an old_oid check wouldn't add to * the safety anyway; we want to delete the reference whatever * its current value. */ if (!refs_read_ref_full(&refs->base, newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, - sha1, NULL) && + oid.hash, NULL) && refs_delete_ref(&refs->base, NULL, newrefname, NULL, REF_NODEREF)) { if (errno == EISDIR) { @@ -1812,10 +1800,10 @@ static int files_rename_ref(struct ref_store *ref_store, strbuf_release(&err); goto rollback; } - hashcpy(lock->old_oid.hash, orig_sha1); + oidcpy(&lock->old_oid, &orig_oid); - if (write_ref_to_lockfile(lock, orig_sha1, &err) || - commit_ref_update(refs, lock, orig_sha1, logmsg, &err)) { + if (write_ref_to_lockfile(lock, &orig_oid, &err) || + commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) { error("unable to write current sha1 into %s: %s", newrefname, err.buf); strbuf_release(&err); goto rollback; @@ -1835,8 +1823,8 @@ static int files_rename_ref(struct ref_store *ref_store, flag = log_all_ref_updates; log_all_ref_updates = LOG_REFS_NONE; - if (write_ref_to_lockfile(lock, orig_sha1, &err) || - commit_ref_update(refs, lock, orig_sha1, NULL, &err)) { + if (write_ref_to_lockfile(lock, &orig_oid, &err) || + commit_ref_update(refs, lock, &orig_oid, NULL, &err)) { error("unable to write current sha1 into %s: %s", oldrefname, err.buf); strbuf_release(&err); } @@ -1986,8 +1974,8 @@ static int files_create_reflog(struct ref_store *ref_store, return 0; } -static int log_ref_write_fd(int fd, const unsigned char *old_sha1, - const unsigned char *new_sha1, +static int log_ref_write_fd(int fd, const struct object_id *old_oid, + const struct object_id *new_oid, const char *committer, const char *msg) { int msglen, written; @@ -1998,8 +1986,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1, maxlen = strlen(committer) + msglen + 100; logrec = xmalloc(maxlen); len = xsnprintf(logrec, maxlen, "%s %s %s\n", - sha1_to_hex(old_sha1), - sha1_to_hex(new_sha1), + oid_to_hex(old_oid), + oid_to_hex(new_oid), committer); if (msglen) len += copy_reflog_msg(logrec + len - 1, msg) - 1; @@ -2013,8 +2001,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1, } static int files_log_ref_write(struct files_ref_store *refs, - const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg, + const char *refname, const struct object_id *old_oid, + const struct object_id *new_oid, const char *msg, int flags, struct strbuf *err) { int logfd, result; @@ -2031,7 +2019,7 @@ static int files_log_ref_write(struct files_ref_store *refs, if (logfd < 0) return 0; - result = log_ref_write_fd(logfd, old_sha1, new_sha1, + result = log_ref_write_fd(logfd, old_oid, new_oid, git_committer_info(0), msg); if (result) { struct strbuf sb = STRBUF_INIT; @@ -2063,29 +2051,29 @@ static int files_log_ref_write(struct files_ref_store *refs, * return -1. */ static int write_ref_to_lockfile(struct ref_lock *lock, - const unsigned char *sha1, struct strbuf *err) + const struct object_id *oid, struct strbuf *err) { static char term = '\n'; struct object *o; int fd; - o = parse_object(sha1); + o = parse_object(oid->hash); if (!o) { strbuf_addf(err, "trying to write ref '%s' with nonexistent object %s", - lock->ref_name, sha1_to_hex(sha1)); + lock->ref_name, oid_to_hex(oid)); unlock_ref(lock); return -1; } if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { strbuf_addf(err, "trying to write non-commit object %s to branch '%s'", - sha1_to_hex(sha1), lock->ref_name); + oid_to_hex(oid), lock->ref_name); unlock_ref(lock); return -1; } fd = get_lock_file_fd(lock->lk); - if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 || + if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ || write_in_full(fd, &term, 1) != 1 || close_ref(lock) < 0) { strbuf_addf(err, @@ -2103,14 +2091,14 @@ static int write_ref_to_lockfile(struct ref_lock *lock, */ static int commit_ref_update(struct files_ref_store *refs, struct ref_lock *lock, - const unsigned char *sha1, const char *logmsg, + const struct object_id *oid, const char *logmsg, struct strbuf *err) { files_assert_main_repository(refs, "commit_ref_update"); clear_loose_ref_cache(refs); if (files_log_ref_write(refs, lock->ref_name, - lock->old_oid.hash, sha1, + &lock->old_oid, oid, logmsg, 0, err)) { char *old_msg = strbuf_detach(err, NULL); strbuf_addf(err, "cannot update the ref '%s': %s", @@ -2133,18 +2121,18 @@ static int commit_ref_update(struct files_ref_store *refs, * check with HEAD only which should cover 99% of all usage * scenarios (even 100% of the default ones). */ - unsigned char head_sha1[20]; + struct object_id head_oid; int head_flag; const char *head_ref; head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD", RESOLVE_REF_READING, - head_sha1, &head_flag); + head_oid.hash, &head_flag); if (head_ref && (head_flag & REF_ISSYMREF) && !strcmp(head_ref, lock->ref_name)) { struct strbuf log_err = STRBUF_INIT; if (files_log_ref_write(refs, "HEAD", - lock->old_oid.hash, sha1, + &lock->old_oid, oid, logmsg, 0, &log_err)) { error("%s", log_err.buf); strbuf_release(&log_err); @@ -2182,12 +2170,12 @@ static void update_symref_reflog(struct files_ref_store *refs, const char *target, const char *logmsg) { struct strbuf err = STRBUF_INIT; - unsigned char new_sha1[20]; + struct object_id new_oid; if (logmsg && !refs_read_ref_full(&refs->base, target, - RESOLVE_REF_READING, new_sha1, NULL) && - files_log_ref_write(refs, refname, lock->old_oid.hash, - new_sha1, logmsg, 0, &err)) { + RESOLVE_REF_READING, new_oid.hash, NULL) && + files_log_ref_write(refs, refname, &lock->old_oid, + &new_oid, logmsg, 0, &err)) { error("%s", err.buf); strbuf_release(&err); } @@ -2866,7 +2854,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, * The reference already has the desired * value, so we don't need to write it. */ - } else if (write_ref_to_lockfile(lock, update->new_oid.hash, + } else if (write_ref_to_lockfile(lock, &update->new_oid, err)) { char *write_err = strbuf_detach(err, NULL); @@ -3001,8 +2989,8 @@ static int files_transaction_commit(struct ref_store *ref_store, update->flags & REF_LOG_ONLY) { if (files_log_ref_write(refs, lock->ref_name, - lock->old_oid.hash, - update->new_oid.hash, + &lock->old_oid, + &update->new_oid, update->msg, update->flags, err)) { char *old_msg = strbuf_detach(err, NULL); @@ -3174,7 +3162,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, if ((update->flags & REF_HAVE_NEW) && !is_null_oid(&update->new_oid)) add_packed_ref(refs, update->refname, - update->new_oid.hash); + &update->new_oid); } if (commit_packed_refs(refs)) { diff --git a/refs/ref-cache.c b/refs/ref-cache.c index 6059362f1d..6b11d9cd12 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -32,7 +32,7 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry) } struct ref_entry *create_ref_entry(const char *refname, - const unsigned char *sha1, int flag, + const struct object_id *oid, int flag, int check_name) { struct ref_entry *ref; @@ -41,7 +41,7 @@ struct ref_entry *create_ref_entry(const char *refname, check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) die("Reference has invalid format: '%s'", refname); FLEX_ALLOC_STR(ref, name, refname); - hashcpy(ref->u.value.oid.hash, sha1); + oidcpy(&ref->u.value.oid, oid); oidclr(&ref->u.value.peeled); ref->flag = flag; return ref; diff --git a/refs/ref-cache.h b/refs/ref-cache.h index ffdc54f3f0..1f65e2f9ed 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -185,7 +185,7 @@ struct ref_entry *create_dir_entry(struct ref_cache *cache, int incomplete); struct ref_entry *create_ref_entry(const char *refname, - const unsigned char *sha1, int flag, + const struct object_id *oid, int flag, int check_name); /* From 1aa40df6b15c1114264ba19897579cec9f063f30 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:25 +0000 Subject: [PATCH 164/566] http-push: convert process_ls_object and descendants to object_id Rename one function to reflect that it now uses struct object_id. This conversion is a prerequisite for converting parse_object. Note that while the use of a buffer that is exactly forty bytes long looks questionable, get_oid_hex reads exactly the right number of bytes and does not require the data to be NUL-terminated. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- http-push.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/http-push.c b/http-push.c index 7781f40788..4e7bd9e42b 100644 --- a/http-push.c +++ b/http-push.c @@ -718,13 +718,13 @@ static int fetch_indices(void) return ret; } -static void one_remote_object(const unsigned char *sha1) +static void one_remote_object(const struct object_id *oid) { struct object *obj; - obj = lookup_object(sha1); + obj = lookup_object(oid->hash); if (!obj) - obj = parse_object(sha1); + obj = parse_object(oid->hash); /* Ignore remote objects that don't exist locally */ if (!obj) @@ -1013,26 +1013,26 @@ static void remote_ls(const char *path, int flags, void *userData); /* extract hex from sharded "xx/x{40}" filename */ -static int get_sha1_hex_from_objpath(const char *path, unsigned char *sha1) +static int get_oid_hex_from_objpath(const char *path, struct object_id *oid) { - char hex[40]; + char hex[GIT_MAX_HEXSZ]; - if (strlen(path) != 41) + if (strlen(path) != GIT_SHA1_HEXSZ + 1) return -1; memcpy(hex, path, 2); path += 2; path++; /* skip '/' */ - memcpy(hex, path, 38); + memcpy(hex, path, GIT_SHA1_HEXSZ - 2); - return get_sha1_hex(hex, sha1); + return get_oid_hex(hex, oid); } static void process_ls_object(struct remote_ls_ctx *ls) { unsigned int *parent = (unsigned int *)ls->userData; const char *path = ls->dentry_name; - unsigned char sha1[20]; + struct object_id oid; if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) { remote_dir_exists[*parent] = 1; @@ -1040,10 +1040,10 @@ static void process_ls_object(struct remote_ls_ctx *ls) } if (!skip_prefix(path, "objects/", &path) || - get_sha1_hex_from_objpath(path, sha1)) + get_oid_hex_from_objpath(path, &oid)) return; - one_remote_object(sha1); + one_remote_object(&oid); } static void process_ls_ref(struct remote_ls_ctx *ls) From a58a1b01ff86465f503bf835a41c83e34be376ce Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:26 +0000 Subject: [PATCH 165/566] revision: rename add_pending_sha1 to add_pending_oid Rename this function and convert it to take a pointer to struct object_id. This is a prerequisite for converting get_reference, which is needed to convert parse_object. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/am.c | 2 +- builtin/checkout.c | 4 ++-- revision.c | 8 ++++---- revision.h | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 7663f12e64..642d7047d9 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1618,7 +1618,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa init_revisions(&rev_info, NULL); rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); - add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0); + add_pending_oid(&rev_info, "HEAD", &our_tree, 0); diff_setup_done(&rev_info.diffopt); run_diff_index(&rev_info, 1); } diff --git a/builtin/checkout.c b/builtin/checkout.c index afa99fb8a0..7f1eeea943 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -721,7 +721,7 @@ static int add_pending_uninteresting_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data) { - add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING); + add_pending_oid(cb_data, refname, oid, UNINTERESTING); return 0; } @@ -807,7 +807,7 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new) add_pending_object(&revs, object, oid_to_hex(&object->oid)); for_each_ref(add_pending_uninteresting_ref, &revs); - add_pending_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING); + add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING); refs = revs.pending; revs.leak_pending = 1; diff --git a/revision.c b/revision.c index c2091b6de7..f82c56e1fd 100644 --- a/revision.c +++ b/revision.c @@ -203,10 +203,10 @@ static struct object *get_reference(struct rev_info *revs, const char *name, return object; } -void add_pending_sha1(struct rev_info *revs, const char *name, - const unsigned char *sha1, unsigned int flags) +void add_pending_oid(struct rev_info *revs, const char *name, + const struct object_id *oid, unsigned int flags) { - struct object *object = get_reference(revs, name, sha1, flags); + struct object *object = get_reference(revs, name, oid->hash, flags); add_pending_object(revs, object, name); } @@ -1159,7 +1159,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid, object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags); add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags); - add_pending_sha1(cb->all_revs, path, oid->hash, cb->all_flags); + add_pending_oid(cb->all_revs, path, oid, cb->all_flags); return 0; } diff --git a/revision.h b/revision.h index 14886ec92b..728425a025 100644 --- a/revision.h +++ b/revision.h @@ -263,9 +263,9 @@ extern void show_object_with_name(FILE *, struct object *, const char *); extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name); -extern void add_pending_sha1(struct rev_info *revs, - const char *name, const unsigned char *sha1, - unsigned int flags); +extern void add_pending_oid(struct rev_info *revs, + const char *name, const struct object_id *oid, + unsigned int flags); extern void add_head_to_pending(struct rev_info *); extern void add_reflogs_to_pending(struct rev_info *, unsigned int flags); From 654b9a905c160aec86be6c0b11bd1c539a62c0b9 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:27 +0000 Subject: [PATCH 166/566] revision: convert remaining parse_object callers to object_id Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- revision.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/revision.c b/revision.c index f82c56e1fd..80f74bb7b4 100644 --- a/revision.c +++ b/revision.c @@ -177,23 +177,23 @@ void add_pending_object(struct rev_info *revs, void add_head_to_pending(struct rev_info *revs) { - unsigned char sha1[20]; + struct object_id oid; struct object *obj; - if (get_sha1("HEAD", sha1)) + if (get_oid("HEAD", &oid)) return; - obj = parse_object(sha1); + obj = parse_object(oid.hash); if (!obj) return; add_pending_object(revs, obj, "HEAD"); } static struct object *get_reference(struct rev_info *revs, const char *name, - const unsigned char *sha1, + const struct object_id *oid, unsigned int flags) { struct object *object; - object = parse_object(sha1); + object = parse_object(oid->hash); if (!object) { if (revs->ignore_missing) return object; @@ -206,7 +206,7 @@ static struct object *get_reference(struct rev_info *revs, const char *name, void add_pending_oid(struct rev_info *revs, const char *name, const struct object_id *oid, unsigned int flags) { - struct object *object = get_reference(revs, name, oid->hash, flags); + struct object *object = get_reference(revs, name, oid, flags); add_pending_object(revs, object, name); } @@ -1157,7 +1157,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid, if (ref_excluded(cb->all_revs->ref_excludes, path)) return 0; - object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags); + object = get_reference(cb->all_revs, path, oid, cb->all_flags); add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags); add_pending_oid(cb->all_revs, path, oid, cb->all_flags); return 0; @@ -1292,7 +1292,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags) static int add_parents_only(struct rev_info *revs, const char *arg_, int flags, int exclude_parent) { - unsigned char sha1[20]; + struct object_id oid; struct object *it; struct commit *commit; struct commit_list *parents; @@ -1303,17 +1303,17 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags, flags ^= UNINTERESTING | BOTTOM; arg++; } - if (get_sha1_committish(arg, sha1)) + if (get_sha1_committish(arg, oid.hash)) return 0; while (1) { - it = get_reference(revs, arg, sha1, 0); + it = get_reference(revs, arg, &oid, 0); if (!it && revs->ignore_missing) return 0; if (it->type != OBJ_TAG) break; if (!((struct tag*)it)->tagged) return 0; - hashcpy(sha1, ((struct tag*)it)->tagged->oid.hash); + oidcpy(&oid, &((struct tag*)it)->tagged->oid); } if (it->type != OBJ_COMMIT) return 0; @@ -1434,7 +1434,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi struct object_context oc; char *dotdot; struct object *object; - unsigned char sha1[20]; + struct object_id oid; int local_flags; const char *arg = arg_; int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME; @@ -1444,7 +1444,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi dotdot = strstr(arg, ".."); if (dotdot) { - unsigned char from_sha1[20]; + struct object_id from_oid; const char *next = dotdot + 2; const char *this = arg; int symmetric = *next == '.'; @@ -1470,8 +1470,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi return -1; } } - if (!get_sha1_committish(this, from_sha1) && - !get_sha1_committish(next, sha1)) { + if (!get_sha1_committish(this, from_oid.hash) && + !get_sha1_committish(next, oid.hash)) { struct object *a_obj, *b_obj; if (!cant_be_filename) { @@ -1479,8 +1479,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi verify_non_filename(revs->prefix, arg); } - a_obj = parse_object(from_sha1); - b_obj = parse_object(sha1); + a_obj = parse_object(from_oid.hash); + b_obj = parse_object(oid.hash); if (!a_obj || !b_obj) { missing: if (revs->ignore_missing) @@ -1568,11 +1568,11 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi if (revarg_opt & REVARG_COMMITTISH) get_sha1_flags = GET_SHA1_COMMITTISH; - if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc)) + if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc)) return revs->ignore_missing ? 0 : -1; if (!cant_be_filename) verify_non_filename(revs->prefix, arg); - object = get_reference(revs, arg, sha1, flags ^ local_flags); + object = get_reference(revs, arg, &oid, flags ^ local_flags); add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags); add_pending_object_with_mode(revs, object, arg, oc.mode); return 0; @@ -2287,12 +2287,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->show_merge) prepare_show_merge(revs); if (revs->def && !revs->pending.nr && !got_rev_arg) { - unsigned char sha1[20]; + struct object_id oid; struct object *object; struct object_context oc; - if (get_sha1_with_context(revs->def, 0, sha1, &oc)) + if (get_sha1_with_context(revs->def, 0, oid.hash, &oc)) diagnose_missing_default(revs->def); - object = get_reference(revs, revs->def, sha1, 0); + object = get_reference(revs, revs->def, &oid, 0); add_pending_object_with_mode(revs, object, revs->def, oc.mode); } From cf93982faefd3a9a488ea9a68c60e3a81a4e0432 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:28 +0000 Subject: [PATCH 167/566] upload-pack: convert remaining parse_object callers to object_id Convert the remaining parse_object callers to struct object_id. Use named constants for several hard-coded values. In addition, rename got_sha1 to got_oid to reflect the new argument. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- upload-pack.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index 20f87cd383..5b9d21089a 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -286,19 +286,19 @@ static void create_pack_file(void) die("git upload-pack: %s", abort_msg); } -static int got_sha1(const char *hex, unsigned char *sha1) +static int got_oid(const char *hex, struct object_id *oid) { struct object *o; int we_knew_they_have = 0; - if (get_sha1_hex(hex, sha1)) + if (get_oid_hex(hex, oid)) die("git upload-pack: expected SHA1 object, got '%s'", hex); - if (!has_sha1_file(sha1)) + if (!has_object_file(oid)) return -1; - o = parse_object(sha1); + o = parse_object(oid->hash); if (!o) - die("oops (%s)", sha1_to_hex(sha1)); + die("oops (%s)", oid_to_hex(oid)); if (o->type == OBJ_COMMIT) { struct commit_list *parents; struct commit *commit = (struct commit *)o; @@ -382,8 +382,8 @@ static int ok_to_give_up(void) static int get_common_commits(void) { - unsigned char sha1[20]; - char last_hex[41]; + struct object_id oid; + char last_hex[GIT_MAX_HEXSZ + 1]; int got_common = 0; int got_other = 0; int sent_ready = 0; @@ -416,11 +416,11 @@ static int get_common_commits(void) continue; } if (skip_prefix(line, "have ", &arg)) { - switch (got_sha1(arg, sha1)) { + switch (got_oid(arg, &oid)) { case -1: /* they have what we do not */ got_other = 1; if (multi_ack && ok_to_give_up()) { - const char *hex = sha1_to_hex(sha1); + const char *hex = oid_to_hex(&oid); if (multi_ack == 2) { sent_ready = 1; packet_write_fmt(1, "ACK %s ready\n", hex); @@ -430,7 +430,7 @@ static int get_common_commits(void) break; default: got_common = 1; - memcpy(last_hex, sha1_to_hex(sha1), 41); + memcpy(last_hex, oid_to_hex(&oid), 41); if (multi_ack == 2) packet_write_fmt(1, "ACK %s common\n", last_hex); else if (multi_ack) @@ -492,7 +492,7 @@ static int do_reachable_revlist(struct child_process *cmd, goto error; namebuf[0] = '^'; - namebuf[41] = '\n'; + namebuf[GIT_SHA1_HEXSZ + 1] = '\n'; for (i = get_max_object_index(); 0 < i; ) { o = get_indexed_object(--i); if (!o) @@ -502,10 +502,10 @@ static int do_reachable_revlist(struct child_process *cmd, if (!is_our_ref(o)) continue; memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); - if (write_in_full(cmd->in, namebuf, 42) < 0) + if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 2) < 0) goto error; } - namebuf[40] = '\n'; + namebuf[GIT_SHA1_HEXSZ] = '\n'; for (i = 0; i < src->nr; i++) { o = src->objects[i].item; if (is_our_ref(o)) { @@ -516,7 +516,7 @@ static int do_reachable_revlist(struct child_process *cmd, if (reachable && o->type == OBJ_COMMIT) o->flags |= TMP_MARK; memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); - if (write_in_full(cmd->in, namebuf, 41) < 0) + if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 1) < 0) goto error; } close(cmd->in); @@ -742,7 +742,7 @@ static void receive_needs(void) for (;;) { struct object *o; const char *features; - unsigned char sha1_buf[20]; + struct object_id oid_buf; char *line = packet_read_line(0, NULL); const char *arg; @@ -751,15 +751,15 @@ static void receive_needs(void) break; if (skip_prefix(line, "shallow ", &arg)) { - unsigned char sha1[20]; + struct object_id oid; struct object *object; - if (get_sha1_hex(arg, sha1)) + if (get_oid_hex(arg, &oid)) die("invalid shallow line: %s", line); - object = parse_object(sha1); + object = parse_object(oid.hash); if (!object) continue; if (object->type != OBJ_COMMIT) - die("invalid shallow object %s", sha1_to_hex(sha1)); + die("invalid shallow object %s", oid_to_hex(&oid)); if (!(object->flags & CLIENT_SHALLOW)) { object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); @@ -785,8 +785,8 @@ static void receive_needs(void) } if (skip_prefix(line, "deepen-not ", &arg)) { char *ref = NULL; - unsigned char sha1[20]; - if (expand_ref(arg, strlen(arg), sha1, &ref) != 1) + struct object_id oid; + if (expand_ref(arg, strlen(arg), oid.hash, &ref) != 1) die("git upload-pack: ambiguous deepen-not: %s", line); string_list_append(&deepen_not, ref); free(ref); @@ -794,7 +794,7 @@ static void receive_needs(void) continue; } if (!skip_prefix(line, "want ", &arg) || - get_sha1_hex(arg, sha1_buf)) + get_oid_hex(arg, &oid_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); @@ -821,13 +821,13 @@ static void receive_needs(void) if (parse_feature_request(features, "include-tag")) use_include_tag = 1; - o = parse_object(sha1_buf); + o = parse_object(oid_buf.hash); if (!o) { packet_write_fmt(1, "ERR upload-pack: not our ref %s", - sha1_to_hex(sha1_buf)); + oid_to_hex(&oid_buf)); die("git upload-pack: not our ref %s", - sha1_to_hex(sha1_buf)); + oid_to_hex(&oid_buf)); } if (!(o->flags & WANTED)) { o->flags |= WANTED; From de37d50d76f4cfac2876af8a8ceff0f65b5f27b0 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:29 +0000 Subject: [PATCH 168/566] sha1_name: convert internals of peel_onion to object_id Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- sha1_name.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sha1_name.c b/sha1_name.c index b7e09ac139..72e72ab9ae 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -798,7 +798,7 @@ struct object *peel_to_type(const char *name, int namelen, static int peel_onion(const char *name, int len, unsigned char *sha1, unsigned lookup_flags) { - unsigned char outer[20]; + struct object_id outer; const char *sp; unsigned int expected_type = 0; struct object *o; @@ -846,10 +846,10 @@ static int peel_onion(const char *name, int len, unsigned char *sha1, else if (expected_type == OBJ_TREE) lookup_flags |= GET_SHA1_TREEISH; - if (get_sha1_1(name, sp - name - 2, outer, lookup_flags)) + if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags)) return -1; - o = parse_object(outer); + o = parse_object(outer.hash); if (!o) return -1; if (!expected_type) { From 4939e2c435529095059faa72a4004a4849fb8682 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:30 +0000 Subject: [PATCH 169/566] builtin/read-tree: convert to struct object_id This is a caller of parse_tree_indirect, which must be converted in order to convert parse_object. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/read-tree.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 23e212ee8c..92eff23e45 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -23,13 +23,13 @@ static int read_empty; static struct tree *trees[MAX_UNPACK_TREES]; static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int list_tree(unsigned char *sha1) +static int list_tree(struct object_id *oid) { struct tree *tree; if (nr_trees >= MAX_UNPACK_TREES) die("I cannot read more than %d trees", MAX_UNPACK_TREES); - tree = parse_tree_indirect(sha1); + tree = parse_tree_indirect(oid->hash); if (!tree) return -1; trees[nr_trees++] = tree; @@ -121,7 +121,7 @@ static struct lock_file lock_file; int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, stage = 0; - unsigned char sha1[20]; + struct object_id oid; struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; int prefix_set = 0; @@ -204,9 +204,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) for (i = 0; i < argc; i++) { const char *arg = argv[i]; - if (get_sha1(arg, sha1)) + if (get_oid(arg, &oid)) die("Not a valid object name %s", arg); - if (list_tree(sha1) < 0) + if (list_tree(&oid) < 0) die("failed to unpack tree object %s", arg); stage++; } From 6f37eb7d858ef7fff79ee083c98ee99ab9bc05a4 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:31 +0000 Subject: [PATCH 170/566] builtin/ls-files: convert overlay_tree_on_cache to object_id This is another caller of parse_tree_indirect. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a6c70dbe9e..da0ff849f0 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -414,14 +414,14 @@ static void prune_cache(const char *prefix, size_t prefixlen) void overlay_tree_on_cache(const char *tree_name, const char *prefix) { struct tree *tree; - unsigned char sha1[20]; + struct object_id oid; struct pathspec pathspec; struct cache_entry *last_stage0 = NULL; int i; - if (get_sha1(tree_name, sha1)) + if (get_oid(tree_name, &oid)) die("tree-ish %s not found.", tree_name); - tree = parse_tree_indirect(sha1); + tree = parse_tree_indirect(oid.hash); if (!tree) die("bad tree-ish %s", tree_name); From ace976b26ced2f35415119984f9d58d26b478afd Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:32 +0000 Subject: [PATCH 171/566] sequencer: convert fast_forward_to to struct object_id fast_forward_to is required for checkout_fast_fowrard, which is required for parse_tree_indirect. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- sequencer.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sequencer.c b/sequencer.c index 218895fde1..9ca352ac78 100644 --- a/sequencer.c +++ b/sequencer.c @@ -374,7 +374,7 @@ static void update_abort_safety_file(void) write_file(git_path_abort_safety_file(), "%s", ""); } -static int fast_forward_to(const unsigned char *to, const unsigned char *from, +static int fast_forward_to(const struct object_id *to, const struct object_id *from, int unborn, struct replay_opts *opts) { struct ref_transaction *transaction; @@ -382,7 +382,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from, struct strbuf err = STRBUF_INIT; read_cache(); - if (checkout_fast_forward(from, to, 1)) + if (checkout_fast_forward(from->hash, to->hash, 1)) return -1; /* the callee should have complained already */ strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts))); @@ -390,7 +390,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from, transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, "HEAD", - to, unborn ? null_sha1 : from, + to->hash, unborn ? null_sha1 : from->hash, 0, sb.buf, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@ -935,7 +935,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, { unsigned int flags = opts->edit ? EDIT_MSG : 0; const char *msg_file = opts->edit ? NULL : git_path_merge_msg(); - unsigned char head[20]; + struct object_id head; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL }; @@ -949,12 +949,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, * that represents the "current" state for merge-recursive * to work on. */ - if (write_cache_as_tree(head, 0, NULL)) + if (write_cache_as_tree(head.hash, 0, NULL)) return error(_("your index file is unmerged.")); } else { - unborn = get_sha1("HEAD", head); + unborn = get_oid("HEAD", &head); if (unborn) - hashcpy(head, EMPTY_TREE_SHA1_BIN); + oidcpy(&head, &empty_tree_oid); if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0)) return error_dirty_index(opts); } @@ -990,11 +990,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, oid_to_hex(&commit->object.oid)); if (opts->allow_ff && !is_fixup(command) && - ((parent && !hashcmp(parent->object.oid.hash, head)) || + ((parent && !oidcmp(&parent->object.oid, &head)) || (!parent && unborn))) { if (is_rebase_i(opts)) write_author_script(msg.message); - res = fast_forward_to(commit->object.oid.hash, head, unborn, + res = fast_forward_to(&commit->object.oid, &head, unborn, opts); if (res || command != TODO_REWORD) goto leave; @@ -1081,7 +1081,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, res = -1; else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) { res = do_recursive_merge(base, next, base_label, next_label, - head, &msgbuf, opts); + head.hash, &msgbuf, opts); if (res < 0) return res; res |= write_message(msgbuf.buf, msgbuf.len, @@ -1097,7 +1097,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, commit_list_insert(next, &remotes); res |= try_merge_command(opts->strategy, opts->xopts_nr, (const char **)opts->xopts, - common, sha1_to_hex(head), remotes); + common, oid_to_hex(&head), remotes); free_commit_list(common); free_commit_list(remotes); } From f06e90dac1f63f46d299ca728464fb0a450f6972 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:33 +0000 Subject: [PATCH 172/566] merge: convert checkout_fast_forward to struct object_id Converting checkout_fast_forward is required to convert parse_tree_indirect. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/merge.c | 4 ++-- builtin/pull.c | 4 ++-- cache.h | 4 ++-- merge.c | 8 ++++---- sequencer.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/builtin/merge.c b/builtin/merge.c index f11b5f3de4..5ea7f7da9a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -1372,8 +1372,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } - if (checkout_fast_forward(head_commit->object.oid.hash, - commit->object.oid.hash, + if (checkout_fast_forward(&head_commit->object.oid, + &commit->object.oid, overwrite_ignore)) { ret = 1; goto done; diff --git a/builtin/pull.c b/builtin/pull.c index 2ffb6569a9..318c273eb3 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -523,7 +523,7 @@ static int pull_into_void(const struct object_id *merge_head, * index/worktree changes that the user already made on the unborn * branch. */ - if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head->hash, 0)) + if (checkout_fast_forward(&empty_tree_oid, merge_head, 0)) return 1; if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR)) @@ -839,7 +839,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) "fast-forwarding your working tree from\n" "commit %s."), oid_to_hex(&orig_head)); - if (checkout_fast_forward(orig_head.hash, curr_head.hash, 0)) + if (checkout_fast_forward(&orig_head, &curr_head, 0)) die(_("Cannot fast-forward your working tree.\n" "After making sure that you saved anything precious from\n" "$ git diff %s\n" diff --git a/cache.h b/cache.h index e1f0e182ad..8862510f94 100644 --- a/cache.h +++ b/cache.h @@ -2198,8 +2198,8 @@ struct commit_list; int try_merge_command(const char *strategy, size_t xopts_nr, const char **xopts, struct commit_list *common, const char *head_arg, struct commit_list *remotes); -int checkout_fast_forward(const unsigned char *from, - const unsigned char *to, +int checkout_fast_forward(const struct object_id *from, + const struct object_id *to, int overwrite_ignore); diff --git a/merge.c b/merge.c index 04ee5fc911..b0cffe16fe 100644 --- a/merge.c +++ b/merge.c @@ -44,8 +44,8 @@ int try_merge_command(const char *strategy, size_t xopts_nr, return ret; } -int checkout_fast_forward(const unsigned char *head, - const unsigned char *remote, +int checkout_fast_forward(const struct object_id *head, + const struct object_id *remote, int overwrite_ignore) { struct tree *trees[MAX_UNPACK_TREES]; @@ -79,10 +79,10 @@ int checkout_fast_forward(const unsigned char *head, opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); - trees[nr_trees] = parse_tree_indirect(head); + trees[nr_trees] = parse_tree_indirect(head->hash); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(remote); + trees[nr_trees] = parse_tree_indirect(remote->hash); if (!trees[nr_trees++]) return -1; for (i = 0; i < nr_trees; i++) { diff --git a/sequencer.c b/sequencer.c index 9ca352ac78..dcc56a2b69 100644 --- a/sequencer.c +++ b/sequencer.c @@ -382,7 +382,7 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f struct strbuf err = STRBUF_INIT; read_cache(); - if (checkout_fast_forward(from->hash, to->hash, 1)) + if (checkout_fast_forward(from, to, 1)) return -1; /* the callee should have complained already */ strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts))); From a9b5f5bfd50bfd0a96e464ccf926092ffe03163a Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:34 +0000 Subject: [PATCH 173/566] builtin/ls-tree: convert to struct object_id This is a prerequisite to convert do_diff_cache, which is required to convert parse_tree_indirect. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/ls-tree.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index d7ebeb4ce6..5baac3ef2f 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -119,7 +119,7 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base, int cmd_ls_tree(int argc, const char **argv, const char *prefix) { - unsigned char sha1[20]; + struct object_id oid; struct tree *tree; int i, full_tree = 0; const struct option ls_tree_options[] = { @@ -164,7 +164,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); - if (get_sha1(argv[0], sha1)) + if (get_oid(argv[0], &oid)) die("Not a valid object name %s", argv[0]); /* @@ -180,7 +180,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) for (i = 0; i < pathspec.nr; i++) pathspec.items[i].nowildcard_len = pathspec.items[i].len; pathspec.has_wildcard = 0; - tree = parse_tree_indirect(sha1); + tree = parse_tree_indirect(oid.hash); if (!tree) die("not a tree object"); return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL); From 944cffbd18a7dbe0b4c5849da6f093c41b49ea00 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:35 +0000 Subject: [PATCH 174/566] diff-lib: convert do_diff_cache to struct object_id This is needed to convert parse_tree_indirect. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/am.c | 2 +- builtin/blame.c | 6 +++--- builtin/reset.c | 2 +- diff-lib.c | 12 ++++++------ diff.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 642d7047d9..200d9dbed4 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1145,7 +1145,7 @@ static int index_has_changes(struct strbuf *sb) DIFF_OPT_SET(&opt, EXIT_WITH_STATUS); if (!sb) DIFF_OPT_SET(&opt, QUICK); - do_diff_cache(head.hash, &opt); + do_diff_cache(&head, &opt); diffcore_std(&opt); for (i = 0; sb && i < diff_queued_diff.nr; i++) { if (i) diff --git a/builtin/blame.c b/builtin/blame.c index 58bb274d02..e920314a74 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -563,7 +563,7 @@ static struct origin *find_origin(struct scoreboard *sb, diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); + do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_sha1(parent->tree->object.oid.hash, origin->commit->tree->object.oid.hash, @@ -633,7 +633,7 @@ static struct origin *find_rename(struct scoreboard *sb, diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); + do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_sha1(parent->tree->object.oid.hash, origin->commit->tree->object.oid.hash, @@ -1272,7 +1272,7 @@ static void find_copy_in_parent(struct scoreboard *sb, DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); if (is_null_oid(&target->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); + do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_sha1(parent->tree->object.oid.hash, target->commit->tree->object.oid.hash, diff --git a/builtin/reset.c b/builtin/reset.c index 0be52fa36a..3415dac5d5 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -154,7 +154,7 @@ static int read_from_tree(const struct pathspec *pathspec, opt.format_callback = update_index_from_diff; opt.format_callback_data = &intent_to_add; - if (do_diff_cache(tree_oid->hash, &opt)) + if (do_diff_cache(tree_oid, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); diff --git a/diff-lib.c b/diff-lib.c index 52447466b5..ee9df0f847 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -478,7 +478,7 @@ static int oneway_diff(const struct cache_entry * const *src, } static int diff_cache(struct rev_info *revs, - const unsigned char *tree_sha1, + const struct object_id *tree_oid, const char *tree_name, int cached) { @@ -486,10 +486,10 @@ static int diff_cache(struct rev_info *revs, struct tree_desc t; struct unpack_trees_options opts; - tree = parse_tree_indirect(tree_sha1); + tree = parse_tree_indirect(tree_oid->hash); if (!tree) return error("bad tree object %s", - tree_name ? tree_name : sha1_to_hex(tree_sha1)); + tree_name ? tree_name : oid_to_hex(tree_oid)); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.index_only = cached; @@ -512,7 +512,7 @@ int run_diff_index(struct rev_info *revs, int cached) struct object_array_entry *ent; ent = revs->pending.objects; - if (diff_cache(revs, ent->item->oid.hash, ent->name, cached)) + if (diff_cache(revs, &ent->item->oid, ent->name, cached)) exit(128); diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/"); @@ -522,7 +522,7 @@ int run_diff_index(struct rev_info *revs, int cached) return 0; } -int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) +int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt) { struct rev_info revs; @@ -530,7 +530,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) copy_pathspec(&revs.prune_data, &opt->pathspec); revs.diffopt = *opt; - if (diff_cache(&revs, tree_sha1, NULL, 1)) + if (diff_cache(&revs, tree_oid, NULL, 1)) exit(128); return 0; } diff --git a/diff.h b/diff.h index 5be1ee77a7..d75e6d15e2 100644 --- a/diff.h +++ b/diff.h @@ -354,7 +354,7 @@ extern const char *diff_aligned_abbrev(const struct object_id *sha1, int); extern int run_diff_files(struct rev_info *revs, unsigned int option); extern int run_diff_index(struct rev_info *revs, int cached); -extern int do_diff_cache(const unsigned char *, struct diff_options *); +extern int do_diff_cache(const struct object_id *, struct diff_options *); extern int diff_flush_patch_id(struct diff_options *, unsigned char *, int); extern int diff_result_code(struct diff_options *, int); From 48be4c625bf0f35309fdf501f47eb7445a9f5494 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:36 +0000 Subject: [PATCH 175/566] sequencer: convert do_recursive_merge to struct object_id This conversion is required to convert parse_tree_indirect. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- sequencer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sequencer.c b/sequencer.c index dcc56a2b69..adcc0a953f 100644 --- a/sequencer.c +++ b/sequencer.c @@ -426,7 +426,7 @@ void append_conflicts_hint(struct strbuf *msgbuf) static int do_recursive_merge(struct commit *base, struct commit *next, const char *base_label, const char *next_label, - unsigned char *head, struct strbuf *msgbuf, + struct object_id *head, struct strbuf *msgbuf, struct replay_opts *opts) { struct merge_options o; @@ -446,7 +446,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next, if (is_rebase_i(opts)) o.buffer_output = 2; - head_tree = parse_tree_indirect(head); + head_tree = parse_tree_indirect(head->hash); next_tree = next ? next->tree : empty_tree(); base_tree = base ? base->tree : empty_tree(); @@ -1081,7 +1081,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, res = -1; else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) { res = do_recursive_merge(base, next, base_label, next_label, - head.hash, &msgbuf, opts); + &head, &msgbuf, opts); if (res < 0) return res; res |= write_message(msgbuf.buf, msgbuf.len, From a9dbc179100b7119cb44eb5b4adcb47967f346a6 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:37 +0000 Subject: [PATCH 176/566] tree: convert parse_tree_indirect to struct object_id Convert parse_tree_indirect to take a pointer to struct object_id. Update all the callers. This transformation was achieved using the following semantic patch and manual updates to the declaration and definition. Update builtin/checkout.c manually as well, since it uses a ternary expression not handled by the semantic patch. @@ expression E1; @@ - parse_tree_indirect(E1.hash) + parse_tree_indirect(&E1) @@ expression E1; @@ - parse_tree_indirect(E1->hash) + parse_tree_indirect(E1) Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- archive.c | 4 ++-- builtin/am.c | 6 +++--- builtin/checkout.c | 8 ++++---- builtin/clone.c | 2 +- builtin/commit.c | 2 +- builtin/ls-files.c | 2 +- builtin/ls-tree.c | 2 +- builtin/merge.c | 6 +++--- builtin/read-tree.c | 2 +- builtin/reset.c | 4 ++-- diff-lib.c | 2 +- merge.c | 4 ++-- sequencer.c | 2 +- t/helper/test-match-trees.c | 4 ++-- tree.c | 4 ++-- tree.h | 2 +- 16 files changed, 28 insertions(+), 28 deletions(-) diff --git a/archive.c b/archive.c index 54701e8bb2..b15a922dab 100644 --- a/archive.c +++ b/archive.c @@ -369,7 +369,7 @@ static void parse_treeish_arg(const char **argv, archive_time = time(NULL); } - tree = parse_tree_indirect(oid.hash); + tree = parse_tree_indirect(&oid); if (tree == NULL) die("not a tree object"); @@ -383,7 +383,7 @@ static void parse_treeish_arg(const char **argv, if (err || !S_ISDIR(mode)) die("current working directory is untracked"); - tree = parse_tree_indirect(tree_oid.hash); + tree = parse_tree_indirect(&tree_oid); } ar_args->tree = tree; ar_args->commit_sha1 = commit_sha1; diff --git a/builtin/am.c b/builtin/am.c index 200d9dbed4..a2867f347b 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2045,11 +2045,11 @@ static int clean_index(const struct object_id *head, const struct object_id *rem struct tree *head_tree, *remote_tree, *index_tree; struct object_id index; - head_tree = parse_tree_indirect(head->hash); + head_tree = parse_tree_indirect(head); if (!head_tree) return error(_("Could not parse object '%s'."), oid_to_hex(head)); - remote_tree = parse_tree_indirect(remote->hash); + remote_tree = parse_tree_indirect(remote); if (!remote_tree) return error(_("Could not parse object '%s'."), oid_to_hex(remote)); @@ -2061,7 +2061,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem if (write_cache_as_tree(index.hash, 0, NULL)) return -1; - index_tree = parse_tree_indirect(index.hash); + index_tree = parse_tree_indirect(&index); if (!index_tree) return error(_("Could not parse object '%s'."), oid_to_hex(&index)); diff --git a/builtin/checkout.c b/builtin/checkout.c index 7f1eeea943..13365fb621 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -527,10 +527,10 @@ static int merge_working_tree(const struct checkout_opts *opts, setup_standard_excludes(topts.dir); } tree = parse_tree_indirect(old->commit ? - old->commit->object.oid.hash : - EMPTY_TREE_SHA1_BIN); + &old->commit->object.oid : + &empty_tree_oid); init_tree_desc(&trees[0], tree->buffer, tree->size); - tree = parse_tree_indirect(new->commit->object.oid.hash); + tree = parse_tree_indirect(&new->commit->object.oid); init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); @@ -1050,7 +1050,7 @@ static int parse_branchname_arg(int argc, const char **argv, new->commit = lookup_commit_reference_gently(rev, 1); if (!new->commit) { /* not a commit */ - *source_tree = parse_tree_indirect(rev->hash); + *source_tree = parse_tree_indirect(rev); } else { parse_commit_or_die(new->commit); *source_tree = new->commit->tree; diff --git a/builtin/clone.c b/builtin/clone.c index 646f287925..da2d3c1aef 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -739,7 +739,7 @@ static int checkout(int submodule_progress) opts.src_index = &the_index; opts.dst_index = &the_index; - tree = parse_tree_indirect(oid.hash); + tree = parse_tree_indirect(&oid); parse_tree(tree); init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) diff --git a/builtin/commit.c b/builtin/commit.c index e69f466d5b..6adc908b32 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -313,7 +313,7 @@ static void create_base_index(const struct commit *current_head) opts.dst_index = &the_index; opts.fn = oneway_merge; - tree = parse_tree_indirect(current_head->object.oid.hash); + tree = parse_tree_indirect(¤t_head->object.oid); if (!tree) die(_("failed to unpack HEAD tree object")); parse_tree(tree); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index da0ff849f0..f20edabe6d 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -421,7 +421,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) if (get_oid(tree_name, &oid)) die("tree-ish %s not found.", tree_name); - tree = parse_tree_indirect(oid.hash); + tree = parse_tree_indirect(&oid); if (!tree) die("bad tree-ish %s", tree_name); diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 5baac3ef2f..ee7b293b11 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -180,7 +180,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) for (i = 0; i < pathspec.nr; i++) pathspec.items[i].nowildcard_len = pathspec.items[i].len; pathspec.has_wildcard = 0; - tree = parse_tree_indirect(oid.hash); + tree = parse_tree_indirect(&oid); if (!tree) die("not a tree object"); return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL); diff --git a/builtin/merge.c b/builtin/merge.c index 5ea7f7da9a..a4a098f40f 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -605,13 +605,13 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head, opts.verbose_update = 1; opts.trivial_merges_only = 1; opts.merge = 1; - trees[nr_trees] = parse_tree_indirect(common->hash); + trees[nr_trees] = parse_tree_indirect(common); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(head->hash); + trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(one->hash); + trees[nr_trees] = parse_tree_indirect(one); if (!trees[nr_trees++]) return -1; opts.fn = threeway_merge; diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 92eff23e45..6d45175f6a 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -29,7 +29,7 @@ static int list_tree(struct object_id *oid) if (nr_trees >= MAX_UNPACK_TREES) die("I cannot read more than %d trees", MAX_UNPACK_TREES); - tree = parse_tree_indirect(oid->hash); + tree = parse_tree_indirect(oid); if (!tree) return -1; trees[nr_trees++] = tree; diff --git a/builtin/reset.c b/builtin/reset.c index 3415dac5d5..c782739c21 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -84,7 +84,7 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet) return -1; if (reset_type == MIXED || reset_type == HARD) { - tree = parse_tree_indirect(oid->hash); + tree = parse_tree_indirect(oid); prime_cache_tree(&the_index, tree); } @@ -311,7 +311,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) struct tree *tree; if (get_sha1_treeish(rev, oid.hash)) die(_("Failed to resolve '%s' as a valid tree."), rev); - tree = parse_tree_indirect(oid.hash); + tree = parse_tree_indirect(&oid); if (!tree) die(_("Could not parse object '%s'."), rev); oidcpy(&oid, &tree->object.oid); diff --git a/diff-lib.c b/diff-lib.c index ee9df0f847..2982bf055a 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -486,7 +486,7 @@ static int diff_cache(struct rev_info *revs, struct tree_desc t; struct unpack_trees_options opts; - tree = parse_tree_indirect(tree_oid->hash); + tree = parse_tree_indirect(tree_oid); if (!tree) return error("bad tree object %s", tree_name ? tree_name : oid_to_hex(tree_oid)); diff --git a/merge.c b/merge.c index b0cffe16fe..1d441ad942 100644 --- a/merge.c +++ b/merge.c @@ -79,10 +79,10 @@ int checkout_fast_forward(const struct object_id *head, opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); - trees[nr_trees] = parse_tree_indirect(head->hash); + trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(remote->hash); + trees[nr_trees] = parse_tree_indirect(remote); if (!trees[nr_trees++]) return -1; for (i = 0; i < nr_trees; i++) { diff --git a/sequencer.c b/sequencer.c index adcc0a953f..5817d8a23d 100644 --- a/sequencer.c +++ b/sequencer.c @@ -446,7 +446,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next, if (is_rebase_i(opts)) o.buffer_output = 2; - head_tree = parse_tree_indirect(head->hash); + head_tree = parse_tree_indirect(head); next_tree = next ? next->tree : empty_tree(); base_tree = base ? base->tree : empty_tree(); diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c index e939502863..356d8edef1 100644 --- a/t/helper/test-match-trees.c +++ b/t/helper/test-match-trees.c @@ -12,10 +12,10 @@ int cmd_main(int ac, const char **av) die("cannot parse %s as an object name", av[1]); if (get_oid(av[2], &hash2)) die("cannot parse %s as an object name", av[2]); - one = parse_tree_indirect(hash1.hash); + one = parse_tree_indirect(&hash1); if (!one) die("not a tree-ish %s", av[1]); - two = parse_tree_indirect(hash2.hash); + two = parse_tree_indirect(&hash2); if (!two) die("not a tree-ish %s", av[2]); diff --git a/tree.c b/tree.c index 28ce930b95..9adcd8bd42 100644 --- a/tree.c +++ b/tree.c @@ -232,9 +232,9 @@ void free_tree_buffer(struct tree *tree) tree->object.parsed = 0; } -struct tree *parse_tree_indirect(const unsigned char *sha1) +struct tree *parse_tree_indirect(const struct object_id *oid) { - struct object *obj = parse_object(sha1); + struct object *obj = parse_object(oid->hash); do { if (!obj) return NULL; diff --git a/tree.h b/tree.h index 2b2c8dbbe9..0d4734b94b 100644 --- a/tree.h +++ b/tree.h @@ -24,7 +24,7 @@ static inline int parse_tree(struct tree *tree) void free_tree_buffer(struct tree *tree); /* Parses and returns the tree in the given ent, chasing tags and commits. */ -struct tree *parse_tree_indirect(const unsigned char *sha1); +struct tree *parse_tree_indirect(const struct object_id *oid); #define READ_TREE_RECURSIVE 1 typedef int (*read_tree_fn_t)(const unsigned char *, struct strbuf *, const char *, unsigned int, int, void *); From c251c83df276dc0bff4d008433268ad59b7a8df2 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sat, 6 May 2017 22:10:38 +0000 Subject: [PATCH 177/566] object: convert parse_object* to take struct object_id Make parse_object, parse_object_or_die, and parse_object_buffer take a pointer to struct object_id. Remove the temporary variables inserted earlier, since they are no longer necessary. Transform all of the callers using the following semantic patch: @@ expression E1; @@ - parse_object(E1.hash) + parse_object(&E1) @@ expression E1; @@ - parse_object(E1->hash) + parse_object(E1) @@ expression E1, E2; @@ - parse_object_or_die(E1.hash, E2) + parse_object_or_die(&E1, E2) @@ expression E1, E2; @@ - parse_object_or_die(E1->hash, E2) + parse_object_or_die(E1, E2) @@ expression E1, E2, E3, E4, E5; @@ - parse_object_buffer(E1.hash, E2, E3, E4, E5) + parse_object_buffer(&E1, E2, E3, E4, E5) @@ expression E1, E2, E3, E4, E5; @@ - parse_object_buffer(E1->hash, E2, E3, E4, E5) + parse_object_buffer(E1, E2, E3, E4, E5) Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- builtin/diff-tree.c | 2 +- builtin/diff.c | 2 +- builtin/fast-export.c | 4 ++-- builtin/fmt-merge-msg.c | 4 ++-- builtin/fsck.c | 8 ++++---- builtin/grep.c | 2 +- builtin/index-pack.c | 3 ++- builtin/log.c | 2 +- builtin/name-rev.c | 6 +++--- builtin/prune.c | 3 ++- builtin/receive-pack.c | 6 +++--- builtin/reflog.c | 2 +- builtin/rev-list.c | 2 +- builtin/unpack-objects.c | 3 ++- bundle.c | 10 +++++---- commit.c | 4 ++-- fetch-pack.c | 14 ++++++------- fsck.c | 2 +- http-backend.c | 2 +- http-push.c | 4 ++-- log-tree.c | 6 +++--- merge-recursive.c | 2 +- object.c | 44 +++++++++++++++++----------------------- object.h | 8 ++++---- pack-bitmap.c | 4 ++-- pretty.c | 2 +- reachable.c | 4 ++-- ref-filter.c | 4 ++-- reflog-walk.c | 4 ++-- refs/files-backend.c | 2 +- remote.c | 4 ++-- revision.c | 12 +++++------ server-info.c | 2 +- sha1_name.c | 14 ++++++------- tag.c | 4 ++-- tree.c | 4 ++-- upload-pack.c | 8 ++++---- walker.c | 2 +- 38 files changed, 107 insertions(+), 108 deletions(-) diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 95b8d1ba7c..5ea1c14317 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -67,7 +67,7 @@ static int diff_tree_stdin(char *line) line[len-1] = 0; if (parse_oid_hex(line, &oid, &p)) return -1; - obj = parse_object(oid.hash); + obj = parse_object(&oid); if (!obj) return -1; if (obj->type == OBJ_COMMIT) diff --git a/builtin/diff.c b/builtin/diff.c index 895f928978..8c03ddaf58 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -395,7 +395,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) const char *name = entry->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) - obj = parse_object(obj->oid.hash); + obj = parse_object(&obj->oid); obj = deref_tag(obj, NULL, 0); if (!obj) die(_("invalid object '%s' given."), name); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index ae36b14db7..969550531b 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -240,7 +240,7 @@ static void export_blob(const struct object_id *oid) die ("Could not read blob %s", oid_to_hex(oid)); if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0) die("sha1 mismatch in blob %s", oid_to_hex(oid)); - object = parse_object_buffer(oid->hash, type, size, buf, &eaten); + object = parse_object_buffer(oid, type, size, buf, &eaten); } if (!object) @@ -777,7 +777,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name) /* handle nested tags */ while (tag && tag->object.type == OBJ_TAG) { - parse_object(tag->object.oid.hash); + parse_object(&tag->object.oid); string_list_append(&extra_refs, full_name)->util = tag; tag = (struct tag *)tag->tagged; } diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 91dd753dd9..70137b0b7e 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -341,7 +341,7 @@ static void shortlog(const char *name, const struct object_id *oid = &origin_data->oid; int limit = opts->shortlog_len; - branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ); + branch = deref_tag(parse_object(oid), oid_to_hex(oid), GIT_SHA1_HEXSZ); if (!branch || branch->type != OBJ_COMMIT) return; @@ -559,7 +559,7 @@ static void find_merge_parents(struct merge_parents *result, * "name" here and we do not want to contaminate its * util field yet. */ - obj = parse_object(oid.hash); + obj = parse_object(&oid); parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT); if (!parent) continue; diff --git a/builtin/fsck.c b/builtin/fsck.c index a187054dac..8b8146174a 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -385,7 +385,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, * verify_packfile(), data_valid variable for details. */ struct object *obj; - obj = parse_object_buffer(oid->hash, type, size, buffer, eaten); + obj = parse_object_buffer(oid, type, size, buffer, eaten); if (!obj) { errors_found |= ERROR_OBJECT; return error("%s: object corrupt or missing", oid_to_hex(oid)); @@ -444,7 +444,7 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid, { struct object *obj; - obj = parse_object(oid->hash); + obj = parse_object(oid); if (!obj) { error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; @@ -506,7 +506,7 @@ static struct object *parse_loose_object(const struct object_id *oid, if (!contents && type != OBJ_BLOB) die("BUG: read_loose_object streamed a non-blob"); - obj = parse_object_buffer(oid->hash, type, size, contents, &eaten); + obj = parse_object_buffer(oid, type, size, contents, &eaten); if (!eaten) free(contents); @@ -599,7 +599,7 @@ static int fsck_cache_tree(struct cache_tree *it) fprintf(stderr, "Checking cache tree\n"); if (0 <= it->entry_count) { - struct object *obj = parse_object(it->oid.hash); + struct object *obj = parse_object(&it->oid); if (!obj) { error("%s: invalid sha1 pointer in cache-tree", oid_to_hex(&it->oid)); diff --git a/builtin/grep.c b/builtin/grep.c index 3ffb5b4e81..e64e14e945 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1196,7 +1196,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) break; } - object = parse_object_or_die(oid.hash, arg); + object = parse_object_or_die(&oid, arg); if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index b75133f625..04b9dcaf0f 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -845,7 +845,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, * we do not need to free the memory here, as the * buf is deleted by the caller. */ - obj = parse_object_buffer(oid->hash, type, size, buf, &eaten); + obj = parse_object_buffer(oid, type, size, buf, + &eaten); if (!obj) die(_("invalid %s"), typename(type)); if (do_fsck_object && diff --git a/builtin/log.c b/builtin/log.c index d8b56ea410..8dd4e3daec 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -596,7 +596,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) rev.shown_one = 1; if (ret) break; - o = parse_object(t->tagged->oid.hash); + o = parse_object(&t->tagged->oid); if (!o) ret = error(_("Could not read object %s"), oid_to_hex(&t->tagged->oid)); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 00760ecc65..f06261cada 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -142,7 +142,7 @@ static int tipcmp(const void *a_, const void *b_) static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data) { - struct object *o = parse_object(oid->hash); + struct object *o = parse_object(oid); struct name_ref_data *data = cb_data; int can_abbreviate_output = data->tags_only && data->name_only; int deref = 0; @@ -200,7 +200,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo struct tag *t = (struct tag *) o; if (!t->tagged) break; /* broken repository */ - o = parse_object(t->tagged->oid.hash); + o = parse_object(&t->tagged->oid); deref = 1; taggerdate = t->date; } @@ -385,7 +385,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) } commit = NULL; - object = parse_object(oid.hash); + object = parse_object(&oid); if (object) { struct object *peeled = deref_tag(object, *argv, 0); if (peeled && peeled->type == OBJ_COMMIT) diff --git a/builtin/prune.c b/builtin/prune.c index 96dca7d58a..536366056a 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -127,7 +127,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix) const char *name = *argv++; if (!get_oid(name, &oid)) { - struct object *object = parse_object_or_die(oid.hash, name); + struct object *object = parse_object_or_die(&oid, + name); add_pending_object(&revs, object, ""); } else diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 6f0f788b0c..36e0e29ea2 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1058,8 +1058,8 @@ static const char *update(struct command *cmd, struct shallow_info *si) struct object *old_object, *new_object; struct commit *old_commit, *new_commit; - old_object = parse_object(old_oid->hash); - new_object = parse_object(new_oid->hash); + old_object = parse_object(old_oid); + new_object = parse_object(new_oid); if (!old_object || !new_object || old_object->type != OBJ_COMMIT || @@ -1082,7 +1082,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) if (is_null_oid(new_oid)) { struct strbuf err = STRBUF_INIT; - if (!parse_object(old_oid->hash)) { + if (!parse_object(old_oid)) { old_oid = NULL; if (ref_exists(name)) { rp_warning("Allowing deletion of corrupt ref."); diff --git a/builtin/reflog.c b/builtin/reflog.c index 7e89e84dce..8f3f1bd997 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -126,7 +126,7 @@ static int commit_is_complete(struct commit *commit) struct commit_list *parent; c = (struct commit *)study.objects[--study.nr].item; - if (!c->object.parsed && !parse_object(c->object.oid.hash)) + if (!c->object.parsed && !parse_object(&c->object.oid)) c->object.flags |= INCOMPLETE; if (c->object.flags & INCOMPLETE) { diff --git a/builtin/rev-list.c b/builtin/rev-list.c index bcf77f0b8a..1ddfca32be 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -181,7 +181,7 @@ static void finish_object(struct object *obj, const char *name, void *cb_data) if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) die("missing blob object '%s'", oid_to_hex(&obj->oid)); if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT) - parse_object(obj->oid.hash); + parse_object(&obj->oid); } static void show_object(struct object *obj, const char *name, void *cb_data) diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 7d5efa2b3b..8bc9997767 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -260,7 +260,8 @@ static void write_object(unsigned nr, enum object_type type, int eaten; hash_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash); added_object(nr, type, buf, size); - obj = parse_object_buffer(obj_list[nr].oid.hash, type, size, buf, &eaten); + obj = parse_object_buffer(&obj_list[nr].oid, type, size, buf, + &eaten); if (!obj) die("invalid %s", typename(type)); add_object_buffer(obj, buf, size); diff --git a/bundle.c b/bundle.c index 3386dba3be..f4abac4672 100644 --- a/bundle.c +++ b/bundle.c @@ -142,7 +142,7 @@ int verify_bundle(struct bundle_header *header, int verbose) init_revisions(&revs, NULL); for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; - struct object *o = parse_object(e->oid.hash); + struct object *o = parse_object(&e->oid); if (o) { o->flags |= PREREQ_MARK; add_pending_object(&revs, o, e->name); @@ -290,12 +290,14 @@ static int compute_and_write_prerequisites(int bundle_fd, if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); if (!get_oid_hex(buf.buf + 1, &oid)) { - struct object *object = parse_object_or_die(oid.hash, buf.buf); + struct object *object = parse_object_or_die(&oid, + buf.buf); object->flags |= UNINTERESTING; add_pending_object(revs, object, buf.buf); } } else if (!get_oid_hex(buf.buf, &oid)) { - struct object *object = parse_object_or_die(oid.hash, buf.buf); + struct object *object = parse_object_or_die(&oid, + buf.buf); object->flags |= SHOWN; } } @@ -379,7 +381,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) * end up triggering "empty bundle" * error. */ - obj = parse_object_or_die(oid.hash, e->name); + obj = parse_object_or_die(&oid, e->name); obj->flags |= SHOWN; add_pending_object(revs, obj, e->name); } diff --git a/commit.c b/commit.c index 8004008bc3..424237a8e9 100644 --- a/commit.c +++ b/commit.c @@ -21,7 +21,7 @@ const char *commit_type = "commit"; struct commit *lookup_commit_reference_gently(const struct object_id *oid, int quiet) { - struct object *obj = deref_tag(parse_object(oid->hash), NULL, 0); + struct object *obj = deref_tag(parse_object(oid), NULL, 0); if (!obj) return NULL; @@ -1589,7 +1589,7 @@ struct commit *get_merge_parent(const char *name) struct object_id oid; if (get_sha1(name, oid.hash)) return NULL; - obj = parse_object(oid.hash); + obj = parse_object(&oid); commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); if (commit && !commit->util) set_merge_remote_desc(commit, name, obj); diff --git a/fetch-pack.c b/fetch-pack.c index 7ec75f2782..2880f5d6a6 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -78,7 +78,7 @@ static void cache_one_alternate(const char *refname, void *vcache) { struct alternate_object_cache *cache = vcache; - struct object *obj = parse_object(oid->hash); + struct object *obj = parse_object(oid); if (!obj || (obj->flags & ALTERNATE)) return; @@ -120,7 +120,7 @@ static void rev_list_push(struct commit *commit, int mark) static int rev_list_insert_ref(const char *refname, const struct object_id *oid) { - struct object *o = deref_tag(parse_object(oid->hash), refname, 0); + struct object *o = deref_tag(parse_object(oid), refname, 0); if (o && o->type == OBJ_COMMIT) rev_list_push((struct commit *)o, SEEN); @@ -137,7 +137,7 @@ static int rev_list_insert_ref_oid(const char *refname, const struct object_id * static int clear_marks(const char *refname, const struct object_id *oid, int flag, void *cb_data) { - struct object *o = deref_tag(parse_object(oid->hash), refname, 0); + struct object *o = deref_tag(parse_object(oid), refname, 0); if (o && o->type == OBJ_COMMIT) clear_commit_marks((struct commit *)o, @@ -426,7 +426,7 @@ static int find_common(struct fetch_pack_args *args, if (!lookup_object(oid.hash)) die(_("object not found: %s"), line); /* make sure that it is parsed as shallow */ - if (!parse_object(oid.hash)) + if (!parse_object(&oid)) die(_("error in object: %s"), line); if (unregister_shallow(&oid)) die(_("no shallow found: %s"), line); @@ -557,14 +557,14 @@ static struct commit_list *complete; static int mark_complete(const struct object_id *oid) { - struct object *o = parse_object(oid->hash); + struct object *o = parse_object(oid); while (o && o->type == OBJ_TAG) { struct tag *t = (struct tag *) o; if (!t->tagged) break; /* broken repository */ o->flags |= COMPLETE; - o = parse_object(t->tagged->oid.hash); + o = parse_object(&t->tagged->oid); } if (o && o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; @@ -681,7 +681,7 @@ static int everything_local(struct fetch_pack_args *args, if (!has_object_file(&ref->old_oid)) continue; - o = parse_object(ref->old_oid.hash); + o = parse_object(&ref->old_oid); if (!o) continue; diff --git a/fsck.c b/fsck.c index ee5224aea9..90857e122d 100644 --- a/fsck.c +++ b/fsck.c @@ -461,7 +461,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options) return -1; if (obj->type == OBJ_NONE) - parse_object(obj->oid.hash); + parse_object(&obj->oid); switch (obj->type) { case OBJ_BLOB: diff --git a/http-backend.c b/http-backend.c index eef0a361f4..7663813323 100644 --- a/http-backend.c +++ b/http-backend.c @@ -431,7 +431,7 @@ static int show_text_ref(const char *name, const struct object_id *oid, { const char *name_nons = strip_namespace(name); struct strbuf *buf = cb_data; - struct object *o = parse_object(oid->hash); + struct object *o = parse_object(oid); if (!o) return 0; diff --git a/http-push.c b/http-push.c index 4e7bd9e42b..67c4d4b472 100644 --- a/http-push.c +++ b/http-push.c @@ -724,7 +724,7 @@ static void one_remote_object(const struct object_id *oid) obj = lookup_object(oid->hash); if (!obj) - obj = parse_object(oid->hash); + obj = parse_object(oid); /* Ignore remote objects that don't exist locally */ if (!obj) @@ -1462,7 +1462,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls) return; } - o = parse_object(ref->old_oid.hash); + o = parse_object(&ref->old_oid); if (!o) { fprintf(stderr, "Unable to parse object %s for remote ref %s\n", diff --git a/log-tree.c b/log-tree.c index 6532c892c2..a4ec11c2bf 100644 --- a/log-tree.c +++ b/log-tree.c @@ -105,13 +105,13 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, warning("invalid replace ref %s", refname); return 0; } - obj = parse_object(original_oid.hash); + obj = parse_object(&original_oid); if (obj) add_name_decoration(DECORATION_GRAFTED, "replaced", obj); return 0; } - obj = parse_object(oid->hash); + obj = parse_object(oid); if (!obj) return 0; @@ -132,7 +132,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, if (!obj) break; if (!obj->parsed) - parse_object(obj->oid.hash); + parse_object(&obj->oid); add_name_decoration(DECORATION_REF_TAG, refname, obj); } return 0; diff --git a/merge-recursive.c b/merge-recursive.c index 92e0a63dcc..ae5238d82c 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -2103,7 +2103,7 @@ static struct commit *get_ref(const struct object_id *oid, const char *name) { struct object *object; - object = deref_tag(parse_object(oid->hash), name, strlen(name)); + object = deref_tag(parse_object(oid), name, strlen(name)); if (!object) return NULL; if (object->type == OBJ_TREE) diff --git a/object.c b/object.c index dd4d3a1ea7..06ba3a11d8 100644 --- a/object.c +++ b/object.c @@ -180,24 +180,21 @@ struct object *lookup_unknown_object(const unsigned char *sha1) return obj; } -struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p) +struct object *parse_object_buffer(const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p) { - struct object_id oid; struct object *obj; *eaten_p = 0; - hashcpy(oid.hash, sha1); - obj = NULL; if (type == OBJ_BLOB) { - struct blob *blob = lookup_blob(&oid); + struct blob *blob = lookup_blob(oid); if (blob) { if (parse_blob_buffer(blob, buffer, size)) return NULL; obj = &blob->object; } } else if (type == OBJ_TREE) { - struct tree *tree = lookup_tree(&oid); + struct tree *tree = lookup_tree(oid); if (tree) { obj = &tree->object; if (!tree->buffer) @@ -209,7 +206,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t } } } else if (type == OBJ_COMMIT) { - struct commit *commit = lookup_commit(&oid); + struct commit *commit = lookup_commit(oid); if (commit) { if (parse_commit_buffer(commit, buffer, size)) return NULL; @@ -220,57 +217,54 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t obj = &commit->object; } } else if (type == OBJ_TAG) { - struct tag *tag = lookup_tag(&oid); + struct tag *tag = lookup_tag(oid); if (tag) { if (parse_tag_buffer(tag, buffer, size)) return NULL; obj = &tag->object; } } else { - warning("object %s has unknown type id %d", sha1_to_hex(sha1), type); + warning("object %s has unknown type id %d", oid_to_hex(oid), type); obj = NULL; } return obj; } -struct object *parse_object_or_die(const unsigned char *sha1, +struct object *parse_object_or_die(const struct object_id *oid, const char *name) { - struct object *o = parse_object(sha1); + struct object *o = parse_object(oid); if (o) return o; - die(_("unable to parse object: %s"), name ? name : sha1_to_hex(sha1)); + die(_("unable to parse object: %s"), name ? name : oid_to_hex(oid)); } -struct object *parse_object(const unsigned char *sha1) +struct object *parse_object(const struct object_id *oid) { unsigned long size; enum object_type type; int eaten; - const unsigned char *repl = lookup_replace_object(sha1); + const unsigned char *repl = lookup_replace_object(oid->hash); void *buffer; struct object *obj; - struct object_id oid; - hashcpy(oid.hash, sha1); - - obj = lookup_object(oid.hash); + obj = lookup_object(oid->hash); if (obj && obj->parsed) return obj; if ((obj && obj->type == OBJ_BLOB) || - (!obj && has_sha1_file(sha1) && - sha1_object_info(sha1, NULL) == OBJ_BLOB)) { + (!obj && has_object_file(oid) && + sha1_object_info(oid->hash, NULL) == OBJ_BLOB)) { if (check_sha1_signature(repl, NULL, 0, NULL) < 0) { - error("sha1 mismatch %s", sha1_to_hex(repl)); + error("sha1 mismatch %s", oid_to_hex(oid)); return NULL; } - parse_blob_buffer(lookup_blob(&oid), NULL, 0); - return lookup_object(sha1); + parse_blob_buffer(lookup_blob(oid), NULL, 0); + return lookup_object(oid->hash); } - buffer = read_sha1_file(sha1, &type, &size); + buffer = read_sha1_file(oid->hash, &type, &size); if (buffer) { if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) { free(buffer); @@ -278,7 +272,7 @@ struct object *parse_object(const unsigned char *sha1) return NULL; } - obj = parse_object_buffer(sha1, type, size, buffer, &eaten); + obj = parse_object_buffer(oid, type, size, buffer, &eaten); if (!eaten) free(buffer); return obj; diff --git a/object.h b/object.h index f52957dcb3..33e5cc9943 100644 --- a/object.h +++ b/object.h @@ -89,20 +89,20 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet); * * Returns NULL if the object is missing or corrupt. */ -struct object *parse_object(const unsigned char *sha1); +struct object *parse_object(const struct object_id *oid); /* * Like parse_object, but will die() instead of returning NULL. If the * "name" parameter is not NULL, it is included in the error message - * (otherwise, the sha1 hex is given). + * (otherwise, the hex object ID is given). */ -struct object *parse_object_or_die(const unsigned char *sha1, const char *name); +struct object *parse_object_or_die(const struct object_id *oid, const char *name); /* Given the result of read_sha1_file(), returns the object after * parsing it. eaten_p indicates if the object has a borrowed copy * of buffer and the caller should not free() it. */ -struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p); +struct object *parse_object_buffer(const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p); /** Returns the object, with potentially excess memory allocated. **/ struct object *lookup_unknown_object(const unsigned char *sha1); diff --git a/pack-bitmap.c b/pack-bitmap.c index 39bcc16846..a3ac3dccd4 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -673,7 +673,7 @@ int prepare_bitmap_walk(struct rev_info *revs) struct object *object = pending_e[i].item; if (object->type == OBJ_NONE) - parse_object_or_die(object->oid.hash, NULL); + parse_object_or_die(&object->oid, NULL); while (object->type == OBJ_TAG) { struct tag *tag = (struct tag *) object; @@ -685,7 +685,7 @@ int prepare_bitmap_walk(struct rev_info *revs) if (!tag->tagged) die("bad tag"); - object = parse_object_or_die(tag->tagged->oid.hash, NULL); + object = parse_object_or_die(&tag->tagged->oid, NULL); } if (object->flags & UNINTERESTING) diff --git a/pretty.c b/pretty.c index d0f86f5d85..c4a0ace34c 100644 --- a/pretty.c +++ b/pretty.c @@ -1137,7 +1137,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ /* these depend on the commit */ if (!commit->object.parsed) - parse_object(commit->object.oid.hash); + parse_object(&commit->object.oid); switch (placeholder[0]) { case 'H': /* commit hash */ diff --git a/reachable.c b/reachable.c index 3bbc84417f..69ca176f6c 100644 --- a/reachable.c +++ b/reachable.c @@ -33,7 +33,7 @@ static int add_one_ref(const char *path, const struct object_id *oid, return 0; } - object = parse_object_or_die(oid->hash, path); + object = parse_object_or_die(oid, path); add_pending_object(revs, object, ""); return 0; @@ -82,7 +82,7 @@ static void add_recent_object(const struct object_id *oid, switch (type) { case OBJ_TAG: case OBJ_COMMIT: - obj = parse_object_or_die(oid->hash, NULL); + obj = parse_object_or_die(oid, NULL); break; case OBJ_TREE: obj = (struct object *)lookup_tree(oid); diff --git a/ref-filter.c b/ref-filter.c index 56fc990a52..3f7cf71b5f 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -683,7 +683,7 @@ static void *get_obj(const struct object_id *oid, struct object **obj, unsigned void *buf = read_sha1_file(oid->hash, &type, sz); if (buf) - *obj = parse_object_buffer(oid->hash, type, *sz, buf, eaten); + *obj = parse_object_buffer(oid, type, *sz, buf, eaten); else *obj = NULL; return buf; @@ -1687,7 +1687,7 @@ static const struct object_id *match_points_at(struct oid_array *points_at, if (oid_array_lookup(points_at, oid) >= 0) return oid; - obj = parse_object(oid->hash); + obj = parse_object(oid); if (!obj) die(_("malformed object at '%s'"), refname); if (obj->type == OBJ_TAG) diff --git a/reflog-walk.c b/reflog-walk.c index c8fdf051d2..110e18f1f3 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -238,13 +238,13 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) do { reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; commit_reflog->recno--; - logobj = parse_object(reflog->ooid.hash); + logobj = parse_object(&reflog->ooid); } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT)); if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) { /* a root commit, but there are still more entries to show */ reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; - logobj = parse_object(reflog->noid.hash); + logobj = parse_object(&reflog->noid); } if (!logobj || logobj->type != OBJ_COMMIT) { diff --git a/refs/files-backend.c b/refs/files-backend.c index 2c360a4107..2cccdf71ae 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2057,7 +2057,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock, struct object *o; int fd; - o = parse_object(oid->hash); + o = parse_object(oid); if (!o) { strbuf_addf(err, "trying to write ref '%s' with nonexistent object %s", diff --git a/remote.c b/remote.c index bf9a47d95b..38ca1353b9 100644 --- a/remote.c +++ b/remote.c @@ -1954,12 +1954,12 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid) * Both new and old must be commit-ish and new is descendant of * old. Otherwise we require --force. */ - o = deref_tag(parse_object(old_oid->hash), NULL, 0); + o = deref_tag(parse_object(old_oid), NULL, 0); if (!o || o->type != OBJ_COMMIT) return 0; old = (struct commit *) o; - o = deref_tag(parse_object(new_oid->hash), NULL, 0); + o = deref_tag(parse_object(new_oid), NULL, 0); if (!o || o->type != OBJ_COMMIT) return 0; new = (struct commit *) o; diff --git a/revision.c b/revision.c index 80f74bb7b4..64e67e017f 100644 --- a/revision.c +++ b/revision.c @@ -181,7 +181,7 @@ void add_head_to_pending(struct rev_info *revs) struct object *obj; if (get_oid("HEAD", &oid)) return; - obj = parse_object(oid.hash); + obj = parse_object(&oid); if (!obj) return; add_pending_object(revs, obj, "HEAD"); @@ -193,7 +193,7 @@ static struct object *get_reference(struct rev_info *revs, const char *name, { struct object *object; - object = parse_object(oid->hash); + object = parse_object(oid); if (!object) { if (revs->ignore_missing) return object; @@ -228,7 +228,7 @@ static struct commit *handle_commit(struct rev_info *revs, add_pending_object(revs, object, tag->tag); if (!tag->tagged) die("bad tag"); - object = parse_object(tag->tagged->oid.hash); + object = parse_object(&tag->tagged->oid); if (!object) { if (flags & UNINTERESTING) return NULL; @@ -1200,7 +1200,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data) { struct all_refs_cb *cb = cb_data; if (!is_null_oid(oid)) { - struct object *o = parse_object(oid->hash); + struct object *o = parse_object(oid); if (o) { o->flags |= cb->all_flags; /* ??? CMDLINEFLAGS ??? */ @@ -1479,8 +1479,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi verify_non_filename(revs->prefix, arg); } - a_obj = parse_object(from_oid.hash); - b_obj = parse_object(oid.hash); + a_obj = parse_object(&from_oid); + b_obj = parse_object(&oid); if (!a_obj || !b_obj) { missing: if (revs->ignore_missing) diff --git a/server-info.c b/server-info.c index f6c1a3dfb0..6f865b73a3 100644 --- a/server-info.c +++ b/server-info.c @@ -53,7 +53,7 @@ static int add_info_ref(const char *path, const struct object_id *oid, int flag, void *cb_data) { FILE *fp = cb_data; - struct object *o = parse_object(oid->hash); + struct object *o = parse_object(oid); if (!o) return -1; diff --git a/sha1_name.c b/sha1_name.c index 72e72ab9ae..de8278530a 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -241,7 +241,7 @@ static int disambiguate_committish_only(const struct object_id *oid, void *cb_da return 0; /* We need to do this the hard way... */ - obj = deref_tag(parse_object(oid->hash), NULL, 0); + obj = deref_tag(parse_object(oid), NULL, 0); if (obj && obj->type == OBJ_COMMIT) return 1; return 0; @@ -265,7 +265,7 @@ static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_ return 0; /* We need to do this the hard way... */ - obj = deref_tag(parse_object(oid->hash), NULL, 0); + obj = deref_tag(parse_object(oid), NULL, 0); if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT)) return 1; return 0; @@ -776,7 +776,7 @@ struct object *peel_to_type(const char *name, int namelen, if (name && !namelen) namelen = strlen(name); while (1) { - if (!o || (!o->parsed && !parse_object(o->oid.hash))) + if (!o || (!o->parsed && !parse_object(&o->oid))) return NULL; if (expected_type == OBJ_ANY || o->type == expected_type) return o; @@ -849,12 +849,12 @@ static int peel_onion(const char *name, int len, unsigned char *sha1, if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags)) return -1; - o = parse_object(outer.hash); + o = parse_object(&outer); if (!o) return -1; if (!expected_type) { o = deref_tag(o, name, sp - name - 2); - if (!o || (!o->parsed && !parse_object(o->oid.hash))) + if (!o || (!o->parsed && !parse_object(&o->oid))) return -1; hashcpy(sha1, o->oid.hash); return 0; @@ -981,7 +981,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid, int flag, void *cb_data) { struct commit_list **list = cb_data; - struct object *object = parse_object(oid->hash); + struct object *object = parse_object(oid); if (!object) return 0; if (object->type == OBJ_TAG) { @@ -1027,7 +1027,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1, int matches; commit = pop_most_recent_commit(&list, ONELINE_SEEN); - if (!parse_object(commit->object.oid.hash)) + if (!parse_object(&commit->object.oid)) continue; buf = get_commit_buffer(commit, NULL); p = strstr(buf, "\n\n"); diff --git a/tag.c b/tag.c index 571798519f..eb7b146f4a 100644 --- a/tag.c +++ b/tag.c @@ -66,7 +66,7 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen) { while (o && o->type == OBJ_TAG) if (((struct tag *)o)->tagged) - o = parse_object(((struct tag *)o)->tagged->oid.hash); + o = parse_object(&((struct tag *)o)->tagged->oid); else o = NULL; if (!o && warn) { @@ -80,7 +80,7 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen) struct object *deref_tag_noverify(struct object *o) { while (o && o->type == OBJ_TAG) { - o = parse_object(o->oid.hash); + o = parse_object(&o->oid); if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged) o = ((struct tag *)o)->tagged; else diff --git a/tree.c b/tree.c index 9adcd8bd42..603b29ee80 100644 --- a/tree.c +++ b/tree.c @@ -234,7 +234,7 @@ void free_tree_buffer(struct tree *tree) struct tree *parse_tree_indirect(const struct object_id *oid) { - struct object *obj = parse_object(oid->hash); + struct object *obj = parse_object(oid); do { if (!obj) return NULL; @@ -247,6 +247,6 @@ struct tree *parse_tree_indirect(const struct object_id *oid) else return NULL; if (!obj->parsed) - parse_object(obj->oid.hash); + parse_object(&obj->oid); } while (1); } diff --git a/upload-pack.c b/upload-pack.c index 5b9d21089a..8619ec4354 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -296,7 +296,7 @@ static int got_oid(const char *hex, struct object_id *oid) if (!has_object_file(oid)) return -1; - o = parse_object(oid->hash); + o = parse_object(oid); if (!o) die("oops (%s)", oid_to_hex(oid)); if (o->type == OBJ_COMMIT) { @@ -334,7 +334,7 @@ static int reachable(struct commit *want) break; } if (!commit->object.parsed) - parse_object(commit->object.oid.hash); + parse_object(&commit->object.oid); if (commit->object.flags & REACHABLE) continue; commit->object.flags |= REACHABLE; @@ -755,7 +755,7 @@ static void receive_needs(void) struct object *object; if (get_oid_hex(arg, &oid)) die("invalid shallow line: %s", line); - object = parse_object(oid.hash); + object = parse_object(&oid); if (!object) continue; if (object->type != OBJ_COMMIT) @@ -821,7 +821,7 @@ static void receive_needs(void) if (parse_feature_request(features, "include-tag")) use_include_tag = 1; - o = parse_object(oid_buf.hash); + o = parse_object(&oid_buf); if (!o) { packet_write_fmt(1, "ERR upload-pack: not our ref %s", diff --git a/walker.c b/walker.c index eae9fb974f..274f1a4935 100644 --- a/walker.c +++ b/walker.c @@ -180,7 +180,7 @@ static int loop(struct walker *walker) } } if (!obj->type) - parse_object(obj->oid.hash); + parse_object(&obj->oid); if (process_object(walker, obj)) return -1; } From 3f789719a65bfa6c302e8f794847a3eb69b6881b Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Mon, 8 May 2017 21:34:58 +0100 Subject: [PATCH 178/566] archive-tar: fix a sparse 'constant too large' warning Commit dddbad728c ("timestamp_t: a new data type for timestamps", 26-04-2017) introduced a new typedef 'timestamp_t', as a synonym for an unsigned long, which was used at the time to represent timestamps in git. A later commit 28f4aee3fb ("use uintmax_t for timestamps", 26-04-2017) changed the typedef to use an 'uintmax_t' for the timestamp representation type. When building on a 32-bit Linux system, sparse complains that a constant (USTAR_MAX_MTIME) used to detect a 'far-future mtime' timestamp, is too large; 'warning: constant 077777777777UL is so big it is unsigned long long' on lines 335 and 338 of archive-tar.c. Note that both gcc and clang only issue a warning if this constant is used in a context that requires an 'unsigned long' (rather than an uintmax_t). (Since TIME_MAX is no longer equal to 0xFFFFFFFF, even on a 32-bit system, the macro USTAR_MAX_MTIME is set to 077777777777UL, which cannot be represented as an 'unsigned long' constant). In order to suppress the warning, change the definition of the macro constant USTAR_MAX_MTIME to use an 'ULL' type suffix. In a similar vein, on systems which use a 64-bit representation of the 'unsigned long' type, the USTAR_MAX_SIZE constant macro is defined with the value 077777777777ULL. Although this does not cause any warning messages to be issued, it would be more appropriate for this constant to use an 'UL' type suffix rather than 'ULL'. Signed-off-by: Ramsay Jones Signed-off-by: Junio C Hamano --- archive-tar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archive-tar.c b/archive-tar.c index 319a5b1c7c..073e60ebd3 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -28,12 +28,12 @@ static int write_tar_filter_archive(const struct archiver *ar, #if ULONG_MAX == 0xFFFFFFFF #define USTAR_MAX_SIZE ULONG_MAX #else -#define USTAR_MAX_SIZE 077777777777ULL +#define USTAR_MAX_SIZE 077777777777UL #endif #if TIME_MAX == 0xFFFFFFFF #define USTAR_MAX_MTIME TIME_MAX #else -#define USTAR_MAX_MTIME 077777777777UL +#define USTAR_MAX_MTIME 077777777777ULL #endif /* writes out the whole block, but only if it is full */ From e294e8959fea043225a023f981121540b14b08cd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 May 2017 19:30:24 -0700 Subject: [PATCH 179/566] apply.c: fix whitespace-only mismerge 4af9a7d3 ("Merge branch 'bc/object-id'", 2016-09-19) involved merging a lot of changes made to builtin/apply.c on the side branch manually to apply.c as an intervening commit 13b5af22 ("apply: move libified code from builtin/apply.c to apply.{c,h}", 2016-04-22) moved a lot of the lines changed on the side branch to a different file apply.c at the top-level, requiring manual patching of it. Apparently, the maintainer screwed up and made the code indent in a funny way while doing so. Reported-by: Stefan Beller Signed-off-by: Junio C Hamano --- apply.c | 312 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 156 insertions(+), 156 deletions(-) diff --git a/apply.c b/apply.c index 0e2caeab9c..acebd1cb49 100644 --- a/apply.c +++ b/apply.c @@ -4091,181 +4091,181 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list) res = write_locked_index(&result, &lock, COMMIT_LOCK); discard_index(&result); - if (res) - return error(_("could not write temporary index to %s"), - state->fake_ancestor); + if (res) + return error(_("could not write temporary index to %s"), + state->fake_ancestor); - return 0; - } + return 0; +} - static void stat_patch_list(struct apply_state *state, struct patch *patch) - { - int files, adds, dels; +static void stat_patch_list(struct apply_state *state, struct patch *patch) +{ + int files, adds, dels; - for (files = adds = dels = 0 ; patch ; patch = patch->next) { - files++; - adds += patch->lines_added; - dels += patch->lines_deleted; - show_stats(state, patch); - } + for (files = adds = dels = 0 ; patch ; patch = patch->next) { + files++; + adds += patch->lines_added; + dels += patch->lines_deleted; + show_stats(state, patch); + } - print_stat_summary(stdout, files, adds, dels); - } + print_stat_summary(stdout, files, adds, dels); +} - static void numstat_patch_list(struct apply_state *state, - struct patch *patch) - { - for ( ; patch; patch = patch->next) { - const char *name; - name = patch->new_name ? patch->new_name : patch->old_name; - if (patch->is_binary) - printf("-\t-\t"); - else - printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); - write_name_quoted(name, stdout, state->line_termination); - } - } +static void numstat_patch_list(struct apply_state *state, + struct patch *patch) +{ + for ( ; patch; patch = patch->next) { + const char *name; + name = patch->new_name ? patch->new_name : patch->old_name; + if (patch->is_binary) + printf("-\t-\t"); + else + printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); + write_name_quoted(name, stdout, state->line_termination); + } +} - static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name) - { - if (mode) - printf(" %s mode %06o %s\n", newdelete, mode, name); - else - printf(" %s %s\n", newdelete, name); - } +static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name) +{ + if (mode) + printf(" %s mode %06o %s\n", newdelete, mode, name); + else + printf(" %s %s\n", newdelete, name); +} - static void show_mode_change(struct patch *p, int show_name) - { - if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) { - if (show_name) - printf(" mode change %06o => %06o %s\n", - p->old_mode, p->new_mode, p->new_name); - else - printf(" mode change %06o => %06o\n", - p->old_mode, p->new_mode); - } - } +static void show_mode_change(struct patch *p, int show_name) +{ + if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) { + if (show_name) + printf(" mode change %06o => %06o %s\n", + p->old_mode, p->new_mode, p->new_name); + else + printf(" mode change %06o => %06o\n", + p->old_mode, p->new_mode); + } +} - static void show_rename_copy(struct patch *p) - { - const char *renamecopy = p->is_rename ? "rename" : "copy"; - const char *old, *new; +static void show_rename_copy(struct patch *p) +{ + const char *renamecopy = p->is_rename ? "rename" : "copy"; + const char *old, *new; - /* Find common prefix */ - old = p->old_name; - new = p->new_name; - while (1) { - const char *slash_old, *slash_new; - slash_old = strchr(old, '/'); - slash_new = strchr(new, '/'); - if (!slash_old || - !slash_new || - slash_old - old != slash_new - new || - memcmp(old, new, slash_new - new)) - break; - old = slash_old + 1; - new = slash_new + 1; - } - /* p->old_name thru old is the common prefix, and old and new - * through the end of names are renames - */ - if (old != p->old_name) - printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy, - (int)(old - p->old_name), p->old_name, - old, new, p->score); - else - printf(" %s %s => %s (%d%%)\n", renamecopy, - p->old_name, p->new_name, p->score); - show_mode_change(p, 0); - } + /* Find common prefix */ + old = p->old_name; + new = p->new_name; + while (1) { + const char *slash_old, *slash_new; + slash_old = strchr(old, '/'); + slash_new = strchr(new, '/'); + if (!slash_old || + !slash_new || + slash_old - old != slash_new - new || + memcmp(old, new, slash_new - new)) + break; + old = slash_old + 1; + new = slash_new + 1; + } + /* p->old_name thru old is the common prefix, and old and new + * through the end of names are renames + */ + if (old != p->old_name) + printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy, + (int)(old - p->old_name), p->old_name, + old, new, p->score); + else + printf(" %s %s => %s (%d%%)\n", renamecopy, + p->old_name, p->new_name, p->score); + show_mode_change(p, 0); +} - static void summary_patch_list(struct patch *patch) - { - struct patch *p; +static void summary_patch_list(struct patch *patch) +{ + struct patch *p; - for (p = patch; p; p = p->next) { - if (p->is_new) - show_file_mode_name("create", p->new_mode, p->new_name); - else if (p->is_delete) - show_file_mode_name("delete", p->old_mode, p->old_name); - else { - if (p->is_rename || p->is_copy) - show_rename_copy(p); - else { - if (p->score) { - printf(" rewrite %s (%d%%)\n", - p->new_name, p->score); - show_mode_change(p, 0); - } - else - show_mode_change(p, 1); - } - } - } - } + for (p = patch; p; p = p->next) { + if (p->is_new) + show_file_mode_name("create", p->new_mode, p->new_name); + else if (p->is_delete) + show_file_mode_name("delete", p->old_mode, p->old_name); + else { + if (p->is_rename || p->is_copy) + show_rename_copy(p); + else { + if (p->score) { + printf(" rewrite %s (%d%%)\n", + p->new_name, p->score); + show_mode_change(p, 0); + } + else + show_mode_change(p, 1); + } + } + } +} - static void patch_stats(struct apply_state *state, struct patch *patch) - { - int lines = patch->lines_added + patch->lines_deleted; +static void patch_stats(struct apply_state *state, struct patch *patch) +{ + int lines = patch->lines_added + patch->lines_deleted; - if (lines > state->max_change) - state->max_change = lines; - if (patch->old_name) { - int len = quote_c_style(patch->old_name, NULL, NULL, 0); - if (!len) - len = strlen(patch->old_name); - if (len > state->max_len) - state->max_len = len; - } - if (patch->new_name) { - int len = quote_c_style(patch->new_name, NULL, NULL, 0); - if (!len) - len = strlen(patch->new_name); - if (len > state->max_len) - state->max_len = len; - } - } + if (lines > state->max_change) + state->max_change = lines; + if (patch->old_name) { + int len = quote_c_style(patch->old_name, NULL, NULL, 0); + if (!len) + len = strlen(patch->old_name); + if (len > state->max_len) + state->max_len = len; + } + if (patch->new_name) { + int len = quote_c_style(patch->new_name, NULL, NULL, 0); + if (!len) + len = strlen(patch->new_name); + if (len > state->max_len) + state->max_len = len; + } +} - static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) - { - if (state->update_index) { - if (remove_file_from_cache(patch->old_name) < 0) - return error(_("unable to remove %s from index"), patch->old_name); - } - if (!state->cached) { - if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { - remove_path(patch->old_name); - } - } - return 0; - } +static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) +{ + if (state->update_index) { + if (remove_file_from_cache(patch->old_name) < 0) + return error(_("unable to remove %s from index"), patch->old_name); + } + if (!state->cached) { + if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { + remove_path(patch->old_name); + } + } + return 0; +} - static int add_index_file(struct apply_state *state, - const char *path, - unsigned mode, - void *buf, - unsigned long size) - { - struct stat st; - struct cache_entry *ce; - int namelen = strlen(path); - unsigned ce_size = cache_entry_size(namelen); +static int add_index_file(struct apply_state *state, + const char *path, + unsigned mode, + void *buf, + unsigned long size) +{ + struct stat st; + struct cache_entry *ce; + int namelen = strlen(path); + unsigned ce_size = cache_entry_size(namelen); - if (!state->update_index) - return 0; + if (!state->update_index) + return 0; - ce = xcalloc(1, ce_size); - memcpy(ce->name, path, namelen); - ce->ce_mode = create_ce_mode(mode); - ce->ce_flags = create_ce_flags(0); - ce->ce_namelen = namelen; - if (S_ISGITLINK(mode)) { - const char *s; + ce = xcalloc(1, ce_size); + memcpy(ce->name, path, namelen); + ce->ce_mode = create_ce_mode(mode); + ce->ce_flags = create_ce_flags(0); + ce->ce_namelen = namelen; + if (S_ISGITLINK(mode)) { + const char *s; - if (!skip_prefix(buf, "Subproject commit ", &s) || - get_oid_hex(s, &ce->oid)) { + if (!skip_prefix(buf, "Subproject commit ", &s) || + get_oid_hex(s, &ce->oid)) { free(ce); - return error(_("corrupt patch for submodule %s"), path); + return error(_("corrupt patch for submodule %s"), path); } } else { if (!state->cached) { From 9df4a6074a98740b70b980d43dca5203db0ff688 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 8 May 2017 22:54:13 -0400 Subject: [PATCH 180/566] pack-objects: disable pack reuse for object-selection options If certain options like --honor-pack-keep, --local, or --incremental are used with pack-objects, then we need to feed each potential object to want_object_in_pack() to see if it should be filtered out. But when the bitmap reuse_packfile optimization is in effect, we do not call that function at all, and in fact skip adding the objects to the to_pack list entirely. This means we have a bug: for certain requests we will silently ignore those options and include objects in that pack that should not be there. The problem has been present since the inception of the pack-reuse code in 6b8fda2db (pack-objects: use bitmaps when packing objects, 2013-12-21), but it was unlikely to come up in practice. These options are generally used for on-disk packing, not transfer packs (which go to stdout), but we've never allowed pack reuse for non-stdout packs (until 645c432d6, we did not even use bitmaps, which the reuse optimization relies on; after that, we explicitly turned it off when not packing to stdout). We can fix this by just disabling the reuse_packfile optimization when the options are in use. In theory we could teach the pack-reuse code to satisfy these checks, but it's not worth the complexity. The purpose of the optimization is to keep the amount of per-object work we do to a minimum. But these options inherently require us to search for other copies of each object, drowning out any benefit of the pack-reuse optimization. But note that the optimizations from 56dfeb626 (pack-objects: compute local/ignore_pack_keep early, 2016-07-29) happen before pack-reuse, meaning that specifying "--honor-pack-keep" in a repository with no .keep files can still follow the fast path. There are tests in t5310 that check these options with bitmaps and --stdout, but they didn't catch the bug, and it's hard to adapt them to do so. One problem is that they don't use --delta-base-offset; without that option, we always disable the reuse optimization entirely. It would be fine to add it in (it actually makes the test more realistic), but that still isn't quite enough. The other problem is that the reuse code is very picky; it only kicks in when it can reuse most of a pack, starting from the first byte. So we'd have to start from a fully repacked and bitmapped state to trigger it. But the tests for these options use a much more subtle state; they want to be sure that the want_object_in_pack() code is allowing some objects but not others. Doing a full repack runs counter to that. So this patch adds new tests at the end of the script which create the fully-packed state and make sure that each option is not fooled by reusable pack. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 6 +++++- t/t5310-pack-bitmaps.sh | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 8841f8b366..9cd13dfdbe 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2635,7 +2635,11 @@ static void loosen_unused_packed_objects(struct rev_info *revs) */ static int pack_options_allow_reuse(void) { - return pack_to_stdout && allow_ofs_delta; + return pack_to_stdout && + allow_ofs_delta && + !ignore_packed_keep && + (!local || !have_non_local_packs) && + !incremental; } static int get_object_list_from_bitmap(struct rev_info *revs) diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index 424bec7d77..c3ddfa217f 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -289,4 +289,42 @@ test_expect_success 'splitting packs does not generate bogus bitmaps' ' git -C no-bitmaps.git fetch .. HEAD ' +test_expect_success 'set up reusable pack' ' + rm -f .git/objects/pack/*.keep && + git repack -adb && + reusable_pack () { + git for-each-ref --format="%(objectname)" | + git pack-objects --delta-base-offset --revs --stdout "$@" + } +' + +test_expect_success 'pack reuse respects --honor-pack-keep' ' + test_when_finished "rm -f .git/objects/pack/*.keep" && + for i in .git/objects/pack/*.pack; do + >${i%.pack}.keep + done && + reusable_pack --honor-pack-keep >empty.pack && + git index-pack empty.pack && + >expect && + git show-index actual && + test_cmp expect actual +' + +test_expect_success 'pack reuse respects --local' ' + mv .git/objects/pack/* alt.git/objects/pack/ && + test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" && + reusable_pack --local >empty.pack && + git index-pack empty.pack && + >expect && + git show-index actual && + test_cmp expect actual +' + +test_expect_success 'pack reuse respects --incremental' ' + reusable_pack --incremental >empty.pack && + git index-pack empty.pack && + >expect && + git show-index actual && + test_cmp expect actual +' test_done From da5a1f81008f89f4e533006df8fa536e43c9c677 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 8 May 2017 22:59:46 -0400 Subject: [PATCH 181/566] t5310: fix "; do" style Our usual shell style is to put the "do" of a loop on its own line, like: while $cond do something done instead of: while $cond; do something done We have a bit of both in our code base, but the former is what's in CodingGuidelines (and outnumbers the latter in t/ by about 6:1). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t5310-pack-bitmaps.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index c3ddfa217f..20e2473a03 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -20,11 +20,13 @@ has_any () { } test_expect_success 'setup repo with moderate-sized history' ' - for i in $(test_seq 1 10); do + for i in $(test_seq 1 10) + do test_commit $i done && git checkout -b other HEAD~5 && - for i in $(test_seq 1 10); do + for i in $(test_seq 1 10) + do test_commit side-$i done && git checkout master && @@ -104,7 +106,8 @@ test_expect_success 'clone from bitmapped repository' ' ' test_expect_success 'setup further non-bitmapped commits' ' - for i in $(test_seq 1 10); do + for i in $(test_seq 1 10) + do test_commit further-$i done ' @@ -300,7 +303,8 @@ test_expect_success 'set up reusable pack' ' test_expect_success 'pack reuse respects --honor-pack-keep' ' test_when_finished "rm -f .git/objects/pack/*.keep" && - for i in .git/objects/pack/*.pack; do + for i in .git/objects/pack/*.pack + do >${i%.pack}.keep done && reusable_pack --honor-pack-keep >empty.pack && From b7b744f297b736c51636407f5883b3777bb36433 Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Mon, 8 May 2017 14:33:50 -0700 Subject: [PATCH 182/566] docs: correct receive.advertisePushOptions default In commit c714e45 ("receive-pack: implement advertising and receiving push options", 2016-07-14), receive-pack was taught to (among other things) advertise that it understood push options, depending on configuration. It was documented that it advertised such ability by default; however, it actually does not. (In that commit, notice that advertise_push_options defaults to 0, unlike advertise_atomic_push which defaults to 1.) Update the documentation to state that it does not advertise the ability by default. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano --- Documentation/config.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index e43d147825..5eb326ed21 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2537,9 +2537,8 @@ receive.advertiseAtomic:: capability, set this variable to false. receive.advertisePushOptions:: - By default, git-receive-pack will advertise the push options - capability to its clients. If you don't want to advertise this - capability, set this variable to false. + When set to true, git-receive-pack will advertise the push options + capability to its clients. False by default. receive.autogc:: By default, git-receive-pack will run "git-gc --auto" after From cf5e77223a012737930e1bbacc85c6423b03af77 Mon Sep 17 00:00:00 2001 From: Marc Branchaud Date: Mon, 8 May 2017 12:03:36 -0400 Subject: [PATCH 183/566] diff: make the indent heuristic part of diff's basic configuration This heuristic was originally introduced as an experimental feature, and therefore part of the UI configuration. But the user often sees diffs generated by plumbing commands like diff-tree. Moving the indent heuristic into diff's basic configuration prepares the way for diff plumbing commands to respect the setting. The heuristic itself merely makes the diffs more aesthetically pleasing, without changing their correctness. Scripts that rely on the diff plumbing commands should not care whether or not the heuristic is employed. Signed-off-by: Marc Branchaud Signed-off-by: Junio C Hamano --- diff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/diff.c b/diff.c index 11eef1c85d..da96577ea8 100644 --- a/diff.c +++ b/diff.c @@ -290,9 +290,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) return 0; } - if (git_diff_heuristic_config(var, value, cb) < 0) - return -1; - if (!strcmp(var, "diff.wserrorhighlight")) { int val = parse_ws_error_highlight(value); if (val < 0) @@ -351,6 +348,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); + if (git_diff_heuristic_config(var, value, cb) < 0) + return -1; + return git_default_config(var, value, cb); } From 37590ce3c535f92e28da83893f843cc0099873bf Mon Sep 17 00:00:00 2001 From: Marc Branchaud Date: Mon, 8 May 2017 12:03:37 -0400 Subject: [PATCH 184/566] diff: have the diff-* builtins configure diff before initializing revisions This matches how the diff Porcelain works. It makes the plumbing commands respect diff's configuration options, such as indentHeuristic, because init_revisions() calls diff_setup() which fills in the diff_options struct. Signed-off-by: Marc Branchaud Signed-off-by: Junio C Hamano --- builtin/diff-files.c | 2 +- builtin/diff-index.c | 2 +- builtin/diff-tree.c | 2 +- t/t4061-diff-indent.sh | 66 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 15c61fd8d1..a572da9d51 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -20,9 +20,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) int result; unsigned options = 0; + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(&rev, prefix); gitmodules_config(); - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ rev.abbrev = 0; precompose_argv(argc, argv); diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 1af373d002..f084826a29 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -17,9 +17,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) int i; int result; + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(&rev, prefix); gitmodules_config(); - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ rev.abbrev = 0; precompose_argv(argc, argv); diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 326f88b657..36a3a19761 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -105,9 +105,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) struct setup_revision_opt s_r_opt; int read_stdin = 0; + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(opt, prefix); gitmodules_config(); - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ opt->abbrev = 0; opt->diff = 1; opt->disable_stdin = 1; diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh index 556450609b..13d3dc96a0 100755 --- a/t/t4061-diff-indent.sh +++ b/t/t4061-diff-indent.sh @@ -213,4 +213,70 @@ test_expect_success 'blame: --no-indent-heuristic overrides config' ' compare_blame spaces-expect out-blame2 ' +test_expect_success 'diff-tree: nice spaces with --indent-heuristic' ' + git diff-tree --indent-heuristic -p old new -- spaces.txt >out-diff-tree-compacted && + compare_diff spaces-compacted-expect out-diff-tree-compacted +' + +test_expect_success 'diff-tree: nice spaces with diff.indentHeuristic' ' + git -c diff.indentHeuristic=true diff-tree -p old new -- spaces.txt >out-diff-tree-compacted2 && + compare_diff spaces-compacted-expect out-diff-tree-compacted2 +' + +test_expect_success 'diff-tree: --no-indent-heuristic overrides config' ' + git -c diff.indentHeuristic=true diff-tree --no-indent-heuristic -p old new -- spaces.txt >out-diff-tree && + compare_diff spaces-expect out-diff-tree +' + +test_expect_success 'diff-index: nice spaces with --indent-heuristic' ' + git checkout -B diff-index && + git reset --soft HEAD~ && + git diff-index --indent-heuristic -p old -- spaces.txt >out-diff-index-compacted && + compare_diff spaces-compacted-expect out-diff-index-compacted && + git checkout -f master +' + +test_expect_success 'diff-index: nice spaces with diff.indentHeuristic' ' + git checkout -B diff-index && + git reset --soft HEAD~ && + git -c diff.indentHeuristic=true diff-index -p old -- spaces.txt >out-diff-index-compacted2 && + compare_diff spaces-compacted-expect out-diff-index-compacted2 && + git checkout -f master +' + +test_expect_success 'diff-index: --no-indent-heuristic overrides config' ' + git checkout -B diff-index && + git reset --soft HEAD~ && + git -c diff.indentHeuristic=true diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index && + compare_diff spaces-expect out-diff-index && + git checkout -f master +' + +test_expect_success 'diff-files: nice spaces with diff.indentHeuristic' ' + git checkout -B diff-files && + git reset HEAD~ && + git -c diff.indentHeuristic=true diff-files -p spaces.txt >out-diff-files-raw && + grep -v index out-diff-files-raw >out-diff-files-compacted && + compare_diff spaces-compacted-expect out-diff-files-compacted && + git checkout -f master +' + +test_expect_success 'diff-files: nice spaces with diff.indentHeuristic' ' + git checkout -B diff-files && + git reset HEAD~ && + git -c diff.indentHeuristic=true diff-files -p spaces.txt >out-diff-files-raw2 && + grep -v index out-diff-files-raw2 >out-diff-files-compacted2 && + compare_diff spaces-compacted-expect out-diff-files-compacted2 && + git checkout -f master +' + +test_expect_success 'diff-files: --no-indent-heuristic overrides config' ' + git checkout -B diff-files && + git reset HEAD~ && + git -c diff.indentHeuristic=true diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw3 && + grep -v index out-diff-files-raw3 >out-diff-files && + compare_diff spaces-expect out-diff-files && + git checkout -f master +' + test_done From 33de7163879ca83be7d6e1583c125f84e1e7c329 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Mon, 8 May 2017 12:03:38 -0400 Subject: [PATCH 185/566] diff: enable indent heuristic by default The feature was included in v2.11 (released 2016-11-29) and we got no negative feedback. Quite the opposite, all feedback we got was positive. Turn it on by default. Users who dislike the feature can turn it off by setting diff.indentHeuristic (which also configures plumbing commands, see prior patches). The change to t/t4051-diff-function-context.sh is needed because the heuristic shifts the changed hunk in the patch. To get the same result regardless of the heuristic configuration, we modify the test file differently: We insert a completely new line after line 2, instead of simply duplicating it. Helped-by: Jeff King Signed-off-by: Stefan Beller Signed-off-by: Marc Branchaud Signed-off-by: Junio C Hamano --- diff.c | 2 +- t/t4051-diff-function-context.sh | 3 +- t/t4061-diff-indent.sh | 148 ++++++++++++++++++++++++------- 3 files changed, 120 insertions(+), 33 deletions(-) diff --git a/diff.c b/diff.c index da96577ea8..2c47ccb4a7 100644 --- a/diff.c +++ b/diff.c @@ -27,7 +27,7 @@ #endif static int diff_detect_rename_default; -static int diff_indent_heuristic; /* experimental */ +static int diff_indent_heuristic = 1; static int diff_rename_limit_default = 400; static int diff_suppress_blank_empty; static int diff_use_color_default = -1; diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh index 6154acb456..3e6b485ecb 100755 --- a/t/t4051-diff-function-context.sh +++ b/t/t4051-diff-function-context.sh @@ -72,7 +72,8 @@ test_expect_success 'setup' ' # overlap function context of 1st change and -u context of 2nd change grep -v "delete me from hello" <"$dir/hello.c" >file.c && - sed 2p <"$dir/dummy.c" >>file.c && + sed "2a\\ + extra line" <"$dir/dummy.c" >>file.c && commit_and_tag changed_hello_dummy file.c && git checkout initial && diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh index 13d3dc96a0..2affd7a100 100755 --- a/t/t4061-diff-indent.sh +++ b/t/t4061-diff-indent.sh @@ -152,26 +152,28 @@ test_expect_success 'prepare' ' EOF ' +# --- diff tests ---------------------------------------------------------- + test_expect_success 'diff: ugly spaces' ' - git diff old new -- spaces.txt >out && + git diff --no-indent-heuristic old new -- spaces.txt >out && compare_diff spaces-expect out ' -test_expect_success 'diff: nice spaces with --indent-heuristic' ' - git diff --indent-heuristic old new -- spaces.txt >out-compacted && - compare_diff spaces-compacted-expect out-compacted -' - -test_expect_success 'diff: nice spaces with diff.indentHeuristic' ' - git -c diff.indentHeuristic=true diff old new -- spaces.txt >out-compacted2 && - compare_diff spaces-compacted-expect out-compacted2 -' - test_expect_success 'diff: --no-indent-heuristic overrides config' ' git -c diff.indentHeuristic=true diff --no-indent-heuristic old new -- spaces.txt >out2 && compare_diff spaces-expect out2 ' +test_expect_success 'diff: nice spaces with --indent-heuristic' ' + git -c diff.indentHeuristic=false diff --indent-heuristic old new -- spaces.txt >out-compacted && + compare_diff spaces-compacted-expect out-compacted +' + +test_expect_success 'diff: nice spaces with diff.indentHeuristic=true' ' + git -c diff.indentHeuristic=true diff old new -- spaces.txt >out-compacted2 && + compare_diff spaces-compacted-expect out-compacted2 +' + test_expect_success 'diff: --indent-heuristic with --patience' ' git diff --indent-heuristic --patience old new -- spaces.txt >out-compacted3 && compare_diff spaces-compacted-expect out-compacted3 @@ -183,7 +185,7 @@ test_expect_success 'diff: --indent-heuristic with --histogram' ' ' test_expect_success 'diff: ugly functions' ' - git diff old new -- functions.c >out && + git diff --no-indent-heuristic old new -- functions.c >out && compare_diff functions-expect out ' @@ -192,42 +194,73 @@ test_expect_success 'diff: nice functions with --indent-heuristic' ' compare_diff functions-compacted-expect out-compacted ' -test_expect_success 'blame: ugly spaces' ' - git blame old..new -- spaces.txt >out-blame && - compare_blame spaces-expect out-blame -' +# --- blame tests --------------------------------------------------------- test_expect_success 'blame: nice spaces with --indent-heuristic' ' git blame --indent-heuristic old..new -- spaces.txt >out-blame-compacted && compare_blame spaces-compacted-expect out-blame-compacted ' -test_expect_success 'blame: nice spaces with diff.indentHeuristic' ' +test_expect_success 'blame: nice spaces with diff.indentHeuristic=true' ' git -c diff.indentHeuristic=true blame old..new -- spaces.txt >out-blame-compacted2 && compare_blame spaces-compacted-expect out-blame-compacted2 ' -test_expect_success 'blame: --no-indent-heuristic overrides config' ' - git -c diff.indentHeuristic=true blame --no-indent-heuristic old..new -- spaces.txt >out-blame2 && - git blame old..new -- spaces.txt >out-blame && +test_expect_success 'blame: ugly spaces with --no-indent-heuristic' ' + git blame --no-indent-heuristic old..new -- spaces.txt >out-blame && + compare_blame spaces-expect out-blame +' + +test_expect_success 'blame: ugly spaces with diff.indentHeuristic=false' ' + git -c diff.indentHeuristic=false blame old..new -- spaces.txt >out-blame2 && compare_blame spaces-expect out-blame2 ' +test_expect_success 'blame: --no-indent-heuristic overrides config' ' + git -c diff.indentHeuristic=true blame --no-indent-heuristic old..new -- spaces.txt >out-blame3 && + git blame old..new -- spaces.txt >out-blame && + compare_blame spaces-expect out-blame3 +' + +test_expect_success 'blame: --indent-heuristic overrides config' ' + git -c diff.indentHeuristic=false blame --indent-heuristic old..new -- spaces.txt >out-blame-compacted3 && + compare_blame spaces-compacted-expect out-blame-compacted2 +' + +# --- diff-tree tests ----------------------------------------------------- + test_expect_success 'diff-tree: nice spaces with --indent-heuristic' ' git diff-tree --indent-heuristic -p old new -- spaces.txt >out-diff-tree-compacted && compare_diff spaces-compacted-expect out-diff-tree-compacted ' -test_expect_success 'diff-tree: nice spaces with diff.indentHeuristic' ' +test_expect_success 'diff-tree: nice spaces with diff.indentHeuristic=true' ' git -c diff.indentHeuristic=true diff-tree -p old new -- spaces.txt >out-diff-tree-compacted2 && compare_diff spaces-compacted-expect out-diff-tree-compacted2 ' -test_expect_success 'diff-tree: --no-indent-heuristic overrides config' ' - git -c diff.indentHeuristic=true diff-tree --no-indent-heuristic -p old new -- spaces.txt >out-diff-tree && +test_expect_success 'diff-tree: ugly spaces with --no-indent-heuristic' ' + git diff-tree --no-indent-heuristic -p old new -- spaces.txt >out-diff-tree && compare_diff spaces-expect out-diff-tree ' +test_expect_success 'diff-tree: ugly spaces with diff.indentHeuristic=false' ' + git -c diff.indentHeuristic=false diff-tree -p old new -- spaces.txt >out-diff-tree2 && + compare_diff spaces-expect out-diff-tree2 +' + +test_expect_success 'diff-tree: --indent-heuristic overrides config' ' + git -c diff.indentHeuristic=false diff-tree --indent-heuristic -p old new -- spaces.txt >out-diff-tree-compacted3 && + compare_diff spaces-compacted-expect out-diff-tree-compacted3 +' + +test_expect_success 'diff-tree: --no-indent-heuristic overrides config' ' + git -c diff.indentHeuristic=true diff-tree --no-indent-heuristic -p old new -- spaces.txt >out-diff-tree3 && + compare_diff spaces-expect out-diff-tree3 +' + +# --- diff-index tests ---------------------------------------------------- + test_expect_success 'diff-index: nice spaces with --indent-heuristic' ' git checkout -B diff-index && git reset --soft HEAD~ && @@ -236,7 +269,7 @@ test_expect_success 'diff-index: nice spaces with --indent-heuristic' ' git checkout -f master ' -test_expect_success 'diff-index: nice spaces with diff.indentHeuristic' ' +test_expect_success 'diff-index: nice spaces with diff.indentHeuristic=true' ' git checkout -B diff-index && git reset --soft HEAD~ && git -c diff.indentHeuristic=true diff-index -p old -- spaces.txt >out-diff-index-compacted2 && @@ -244,24 +277,50 @@ test_expect_success 'diff-index: nice spaces with diff.indentHeuristic' ' git checkout -f master ' -test_expect_success 'diff-index: --no-indent-heuristic overrides config' ' +test_expect_success 'diff-index: ugly spaces with --no-indent-heuristic' ' git checkout -B diff-index && git reset --soft HEAD~ && - git -c diff.indentHeuristic=true diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index && + git diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index && compare_diff spaces-expect out-diff-index && git checkout -f master ' -test_expect_success 'diff-files: nice spaces with diff.indentHeuristic' ' +test_expect_success 'diff-index: ugly spaces with diff.indentHeuristic=false' ' + git checkout -B diff-index && + git reset --soft HEAD~ && + git -c diff.indentHeuristic=false diff-index -p old -- spaces.txt >out-diff-index2 && + compare_diff spaces-expect out-diff-index2 && + git checkout -f master +' + +test_expect_success 'diff-index: --indent-heuristic overrides config' ' + git checkout -B diff-index && + git reset --soft HEAD~ && + git -c diff.indentHeuristic=false diff-index --indent-heuristic -p old -- spaces.txt >out-diff-index-compacted3 && + compare_diff spaces-compacted-expect out-diff-index-compacted3 && + git checkout -f master +' + +test_expect_success 'diff-index: --no-indent-heuristic overrides config' ' + git checkout -B diff-index && + git reset --soft HEAD~ && + git -c diff.indentHeuristic=true diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index3 && + compare_diff spaces-expect out-diff-index3 && + git checkout -f master +' + +# --- diff-files tests ---------------------------------------------------- + +test_expect_success 'diff-files: nice spaces with --indent-heuristic' ' git checkout -B diff-files && git reset HEAD~ && - git -c diff.indentHeuristic=true diff-files -p spaces.txt >out-diff-files-raw && + git diff-files --indent-heuristic -p spaces.txt >out-diff-files-raw && grep -v index out-diff-files-raw >out-diff-files-compacted && compare_diff spaces-compacted-expect out-diff-files-compacted && git checkout -f master ' -test_expect_success 'diff-files: nice spaces with diff.indentHeuristic' ' +test_expect_success 'diff-files: nice spaces with diff.indentHeuristic=true' ' git checkout -B diff-files && git reset HEAD~ && git -c diff.indentHeuristic=true diff-files -p spaces.txt >out-diff-files-raw2 && @@ -270,11 +329,38 @@ test_expect_success 'diff-files: nice spaces with diff.indentHeuristic' ' git checkout -f master ' +test_expect_success 'diff-files: ugly spaces with --no-indent-heuristic' ' + git checkout -B diff-files && + git reset HEAD~ && + git diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw && + grep -v index out-diff-files-raw >out-diff-files && + compare_diff spaces-expect out-diff-files && + git checkout -f master +' + +test_expect_success 'diff-files: ugly spaces with diff.indentHeuristic=false' ' + git checkout -B diff-files && + git reset HEAD~ && + git -c diff.indentHeuristic=false diff-files -p spaces.txt >out-diff-files-raw2 && + grep -v index out-diff-files-raw2 >out-diff-files && + compare_diff spaces-expect out-diff-files && + git checkout -f master +' + +test_expect_success 'diff-files: --indent-heuristic overrides config' ' + git checkout -B diff-files && + git reset HEAD~ && + git -c diff.indentHeuristic=false diff-files --indent-heuristic -p spaces.txt >out-diff-files-raw3 && + grep -v index out-diff-files-raw3 >out-diff-files-compacted && + compare_diff spaces-compacted-expect out-diff-files-compacted && + git checkout -f master +' + test_expect_success 'diff-files: --no-indent-heuristic overrides config' ' git checkout -B diff-files && git reset HEAD~ && - git -c diff.indentHeuristic=true diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw3 && - grep -v index out-diff-files-raw3 >out-diff-files && + git -c diff.indentHeuristic=true diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw4 && + grep -v index out-diff-files-raw4 >out-diff-files && compare_diff spaces-expect out-diff-files && git checkout -f master ' From 1fa8a66bf76953c6fb9cfe5e17b26a3a0920f538 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 8 May 2017 12:03:39 -0400 Subject: [PATCH 186/566] add--interactive: drop diff.indentHeuristic handling Now that diff.indentHeuristic is handled automatically by the plumbing commands, there's no need to propagate it manually. Signed-off-by: Jeff King Signed-off-by: Marc Branchaud Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 709a5f6ce6..79d675b5b0 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -46,7 +46,6 @@ my ($diff_new_color) = my $normal_color = $repo->get_color("", "reset"); my $diff_algorithm = $repo->config('diff.algorithm'); -my $diff_indent_heuristic = $repo->config_bool('diff.indentheuristic'); my $diff_filter = $repo->config('interactive.difffilter'); my $use_readkey = 0; @@ -730,9 +729,6 @@ sub parse_diff { if (defined $diff_algorithm) { splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}"; } - if ($diff_indent_heuristic) { - splice @diff_cmd, 1, 0, "--indent-heuristic"; - } if (defined $patch_mode_revision) { push @diff_cmd, get_diff_reference($patch_mode_revision); } From 443a12f37be1c5967785b83bf04935fe357afb9b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 May 2017 13:21:06 +0900 Subject: [PATCH 187/566] checkout: fix memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When "git checkout -m" does an in-core three-way merge to carry local modifications forward to check out a different branch, the code forgot to free the updated contents it has in-core. Noticed-by: René Scharfe Signed-off-by: Junio C Hamano --- builtin/checkout.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/checkout.c b/builtin/checkout.c index 5faea3a05f..94849cf250 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -247,6 +247,7 @@ static int checkout_merged(int pos, const struct checkout *state) if (write_sha1_file(result_buf.ptr, result_buf.size, blob_type, oid.hash)) die(_("Unable to add merge result for '%s'"), path); + free(result_buf.ptr); ce = make_cache_entry(mode, oid.hash, path, 2, 0); if (!ce) die(_("make_cache_entry failed for path '%s'"), path); From 5840eb9d1412b60982e78a6557b93b0fc162054d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 May 2017 10:38:59 +0900 Subject: [PATCH 188/566] doc: replace more gmane links Signed-off-by: Junio C Hamano --- Documentation/git-bisect-lk2009.txt | 2 +- git-rebase--interactive.sh | 2 +- t/t4038-diff-combined.sh | 2 +- tree-walk.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-bisect-lk2009.txt b/Documentation/git-bisect-lk2009.txt index b5d5e8b544..78479b003e 100644 --- a/Documentation/git-bisect-lk2009.txt +++ b/Documentation/git-bisect-lk2009.txt @@ -1353,6 +1353,6 @@ References - [[[4]]] https://public-inbox.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/[Junio C Hamano. 'Automated bisect success story'.] - [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.] - [[[6]]] https://lwn.net/Articles/277872/[Jonathan Corbet. 'Bisection divides users and developers'. LWN.net.] -- [[[7]]] http://article.gmane.org/gmane.linux.scsi/36652/[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Gmane.] +- [[[7]]] http://marc.info/?l=linux-kernel&m=119702753411680&w=2[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Linux-kernel mailing list.] - [[[8]]] https://www.kernel.org/pub/software/scm/git/docs/git-bisect.html[Junio C Hamano and the git-list. 'git-bisect(1) Manual Page'. Linux Kernel Archives.] - [[[9]]] https://github.com/Ealdwulf/bbchop[Ealdwulf. 'bbchop'. GitHub.] diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 2c9c0165b5..90b1fbe9cf 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -5,7 +5,7 @@ # Copyright (c) 2006 Johannes E. Schindelin # # The original idea comes from Eric W. Biederman, in -# http://article.gmane.org/gmane.comp.version-control.git/22407 +# https://public-inbox.org/git/m1odwkyuf5.fsf_-_@ebiederm.dsl.xmission.com/ # # The file containing rebase commands, comments, and empty lines. # This file is created by "git rebase -i" then edited by the user. As diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh index 0b4f7dfdc6..e2824d3437 100755 --- a/t/t4038-diff-combined.sh +++ b/t/t4038-diff-combined.sh @@ -354,7 +354,7 @@ test_expect_failure 'combine diff coalesce three parents' ' ' # Test for a bug reported at -# http://thread.gmane.org/gmane.comp.version-control.git/224410 +# https://public-inbox.org/git/20130515143508.GO25742@login.drsnuggles.stderr.nl/ # where a delete lines were missing from combined diff output when they # occurred exactly before the context lines of a later change. test_expect_success 'combine diff missing delete bug' ' diff --git a/tree-walk.c b/tree-walk.c index ff77605680..f25a08fddf 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -1075,7 +1075,7 @@ match_wildcards: * later on. * max_depth is ignored but we may consider support it * in future, see - * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840 + * https://public-inbox.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/ */ if (ps->recursive && S_ISDIR(entry->mode)) return entry_interesting; From cbaf82cc6b734244a9190c3341ca5f08f8d5acc4 Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Tue, 9 May 2017 12:23:53 -0700 Subject: [PATCH 189/566] receive-pack: verify push options in cert In commit f6a4e61 ("push: accept push options", 2016-07-14), send-pack was taught to include push options both within the signed cert (if the push is a signed push) and outside the signed cert; however, receive-pack ignores push options within the cert, only handling push options outside the cert. Teach receive-pack, in the case that push options are provided for a signed push, to verify that the push options both within the cert and outside the cert are consistent. This sets in stone the requirement that send-pack redundantly send its push options in 2 places, but I think that this is better than the alternatives. Sending push options only within the cert is backwards-incompatible with existing Git servers (which read push options only from outside the cert), and sending push options only outside the cert means that the push options are not signed for. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano --- Documentation/technical/pack-protocol.txt | 32 +++++++++++--- builtin/receive-pack.c | 51 ++++++++++++++++++++++- t/t5534-push-signed.sh | 37 ++++++++++++++++ 3 files changed, 112 insertions(+), 8 deletions(-) diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt index c59ac9936a..d7358f748a 100644 --- a/Documentation/technical/pack-protocol.txt +++ b/Documentation/technical/pack-protocol.txt @@ -468,13 +468,10 @@ that it wants to update, it sends a line listing the obj-id currently on the server, the obj-id the client would like to update it to and the name of the reference. -This list is followed by a flush-pkt. Then the push options are transmitted -one per packet followed by another flush-pkt. After that the packfile that -should contain all the objects that the server will need to complete the new -references will be sent. +This list is followed by a flush-pkt. ---- - update-request = *shallow ( command-list | push-cert ) [packfile] + update-requests = *shallow ( command-list | push-cert ) shallow = PKT-LINE("shallow" SP obj-id) @@ -495,12 +492,35 @@ references will be sent. PKT-LINE("pusher" SP ident LF) PKT-LINE("pushee" SP url LF) PKT-LINE("nonce" SP nonce LF) + *PKT-LINE("push-option" SP push-option LF) PKT-LINE(LF) *PKT-LINE(command LF) *PKT-LINE(gpg-signature-lines LF) PKT-LINE("push-cert-end" LF) - packfile = "PACK" 28*(OCTET) + push-option = 1*( VCHAR | SP ) +---- + +If the server has advertised the 'push-options' capability and the client has +specified 'push-options' as part of the capability list above, the client then +sends its push options followed by a flush-pkt. + +---- + push-options = *PKT-LINE(push-option) flush-pkt +---- + +For backwards compatibility with older Git servers, if the client sends a push +cert and push options, it MUST send its push options both embedded within the +push cert and after the push cert. (Note that the push options within the cert +are prefixed, but the push options after the cert are not.) Both these lists +MUST be the same, modulo the prefix. + +After that the packfile that +should contain all the objects that the server will need to complete the new +references will be sent. + +---- + packfile = "PACK" 28*(OCTET) ---- If the receiving end does not support delete-refs, the sending end MUST diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index b618d52139..ed21142349 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -470,7 +470,8 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp) * after dropping "_commit" from its name and possibly moving it out * of commit.c */ -static char *find_header(const char *msg, size_t len, const char *key) +static char *find_header(const char *msg, size_t len, const char *key, + const char **next_line) { int key_len = strlen(key); const char *line = msg; @@ -483,6 +484,8 @@ static char *find_header(const char *msg, size_t len, const char *key) if (line + key_len < eol && !memcmp(line, key, key_len) && line[key_len] == ' ') { int offset = key_len + 1; + if (next_line) + *next_line = *eol ? eol + 1 : eol; return xmemdupz(line + offset, (eol - line) - offset); } line = *eol ? eol + 1 : NULL; @@ -492,7 +495,7 @@ static char *find_header(const char *msg, size_t len, const char *key) static const char *check_nonce(const char *buf, size_t len) { - char *nonce = find_header(buf, len, "nonce"); + char *nonce = find_header(buf, len, "nonce", NULL); unsigned long stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; @@ -572,6 +575,45 @@ leave: return retval; } +/* + * Return 1 if there is no push_cert or if the push options in push_cert are + * the same as those in the argument; 0 otherwise. + */ +static int check_cert_push_options(const struct string_list *push_options) +{ + const char *buf = push_cert.buf; + int len = push_cert.len; + + char *option; + const char *next_line; + int options_seen = 0; + + int retval = 1; + + if (!len) + return 1; + + while ((option = find_header(buf, len, "push-option", &next_line))) { + len -= (next_line - buf); + buf = next_line; + options_seen++; + if (options_seen > push_options->nr + || strcmp(option, + push_options->items[options_seen - 1].string)) { + retval = 0; + goto leave; + } + free(option); + } + + if (options_seen != push_options->nr) + retval = 0; + +leave: + free(option); + return retval; +} + static void prepare_push_cert_sha1(struct child_process *proc) { static int already_done; @@ -1924,6 +1966,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (use_push_options) read_push_options(&push_options); + if (!check_cert_push_options(&push_options)) { + struct command *cmd; + for (cmd = commands; cmd; cmd = cmd->next) + cmd->error_string = "inconsistent push options"; + } prepare_shallow_info(&si, &shallow); if (!si.nr_ours && !si.nr_theirs) diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh index ecb8d446a5..5bcb288f5c 100755 --- a/t/t5534-push-signed.sh +++ b/t/t5534-push-signed.sh @@ -124,6 +124,43 @@ test_expect_success GPG 'signed push sends push certificate' ' test_cmp expect dst/push-cert-status ' +test_expect_success GPG 'inconsistent push options in signed push not allowed' ' + # First, invoke receive-pack with dummy input to obtain its preamble. + prepare_dst && + git -C dst config receive.certnonceseed sekrit && + git -C dst config receive.advertisepushoptions 1 && + printf xxxx | test_might_fail git receive-pack dst >preamble && + + # Then, invoke push. Simulate a receive-pack that sends the preamble we + # obtained, followed by a dummy packet. + write_script myscript <<-\EOF && + cat preamble && + printf xxxx && + cat >push + EOF + test_might_fail git push --push-option="foo" --push-option="bar" \ + --receive-pack="\"$(pwd)/myscript\"" --signed dst --delete ff && + + # Replay the push output on a fresh dst, checking that ff is truly + # deleted. + prepare_dst && + git -C dst config receive.certnonceseed sekrit && + git -C dst config receive.advertisepushoptions 1 && + git receive-pack dst push.tweak && + prepare_dst && + git -C dst config receive.certnonceseed sekrit && + git -C dst config receive.advertisepushoptions 1 && + git receive-pack dst out && + git -C dst rev-parse ff && + grep "inconsistent push options" out +' + test_expect_success GPG 'fail without key and heed user.signingkey' ' prepare_dst && mkdir -p dst/.git/hooks && From 00ddc9d13cadb66d51e72107109baef1b3ca7c70 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 May 2017 14:53:21 +0200 Subject: [PATCH 190/566] Fix build with core.autocrlf=true On Windows, the default line endings are denoted by a Carriage Return byte followed by a Line Feed byte, while Linux and MacOSX use a single Line Feed byte to denote a line ending. To help with this situation, Git introduced several mechanisms over the last decade, most prominently the `core.autocrlf` setting. Sometimes, however, a single setting is incorrect, e.g. when certain files in the source code are to be consumed by software that can handle only LF line endings, while other files can use whatever is appropriate for the current platform. To allow for that, Git added the `eol` option to its .gitattributes handling, expecting every user of Git to mark their source code appropriately. Bash assumes that line-endings of scripts are denoted by a single Line Feed byte. Therefore, shell scripts in Git's source code are one example where that `eol=lf` option is *required*. When generating common-cmds.h, the Unix tools we use generally operate on the assumption that input and output deliminate their lines using LF-only line endings. Consequently, they would happily copy the CR byte verbatim into the strings in common-cmds.h, which in turn makes the C preprocessor barf (that interprets them as MacOS-style line endings). Therefore, we have to mark the input files as LF-only: command-list.txt and Documentation/git-*.txt. Quite a bit belatedly, this patch brings Git's own source code in line with those expectations by setting those attributes to allow for a correct build even when core.autocrlf=true. This patch can be validated even on Linux, by using this cadence: git config core.autocrlf true rm .git/index && git stash make -j15 DEVELOPER=1 Signed-off-by: Johannes Schindelin Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- .gitattributes | 8 +++++++- git-gui/.gitattributes | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 320e33c327..8ce9c6b888 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,9 @@ * whitespace=!indent,trail,space *.[ch] whitespace=indent,trail,space diff=cpp -*.sh whitespace=indent,trail,space +*.sh whitespace=indent,trail,space eol=lf +*.perl eol=lf +*.pm eol=lf +/Documentation/git-*.txt eol=lf +/command-list.txt eol=lf +/GIT-VERSION-GEN eol=lf +/mergetools/* eol=lf diff --git a/git-gui/.gitattributes b/git-gui/.gitattributes index 33d07c06bd..59cd41dbff 100644 --- a/git-gui/.gitattributes +++ b/git-gui/.gitattributes @@ -2,3 +2,4 @@ * encoding=US-ASCII git-gui.sh encoding=UTF-8 /po/*.po encoding=UTF-8 +/GIT-VERSION-GEN eol=lf From bee286f8f7c2e08a9c7fef28136796f6fd99a8ab Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 May 2017 14:53:25 +0200 Subject: [PATCH 191/566] git-new-workdir: mark script as LF-only Bash does not handle scripts with CR/LF line endings correctly, therefore they *have* to be forced to LF-only line endings. Funnily enough, this fixes t3000-ls-files-others and t1021-rerere-in-workdir when git.git was checked out with core.autocrlf=true, as these test still use git-new-workdir (once `git worktree` is no longer marked as experimental, both scripts probably want to be ported to using that command instead). Signed-off-by: Johannes Schindelin Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/workdir/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 contrib/workdir/.gitattributes diff --git a/contrib/workdir/.gitattributes b/contrib/workdir/.gitattributes new file mode 100644 index 0000000000..1f78c5d1bd --- /dev/null +++ b/contrib/workdir/.gitattributes @@ -0,0 +1 @@ +/git-new-workdir eol=lf From cedf4e27de019915a0386cb8f9a4721f2c26e7f7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 May 2017 14:54:20 +0200 Subject: [PATCH 192/566] completion: mark bash script as LF-only Without this change, the completion script does not work, as Bash expects its scripts to have line feeds as end-of-line markers (this is particularly prominent in quoted multi-line strings, where carriage returns would slip into the strings as verbatim characters otherwise). This change is required to let t9902-completion pass when Git's source code is checked out with `core.autocrlf = true`. Signed-off-by: Johannes Schindelin Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/completion/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 contrib/completion/.gitattributes diff --git a/contrib/completion/.gitattributes b/contrib/completion/.gitattributes new file mode 100644 index 0000000000..19116944c1 --- /dev/null +++ b/contrib/completion/.gitattributes @@ -0,0 +1 @@ +*.bash eol=lf From 8a7f8bedf361f6af761863ee4ea6206f1cf3bc19 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 May 2017 14:54:24 +0200 Subject: [PATCH 193/566] t3901: move supporting files into t/t3901/ The current convention is to either generate files on the fly in tests, or to use supporting files taken from a t/tNNNN/ directory (where NNNN matches the test's number, or the number of the test from which we borrow supporting files). The test t3901-i18n-patch.sh was obviously introduced before that convention was in full swing, hence its supporting files still lived in t/t3901-8859-1.txt and t/t3901-utf8.txt, respectively. Let's adjust to the current convention. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t0203-gettext-setlocale-sanity.sh | 4 +-- t/t3901-i18n-patch.sh | 38 ++++++++++++------------ t/{t3901-8859-1.txt => t3901/8859-1.txt} | 0 t/{t3901-utf8.txt => t3901/utf8.txt} | 0 t/t9350-fast-export.sh | 2 +- t/t9500-gitweb-standalone-no-errors.sh | 4 +-- 6 files changed, 24 insertions(+), 24 deletions(-) rename t/{t3901-8859-1.txt => t3901/8859-1.txt} (100%) rename t/{t3901-utf8.txt => t3901/utf8.txt} (100%) diff --git a/t/t0203-gettext-setlocale-sanity.sh b/t/t0203-gettext-setlocale-sanity.sh index a212460081..71b0d74b4d 100755 --- a/t/t0203-gettext-setlocale-sanity.sh +++ b/t/t0203-gettext-setlocale-sanity.sh @@ -8,7 +8,7 @@ test_description="The Git C functions aren't broken by setlocale(3)" . ./lib-gettext.sh test_expect_success 'git show a ISO-8859-1 commit under C locale' ' - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && test_commit "iso-c-commit" iso-under-c && git show >out 2>err && ! test -s err && @@ -16,7 +16,7 @@ test_expect_success 'git show a ISO-8859-1 commit under C locale' ' ' test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 locale' ' - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && test_commit "iso-utf8-commit" iso-under-utf8 && LANGUAGE=is LC_ALL="$is_IS_locale" git show >out 2>err && ! test -s err && diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index f663d567c8..923eb01f0e 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh @@ -31,7 +31,7 @@ test_expect_success setup ' # use UTF-8 in author and committer name to match the # i18n.commitencoding settings - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && test_tick && echo "$GIT_AUTHOR_NAME" >mine && @@ -55,7 +55,7 @@ test_expect_success setup ' # the second one on the side branch is ISO-8859-1 git config i18n.commitencoding ISO8859-1 && # use author and committer name in ISO-8859-1 to match it. - . "$TEST_DIRECTORY"/t3901-8859-1.txt + . "$TEST_DIRECTORY"/t3901/8859-1.txt fi && test_tick && echo Yet another >theirs && @@ -100,7 +100,7 @@ test_expect_success 'rebase (U/U)' ' # The result will be committed by GIT_COMMITTER_NAME -- # we want UTF-8 encoded name. - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git checkout -b test && git rebase master && @@ -110,7 +110,7 @@ test_expect_success 'rebase (U/U)' ' test_expect_success 'rebase (U/L)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard side && git rebase master && @@ -122,7 +122,7 @@ test_expect_success !MINGW 'rebase (L/L)' ' # In this test we want ISO-8859-1 encoded commits as the result git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard side && git rebase master && @@ -135,7 +135,7 @@ test_expect_success !MINGW 'rebase (L/U)' ' # to get ISO-8859-1 results. git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard side && git rebase master && @@ -148,7 +148,7 @@ test_expect_success 'cherry-pick(U/U)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard master && git cherry-pick side^ && @@ -163,7 +163,7 @@ test_expect_success !MINGW 'cherry-pick(L/L)' ' git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard master && git cherry-pick side^ && @@ -178,7 +178,7 @@ test_expect_success 'cherry-pick(U/L)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard master && git cherry-pick side^ && @@ -194,7 +194,7 @@ test_expect_success !MINGW 'cherry-pick(L/U)' ' git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard master && git cherry-pick side^ && @@ -207,7 +207,7 @@ test_expect_success !MINGW 'cherry-pick(L/U)' ' test_expect_success 'rebase --merge (U/U)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard side && git rebase --merge master && @@ -218,7 +218,7 @@ test_expect_success 'rebase --merge (U/U)' ' test_expect_success 'rebase --merge (U/L)' ' git config i18n.commitencoding UTF-8 && git config i18n.logoutputencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard side && git rebase --merge master && @@ -230,7 +230,7 @@ test_expect_success 'rebase --merge (L/L)' ' # In this test we want ISO-8859-1 encoded commits as the result git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard side && git rebase --merge master && @@ -243,7 +243,7 @@ test_expect_success 'rebase --merge (L/U)' ' # to get ISO-8859-1 results. git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard side && git rebase --merge master && @@ -254,7 +254,7 @@ test_expect_success 'rebase --merge (L/U)' ' test_expect_success 'am (U/U)' ' # Apply UTF-8 patches with UTF-8 commitencoding git config i18n.commitencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard master && git am out-u1 out-u2 && @@ -265,7 +265,7 @@ test_expect_success 'am (U/U)' ' test_expect_success !MINGW 'am (L/L)' ' # Apply ISO-8859-1 patches with ISO-8859-1 commitencoding git config i18n.commitencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard master && git am out-l1 out-l2 && @@ -276,7 +276,7 @@ test_expect_success !MINGW 'am (L/L)' ' test_expect_success 'am (U/L)' ' # Apply ISO-8859-1 patches with UTF-8 commitencoding git config i18n.commitencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard master && # am specifies --utf8 by default. @@ -288,7 +288,7 @@ test_expect_success 'am (U/L)' ' test_expect_success 'am --no-utf8 (U/L)' ' # Apply ISO-8859-1 patches with UTF-8 commitencoding git config i18n.commitencoding UTF-8 && - . "$TEST_DIRECTORY"/t3901-utf8.txt && + . "$TEST_DIRECTORY"/t3901/utf8.txt && git reset --hard master && git am --no-utf8 out-l1 out-l2 2>err && @@ -303,7 +303,7 @@ test_expect_success 'am --no-utf8 (U/L)' ' test_expect_success !MINGW 'am (L/U)' ' # Apply UTF-8 patches with ISO-8859-1 commitencoding git config i18n.commitencoding ISO8859-1 && - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && git reset --hard master && # mailinfo will re-code the commit message to the charset specified by diff --git a/t/t3901-8859-1.txt b/t/t3901/8859-1.txt similarity index 100% rename from t/t3901-8859-1.txt rename to t/t3901/8859-1.txt diff --git a/t/t3901-utf8.txt b/t/t3901/utf8.txt similarity index 100% rename from t/t3901-utf8.txt rename to t/t3901/utf8.txt diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index b5149fde6e..8dcb05c4a5 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -70,7 +70,7 @@ test_expect_success 'iso-8859-1' ' git config i18n.commitencoding ISO8859-1 && # use author and committer name in ISO-8859-1 to match it. - . "$TEST_DIRECTORY"/t3901-8859-1.txt && + . "$TEST_DIRECTORY"/t3901/8859-1.txt && test_tick && echo rosten >file && git commit -s -m den file && diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 6d06ed96cb..cc8d463e01 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -519,7 +519,7 @@ test_expect_success \ test_expect_success \ 'encode(commit): utf8' \ - '. "$TEST_DIRECTORY"/t3901-utf8.txt && + '. "$TEST_DIRECTORY"/t3901/utf8.txt && test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" && test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" && echo "UTF-8" >> file && @@ -529,7 +529,7 @@ test_expect_success \ test_expect_success \ 'encode(commit): iso-8859-1' \ - '. "$TEST_DIRECTORY"/t3901-8859-1.txt && + '. "$TEST_DIRECTORY"/t3901/8859-1.txt && test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" && test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" && echo "ISO-8859-1" >> file && From 614f4f0f350aef57473b0311b0a36b13e91e1310 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 May 2017 14:54:27 +0200 Subject: [PATCH 194/566] Fix the remaining tests that failed with core.autocrlf=true The test suite is mainly developed on Linux and MacOSX, which is the reason that nobody thought to mark files as LF-only as needed. The symptom is a test suite that fails left and right when being checked out using Git for Windows (which defaults to core.autocrlf=true). Mostly, the problems stem from Git's (LF-only) output being compared to hard-coded files that are checked out with line endings according to core.autocrlf (which is of course incorrect). This includes the two test files in t/diff-lib/, README and COPYING. This patch can be validated even on Linux by using this cadence: git config core.autocrlf true rm .git/index && git stash make -j15 DEVELOPER=1 test Signed-off-by: Johannes Schindelin Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/.gitattributes | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/t/.gitattributes b/t/.gitattributes index 2d44088f56..11e5fe3728 100644 --- a/t/.gitattributes +++ b/t/.gitattributes @@ -1,2 +1,21 @@ t[0-9][0-9][0-9][0-9]/* -whitespace -t0110/url-* binary +/diff-lib/* eol=lf +/t0110/url-* binary +/t3900/*.txt eol=lf +/t3901/*.txt eol=lf +/t4034/*/* eol=lf +/t4013/* eol=lf +/t4018/* eol=lf +/t4100/* eol=lf +/t4101/* eol=lf +/t4109/* eol=lf +/t4110/* eol=lf +/t4135/* eol=lf +/t4211/* eol=lf +/t4252/* eol=lf +/t5100/* eol=lf +/t5515/* eol=lf +/t556x_common eol=lf +/t7500/* eol=lf +/t8005/*.txt eol=lf +/t9*/*.dump eol=lf From 2779f665054c3ba85c1ee298929e640149d84b1e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 9 May 2017 14:54:32 +0200 Subject: [PATCH 195/566] t4051: mark supporting files as requiring LF-only line endings The test t4051-diff-function-context.sh passes on Linux when core.autocrlf=true even without marking its support files as LF-only, but they fail when core.autocrlf=true in Git for Windows' SDK. The reason is that `grep ... >file.c.new` will keep CR/LF line endings on Linux (obviously treating CRs as if they were regular characters), but will be converted to LF-only line endings with MSYS2's grep that is used in Git for Windows. As we do not want to validate the way the available `grep` works, let's just mark the input as LF-only and move on. Signed-off-by: Johannes Schindelin Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/.gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/t/.gitattributes b/t/.gitattributes index 11e5fe3728..3bd959ae52 100644 --- a/t/.gitattributes +++ b/t/.gitattributes @@ -6,6 +6,7 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /t4034/*/* eol=lf /t4013/* eol=lf /t4018/* eol=lf +/t4051/* eol=lf /t4100/* eol=lf /t4101/* eol=lf /t4109/* eol=lf From 2c3b40799fcdb9d2dedc90134e4f3841af08bbe7 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 9 May 2017 12:17:58 -0700 Subject: [PATCH 196/566] pathspec: provide a more descriptive die message The current message displayed upon an internal error in 'init_pathspec_item()' isn't very descriptive and doesn't provide much context to where the error occurred. Update the error message to provide more context to where the error occured. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- pathspec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pathspec.c b/pathspec.c index 50f76fff45..904cfb7393 100644 --- a/pathspec.c +++ b/pathspec.c @@ -555,7 +555,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, * would trigger that. */ die_inside_submodule_path(item); - die ("BUG: item->nowildcard_len > item->len || item->prefix > item->len)"); + die ("BUG: error initializing pathspec_item"); } } From bdab972153a73815e04e9699406433e409ed28ab Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 9 May 2017 12:17:59 -0700 Subject: [PATCH 197/566] submodule: add die_in_unpopulated_submodule function Currently 'git add' is the only command which dies when launched from an unpopulated submodule (the place-holder directory for a submodule which hasn't been checked out). This is triggered implicitly by passing the PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE flag to 'parse_pathspec()'. Instead make this desire more explicit by creating a function 'die_in_unpopulated_submodule()' which dies if the provided 'prefix' has a leading path component which matches a submodule in the the index. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/add.c | 3 +++ pathspec.c | 29 ----------------------------- submodule.c | 30 ++++++++++++++++++++++++++++++ submodule.h | 2 ++ t/t6134-pathspec-in-submodule.sh | 6 +----- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index 9f53f020d0..ec58e36796 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -17,6 +17,7 @@ #include "revision.h" #include "bulk-checkin.h" #include "argv-array.h" +#include "submodule.h" static const char * const builtin_add_usage[] = { N_("git add [] [--] ..."), @@ -379,6 +380,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die(_("index file corrupt")); + die_in_unpopulated_submodule(&the_index, prefix); + /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. diff --git a/pathspec.c b/pathspec.c index 904cfb7393..9b7634c5aa 100644 --- a/pathspec.c +++ b/pathspec.c @@ -424,27 +424,6 @@ static void strip_submodule_slash_expensive(struct pathspec_item *item) } } -static void die_inside_submodule_path(struct pathspec_item *item) -{ - int i; - - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - int ce_len = ce_namelen(ce); - - if (!S_ISGITLINK(ce->ce_mode)) - continue; - - if (item->len < ce_len || - !(item->match[ce_len] == '/' || item->match[ce_len] == '\0') || - memcmp(ce->name, item->match, ce_len)) - continue; - - die(_("Pathspec '%s' is in submodule '%.*s'"), - item->original, ce_len, ce->name); - } -} - /* * Perform the initialization of a pathspec_item based on a pathspec element. */ @@ -547,14 +526,6 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, /* sanity checks, pathspec matchers assume these are sane */ if (item->nowildcard_len > item->len || item->prefix > item->len) { - /* - * This case can be triggered by the user pointing us to a - * pathspec inside a submodule, which is an input error. - * Detect that here and complain, but fallback in the - * non-submodule case to a BUG, as we have no idea what - * would trigger that. - */ - die_inside_submodule_path(item); die ("BUG: error initializing pathspec_item"); } } diff --git a/submodule.c b/submodule.c index d3299e29c0..885663c421 100644 --- a/submodule.c +++ b/submodule.c @@ -282,6 +282,36 @@ int is_submodule_populated_gently(const char *path, int *return_error_code) return ret; } +/* + * Dies if the provided 'prefix' corresponds to an unpopulated submodule + */ +void die_in_unpopulated_submodule(const struct index_state *istate, + const char *prefix) +{ + int i, prefixlen; + + if (!prefix) + return; + + prefixlen = strlen(prefix); + + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + int ce_len = ce_namelen(ce); + + if (!S_ISGITLINK(ce->ce_mode)) + continue; + if (prefixlen <= ce_len) + continue; + if (strncmp(ce->name, prefix, ce_len)) + continue; + if (prefix[ce_len] != '/') + continue; + + die(_("in unpopulated submodule '%s'"), ce->name); + } +} + int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst) { diff --git a/submodule.h b/submodule.h index 1277480add..d11b4da40f 100644 --- a/submodule.h +++ b/submodule.h @@ -49,6 +49,8 @@ extern int is_submodule_initialized(const char *path); * Otherwise the return error code is the same as of resolve_gitdir_gently. */ extern int is_submodule_populated_gently(const char *path, int *return_error_code); +extern void die_in_unpopulated_submodule(const struct index_state *istate, + const char *prefix); extern int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst); extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s); diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh index fd401ca605..0f1cb49ced 100755 --- a/t/t6134-pathspec-in-submodule.sh +++ b/t/t6134-pathspec-in-submodule.sh @@ -24,13 +24,9 @@ test_expect_success 'error message for path inside submodule' ' test_cmp expect actual ' -cat <expect -fatal: Pathspec '.' is in submodule 'sub' -EOF - test_expect_success 'error message for path inside submodule from within submodule' ' test_must_fail git -C sub add . 2>actual && - test_cmp expect actual + test_i18ngrep "in unpopulated submodule" actual ' test_done From c8f7c8b7042ba51f9bfd24aba7aaab4007365380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Wed, 10 May 2017 16:06:19 +0200 Subject: [PATCH 198/566] t0027: tests are not expensive; remove t0025 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The purpose of t0027 is to test all CRLF related conversions at "git checkout" and "git add". Running t0027 under Git for Windows takes 3-4 minutes, so the whole script had been marked as "EXPENSIVE". However, the "Git for Windows" fork overrides this since 2014: "t0027 is marked expensive, but really, for MinGW we want to run these tests always." The test seems not to be expensive on other platforms at all: it takes less than 14 seconds under Linux, and 63 seconds under Mac Os X, and this is more or less the same with a SSD or a spinning disk. So let's drop the "EXPENSIVE" prereq. While at it, retire t0025; recent "stress" tests show that t0025 is flaky, reported by Lars Schneider , but all tests in t0025 are covered by t0027 already. Signed-off-by: Torsten Bögershausen Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t0025-crlf-auto.sh | 181 ------------------------------------------- t/t0027-auto-crlf.sh | 6 -- 2 files changed, 187 deletions(-) delete mode 100755 t/t0025-crlf-auto.sh diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh deleted file mode 100755 index 89826c568b..0000000000 --- a/t/t0025-crlf-auto.sh +++ /dev/null @@ -1,181 +0,0 @@ -#!/bin/sh - -test_description='CRLF conversion' - -. ./test-lib.sh - -has_cr() { - tr '\015' Q <"$1" | grep Q >/dev/null -} - -test_expect_success setup ' - - git config core.autocrlf false && - - for w in Hello world how are you; do echo $w; done >LFonly && - for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >CRLFonly && - for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >LFwithNUL && - git add . && - - git commit -m initial && - - LFonly=$(git rev-parse HEAD:LFonly) && - CRLFonly=$(git rev-parse HEAD:CRLFonly) && - LFwithNUL=$(git rev-parse HEAD:LFwithNUL) && - - echo happy. -' - -test_expect_success 'default settings cause no changes' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git read-tree --reset -u HEAD && - - ! has_cr LFonly && - has_cr CRLFonly && - LFonlydiff=$(git diff LFonly) && - CRLFonlydiff=$(git diff CRLFonly) && - LFwithNULdiff=$(git diff LFwithNUL) && - test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff" -' - -test_expect_success 'crlf=true causes a CRLF file to be normalized' ' - - # Backwards compatibility check - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - echo "CRLFonly crlf" > .gitattributes && - git read-tree --reset -u HEAD && - - # Note, "normalized" means that git will normalize it if added - has_cr CRLFonly && - CRLFonlydiff=$(git diff CRLFonly) && - test -n "$CRLFonlydiff" -' - -test_expect_success 'text=true causes a CRLF file to be normalized' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - echo "CRLFonly text" > .gitattributes && - git read-tree --reset -u HEAD && - - # Note, "normalized" means that git will normalize it if added - has_cr CRLFonly && - CRLFonlydiff=$(git diff CRLFonly) && - test -n "$CRLFonlydiff" -' - -test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git config core.autocrlf false && - echo "LFonly eol=crlf" > .gitattributes && - git read-tree --reset -u HEAD && - - has_cr LFonly && - LFonlydiff=$(git diff LFonly) && - test -z "$LFonlydiff" -' - -test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git config core.autocrlf input && - echo "LFonly eol=crlf" > .gitattributes && - git read-tree --reset -u HEAD && - - has_cr LFonly && - LFonlydiff=$(git diff LFonly) && - test -z "$LFonlydiff" -' - -test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git config core.autocrlf true && - echo "LFonly eol=lf" > .gitattributes && - git read-tree --reset -u HEAD && - - ! has_cr LFonly && - LFonlydiff=$(git diff LFonly) && - test -z "$LFonlydiff" -' - -test_expect_success 'autocrlf=true does not normalize CRLF files' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git config core.autocrlf true && - git read-tree --reset -u HEAD && - - has_cr LFonly && - has_cr CRLFonly && - LFonlydiff=$(git diff LFonly) && - CRLFonlydiff=$(git diff CRLFonly) && - LFwithNULdiff=$(git diff LFwithNUL) && - test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff" -' - -test_expect_success 'text=auto, autocrlf=true does not normalize CRLF files' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git config core.autocrlf true && - echo "* text=auto" > .gitattributes && - git read-tree --reset -u HEAD && - - has_cr LFonly && - has_cr CRLFonly && - LFonlydiff=$(git diff LFonly) && - CRLFonlydiff=$(git diff CRLFonly) && - LFwithNULdiff=$(git diff LFwithNUL) && - test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff" -' - -test_expect_success 'text=auto, autocrlf=true does not normalize binary files' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - git config core.autocrlf true && - echo "* text=auto" > .gitattributes && - git read-tree --reset -u HEAD && - - ! has_cr LFwithNUL && - LFwithNULdiff=$(git diff LFwithNUL) && - test -z "$LFwithNULdiff" -' - -test_expect_success 'eol=crlf _does_ normalize binary files' ' - - rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL && - echo "LFwithNUL eol=crlf" > .gitattributes && - git read-tree --reset -u HEAD && - - has_cr LFwithNUL && - LFwithNULdiff=$(git diff LFwithNUL) && - test -z "$LFwithNULdiff" -' - -test_expect_success 'prepare unnormalized' ' - > .gitattributes && - git config core.autocrlf false && - printf "LINEONE\nLINETWO\r\n" >mixed && - git add mixed .gitattributes && - git commit -m "Add mixed" && - git ls-files --eol | egrep "i/crlf" && - git ls-files --eol | egrep "i/mixed" -' - -test_expect_success 'normalize unnormalized' ' - echo "* text=auto" >.gitattributes && - rm .git/index && - git add . && - git commit -m "Introduce end-of-line normalization" && - git ls-files --eol | tr "\\t" " " | sort >act && -cat >exp <"$1".expect && tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual && From b9b10d36816b8c7276bb83eb08fdbb59f68ef99b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 10 May 2017 21:31:54 -0700 Subject: [PATCH 199/566] read-tree: "read-tree -m --empty" does not make sense fb1bb965 ("read-tree: deprecate syntax without tree-ish args", 2010-09-10) wanted to deprecate "git read-tree" without any tree, which used to be the way to empty the index, and encourage use of "git read-tree --empty" instead. However, when used with "-m", "--empty" does not make any sense, either, simply because merging 0 trees will result in a different error anyway. Omit the deprecation warning and let the code to emit real error message diagnose the error. Signed-off-by: Junio C Hamano --- builtin/read-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 8c693e7568..ce5265dde2 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -181,7 +181,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) die("failed to unpack tree object %s", arg); stage++; } - if (nr_trees == 0 && !read_empty) + if (!nr_trees && !read_empty && !opts.merge) warning("read-tree: emptying the index with no arguments is deprecated; use --empty"); else if (nr_trees > 0 && read_empty) die("passing trees as arguments contradicts --empty"); From 0d75bfe67bfd7538c0188f862fb4770f09620b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Fri, 5 May 2017 18:19:32 +0000 Subject: [PATCH 200/566] tests: fix tests broken under GETTEXT_POISON=YesPlease MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GETTEXT_POISON=YesPlease compile-time testing option added in my bb946bba76 ("i18n: add GETTEXT_POISON to simulate unfriendly translator", 2011-02-22) has been slowly bitrotting as strings have been marked for translation, and new tests have been added without running it. I brought this up on the list ("[BUG] test suite broken with GETTEXT_POISON=YesPlease", [1]) asking whether this mode was useful at all anymore. At least one person occasionally uses it, and Lars Schneider offered to change one of the the Travis builds to run in this mode, so fix up the failing ones. My test setup runs most of the tests, with the notable exception of skipping all the p4 tests, so it's possible that there's still some lurking regressions I haven't fixed. 1. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t0027-auto-crlf.sh | 2 +- t/t1309-early-config.sh | 2 +- t/t1430-bad-ref-name.sh | 2 +- t/t3203-branch-output.sh | 2 +- t/t3404-rebase-interactive.sh | 14 +++++++------- t/t3415-rebase-autosquash.sh | 10 +++++----- t/t3903-stash.sh | 4 ++-- t/t4205-log-pretty-formats.sh | 4 ++-- t/t5316-pack-delta-depth.sh | 8 ++++++-- t/t6134-pathspec-in-submodule.sh | 4 ++-- t/t7004-tag.sh | 4 ++-- t/t7406-submodule-update.sh | 2 +- t/t7508-status.sh | 6 +++--- t/t7509-commit.sh | 4 ++-- t/t7800-difftool.sh | 4 ++-- 15 files changed, 38 insertions(+), 34 deletions(-) diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh index 90db54c9f9..81609af7bb 100755 --- a/t/t0027-auto-crlf.sh +++ b/t/t0027-auto-crlf.sh @@ -75,7 +75,7 @@ check_warning () { *) echo >&2 "Illegal 1": "$1" ; return false ;; esac grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq >"$2".actual - test_cmp "$2".expect "$2".actual + test_i18ncmp "$2".expect "$2".actual } commit_check_warn () { diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh index 1af8c454cf..3dda215e8e 100755 --- a/t/t1309-early-config.sh +++ b/t/t1309-early-config.sh @@ -77,7 +77,7 @@ test_with_config () { test_expect_success 'ignore .git/ with incompatible repository version' ' test_with_config "[core]repositoryformatversion = 999999" 2>err && - grep "warning:.* Expected git repo version <= [1-9]" err + test_i18ngrep "warning:.* Expected git repo version <= [1-9]" err ' test_expect_failure 'ignore .git/ with invalid repository version' ' diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index 8937e25e49..e88349c8a0 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -122,7 +122,7 @@ test_expect_success 'push cannot create a badly named ref' ' ! grep -e "broken\.\.\.ref" output ' -test_expect_failure 'push --mirror can delete badly named ref' ' +test_expect_failure C_LOCALE_OUTPUT 'push --mirror can delete badly named ref' ' top=$(pwd) && git init src && git init dest && diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 5778c0afe1..a428ae6703 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -236,7 +236,7 @@ test_expect_success 'git branch --format option' ' Refname is refs/heads/ref-to-remote EOF git branch --format="Refname is %(refname)" >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 33d392ba11..5bd0275930 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -366,7 +366,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' ' grep "^ file1 | 2 +-$" output ' -test_expect_success 'multi-squash only fires up editor once' ' +test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' ' base=$(git rev-parse HEAD~4) && set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \ @@ -376,7 +376,7 @@ test_expect_success 'multi-squash only fires up editor once' ' test 1 = $(git show | grep ONCE | wc -l) ' -test_expect_success 'multi-fixup does not fire up editor' ' +test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' ' git checkout -b multi-fixup E && base=$(git rev-parse HEAD~4) && set_fake_editor && @@ -426,7 +426,7 @@ D ONCE EOF -test_expect_success 'squash and fixup generate correct log messages' ' +test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' ' git checkout -b squash-fixup E && base=$(git rev-parse HEAD~4) && set_fake_editor && @@ -439,7 +439,7 @@ test_expect_success 'squash and fixup generate correct log messages' ' git branch -D squash-fixup ' -test_expect_success 'squash ignores comments' ' +test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' ' git checkout -b skip-comments E && base=$(git rev-parse HEAD~4) && set_fake_editor && @@ -452,7 +452,7 @@ test_expect_success 'squash ignores comments' ' git branch -D skip-comments ' -test_expect_success 'squash ignores blank lines' ' +test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' ' git checkout -b skip-blank-lines E && base=$(git rev-parse HEAD~4) && set_fake_editor && @@ -860,7 +860,7 @@ test_expect_success 'rebase -ix with several instances of --exec' ' test_cmp expected actual ' -test_expect_success 'rebase -ix with --autosquash' ' +test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' ' git reset --hard execute && git checkout -b autosquash && echo second >second.txt && @@ -943,7 +943,7 @@ test_expect_success 'rebase -i --root fixup root commit' ' test 0 = $(git cat-file commit HEAD | grep -c ^parent\ ) ' -test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' ' +test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' ' git reset --hard && git checkout conflict-branch && set_fake_editor && diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index 48346f1cc0..5848949ec3 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh @@ -234,23 +234,23 @@ test_auto_fixup_fixup () { fi } -test_expect_success 'fixup! fixup!' ' +test_expect_success C_LOCALE_OUTPUT 'fixup! fixup!' ' test_auto_fixup_fixup fixup fixup ' -test_expect_success 'fixup! squash!' ' +test_expect_success C_LOCALE_OUTPUT 'fixup! squash!' ' test_auto_fixup_fixup fixup squash ' -test_expect_success 'squash! squash!' ' +test_expect_success C_LOCALE_OUTPUT 'squash! squash!' ' test_auto_fixup_fixup squash squash ' -test_expect_success 'squash! fixup!' ' +test_expect_success C_LOCALE_OUTPUT 'squash! fixup!' ' test_auto_fixup_fixup squash fixup ' -test_expect_success 'autosquash with custom inst format' ' +test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' ' git reset --hard base && git config --add rebase.instructionFormat "[%an @ %ar] %s" && echo 2 >file1 && diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index b71d1e659e..3b4bed5c9a 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -865,7 +865,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' ' git stash push -p foo >actual && echo "No local changes to save" >expect && git reset --hard HEAD~ && - test_cmp expect actual + test_i18ncmp expect actual ' test_expect_success 'stash push with pathspec shows no changes when there are none' ' @@ -875,7 +875,7 @@ test_expect_success 'stash push with pathspec shows no changes when there are no git stash push foo >actual && echo "No local changes to save" >expect && git reset --hard HEAD~ && - test_cmp expect actual + test_i18ncmp expect actual ' test_expect_success 'stash push with pathspec not in the repository errors out' ' diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 21eb8c8587..18aa1b5889 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -126,12 +126,12 @@ test_expect_success 'NUL separation with --stat' ' test_i18ncmp expected actual ' -test_expect_failure 'NUL termination with --stat' ' +test_expect_failure C_LOCALE_OUTPUT 'NUL termination with --stat' ' stat0_part=$(git diff --stat HEAD^ HEAD) && stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) && printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected && git log -z --stat --pretty="tformat:%s" >actual && - test_i18ncmp expected actual + test_cmp expected actual ' test_expect_success 'setup more commits' ' diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh index 37143ea0ac..2ed479b712 100755 --- a/t/t5316-pack-delta-depth.sh +++ b/t/t5316-pack-delta-depth.sh @@ -82,12 +82,16 @@ test_expect_success 'packing produces a long delta' ' # Use --window=0 to make sure we are seeing reused deltas, # not computing a new long chain. pack=$(git pack-objects --all --window=0 expect && + max_chain pack-$pack.pack >actual && + test_i18ncmp expect actual ' test_expect_success '--depth limits depth' ' pack=$(git pack-objects --all --depth=5 expect && + max_chain pack-$pack.pack >actual && + test_i18ncmp expect actual ' test_done diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh index fd401ca605..99a8982ab1 100755 --- a/t/t6134-pathspec-in-submodule.sh +++ b/t/t6134-pathspec-in-submodule.sh @@ -21,7 +21,7 @@ EOF test_expect_success 'error message for path inside submodule' ' echo a >sub/a && test_must_fail git add sub/a 2>actual && - test_cmp expect actual + test_i18ncmp expect actual ' cat <expect @@ -30,7 +30,7 @@ EOF test_expect_success 'error message for path inside submodule from within submodule' ' test_must_fail git -C sub add . 2>actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index bb2e4d704d..0ef7b94394 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -87,7 +87,7 @@ test_expect_success 'creating a tag with --create-reflog should create reflog' ' git tag --create-reflog tag_with_reflog && git reflog exists refs/tags/tag_with_reflog && sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual && - test_cmp expected actual + test_i18ncmp expected actual ' test_expect_success 'annotated tag with --create-reflog has correct message' ' @@ -98,7 +98,7 @@ test_expect_success 'annotated tag with --create-reflog has correct message' ' git tag -m "annotated tag" --create-reflog tag_with_reflog && git reflog exists refs/tags/tag_with_reflog && sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual && - test_cmp expected actual + test_i18ncmp expected actual ' test_expect_success '--create-reflog does not create reflog on failure' ' diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 4ac386d98b..034914a14f 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -447,7 +447,7 @@ test_expect_success 'submodule update - command run for initial population of su EOF rm -rf super/submodule && test_must_fail git -C super submodule update 2>actual && - test_cmp expect actual && + test_i18ncmp expect actual && git -C super submodule update --checkout ' diff --git a/t/t7508-status.sh b/t/t7508-status.sh index fb00e6d9b0..5edcc6edfe 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -360,7 +360,7 @@ EOF test_expect_success 'status -s -b' ' git status -s -b >output && - test_cmp expect output + test_i18ncmp expect output ' @@ -370,7 +370,7 @@ test_expect_success 'status -s -z -b' ' git status -s -z -b >output && nul_to_q output.q && mv output.q output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_success 'setup dir3' ' @@ -687,7 +687,7 @@ EOF test_expect_success 'status -s -b with color.status' ' git status -s -b | test_decode_color >output && - test_cmp expect output + test_i18ncmp expect output ' diff --git a/t/t7509-commit.sh b/t/t7509-commit.sh index db9774e345..ddef7ea6b0 100755 --- a/t/t7509-commit.sh +++ b/t/t7509-commit.sh @@ -101,7 +101,7 @@ test_expect_success '--amend option with empty author' ' echo "Empty author test" >>foo && test_tick && test_must_fail git commit -a -m "empty author" --amend 2>err && - grep "empty ident" err + test_i18ngrep "empty ident" err ' test_expect_success '--amend option with missing author' ' @@ -114,7 +114,7 @@ test_expect_success '--amend option with missing author' ' echo "Missing author test" >>foo && test_tick && test_must_fail git commit -a -m "malformed author" --amend 2>err && - grep "empty ident" err + test_i18ngrep "empty ident" err ' test_expect_success '--reset-author makes the commit ours even with --amend option' ' diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 7f09867478..668bbee73c 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -25,14 +25,14 @@ prompt_given () test_expect_success 'basic usage requires no repo' ' test_expect_code 129 git difftool -h >output && - grep ^usage: output && + test_i18ngrep ^usage: output && # create a ceiling directory to prevent Git from finding a repo mkdir -p not/repo && test_when_finished rm -r not && test_expect_code 129 \ env GIT_CEILING_DIRECTORIES="$(pwd)/not" \ git -C not/repo difftool -h >output && - grep ^usage: output + test_i18ngrep ^usage: output ' # Create a file on master and change it on branch From 3ae72a3acaac8b79653d67cf1aae94f61efd41c7 Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Fri, 5 May 2017 17:40:52 +0200 Subject: [PATCH 201/566] travis-ci: setup "prove cache" in "script" step The command that made the "prove cache" persistent across builds was executed in the "before_install" step. Consequently, every job that wanted to make use of the cache had to run this step. The "prove cache" is only used in the "script" step for the "make test" command. Therefore, we should configure the "prove cache" in this step. This change is useful for a subsequent patch that adds a job which does not need the "before_install" step but wants to run the "script" step to execute the tests. Signed-off-by: Lars Schneider Signed-off-by: Junio C Hamano --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1b32c98f2c..f96f09ce9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -134,12 +134,14 @@ before_install: p4 -V | grep Rev.; echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)"; git-lfs version; - mkdir -p $HOME/travis-cache; - ln -s $HOME/travis-cache/.prove t/.prove; before_script: make --jobs=2 -script: make --quiet test +script: + - > + mkdir -p $HOME/travis-cache; + ln -s $HOME/travis-cache/.prove t/.prove; + make --quiet test; after_failure: - > From b8e188f6f51fb016eba7eb8a76a109cdadbd4231 Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Fri, 5 May 2017 17:40:53 +0200 Subject: [PATCH 202/566] travis-ci: add job to run tests with GETTEXT_POISON Add a job to run Git tests with GETTEXT_POISON. In this job we don't run the git-p4, git-svn, and HTTPD tests to save resources/time (those tests are already executed in other jobs). Since we don't run these tests, we can also skip the "before_install" step (which would install the necessary dependencies) with an empty override. Signed-off-by: Lars Schneider Signed-off-by: Junio C Hamano --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index f96f09ce9d..6dd73d1f02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,11 @@ env: matrix: include: + - env: GETTEXT_POISON=YesPlease + os: linux + compiler: + addons: + before_install: - env: Windows os: linux compiler: From 9d71d94d34fb3d3bb206ab9d5dbe08eb529ca729 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 11 May 2017 05:10:47 -0400 Subject: [PATCH 203/566] docs/config: clarify include/includeIf relationship MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "includeIf" directives behave exactly like include ones, except they only kick in when the conditional is true. That was mentioned in the "conditional" section, but let's make it more clear for the whole "includes" section, since people don't necessarily read the documentation top to bottom. Signed-off-by: Jeff King Reviewed-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 475e874d51..7e7a358aea 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -79,14 +79,20 @@ escape sequences) are invalid. Includes ~~~~~~~~ +The `include` and `includeIf` sections allow you to include config +directives from another source. These sections behave identically to +each other with the exception that `includeIf` sections may be ignored +if their condition does not evaluate to true; see "Conditional includes" +below. + You can include a config file from another by setting the special -`include.path` variable to the name of the file to be included. The -variable takes a pathname as its value, and is subject to tilde -expansion. `include.path` can be given multiple times. +`include.path` (or `includeIf.*.path`) variable to the name of the file +to be included. The variable takes a pathname as its value, and is +subject to tilde expansion. These variables can be given multiple times. The included file is expanded immediately, as if its contents had been found at the location of the include directive. If the value of the -`include.path` variable is a relative path, the path is considered to +variable is a relative path, the path is considered to be relative to the configuration file in which the include directive was found. See below for examples. @@ -95,8 +101,7 @@ Conditional includes You can include a config file from another conditionally by setting a `includeIf..path` variable to the name of the file to be -included. The variable's value is treated the same way as -`include.path`. `includeIf..path` can be given multiple times. +included. The condition starts with a keyword followed by a colon and some data whose format and meaning depends on the keyword. Supported keywords From 994cd6c7ca2da9bf09582ba08cbad583cba76968 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 11 May 2017 05:11:06 -0400 Subject: [PATCH 204/566] docs/config: give a relative includeIf example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changes in the previous commit hopefully clarify that the evaluation of an include "path" variable is the same no matter if it's in a conditional section or not. But since this question came up on the list, let's add an example that makes it obvious. Signed-off-by: Jeff King Reviewed-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 7e7a358aea..cc6d79e537 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -187,6 +187,12 @@ Example [includeIf "gitdir:~/to/group/"] path = /path/to/foo.inc + ; relative paths are always relative to the including + ; file (if the condition is true); their location is not + ; affected by the condition + [includeIf "gitdir:/path/to/group/"] + path = foo.inc + Values ~~~~~~ From a076df2813a4f1c93b22ca586865b32b3e3a08a7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 11 May 2017 05:13:04 -0400 Subject: [PATCH 205/566] docs/config: avoid the term "expand" for includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the word "expand" to refer to including the contents of another config file isn't really accurate, since it's a verbatim insertion. And it can cause confusion with the expanding of the path itself via things like "~". Let's clarify when we are referring to the contents versus the filename, and use appropriate verbs in each case. Signed-off-by: Jeff King Reviewed-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index cc6d79e537..be75f70055 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -90,8 +90,8 @@ You can include a config file from another by setting the special to be included. The variable takes a pathname as its value, and is subject to tilde expansion. These variables can be given multiple times. -The included file is expanded immediately, as if its contents had been -found at the location of the include directive. If the value of the +The contents of the included file are inserted immediately, as if they +had been found at the location of the include directive. If the value of the variable is a relative path, the path is considered to be relative to the configuration file in which the include directive was found. See below for examples. @@ -172,8 +172,8 @@ Example [include] path = /path/to/foo.inc ; include by absolute path - path = foo ; expand "foo" relative to the current file - path = ~/foo ; expand "foo" in your `$HOME` directory + path = foo ; find "foo" relative to the current file + path = ~/foo ; find "foo" in your `$HOME` directory ; include if $GIT_DIR is /path/to/foo/.git [includeIf "gitdir:/path/to/foo/.git"] From ce933ebd5a4a68a1042661499c05368bb60fd523 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 11 May 2017 05:14:30 -0400 Subject: [PATCH 206/566] docs/config: consistify include.path examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the include examples use "foo.inc", but some use "foo". Since the string of examples are meant to show variations and how they differ, it's a good idea to change only one thing at a time. The filename differences are not relevant to what we're trying to show. Signed-off-by: Jeff King Reviewed-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index be75f70055..c398d9a846 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -172,8 +172,8 @@ Example [include] path = /path/to/foo.inc ; include by absolute path - path = foo ; find "foo" relative to the current file - path = ~/foo ; find "foo" in your `$HOME` directory + path = foo.inc ; find "foo.inc" relative to the current file + path = ~/foo.inc ; find "foo.inc" in your `$HOME` directory ; include if $GIT_DIR is /path/to/foo/.git [includeIf "gitdir:/path/to/foo/.git"] From 91de27c54a6ede76b3859bc89f4c387705025f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 11 May 2017 09:41:07 +0000 Subject: [PATCH 207/566] perf: add function to setup a fresh test repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to setup a fresh test repo via 'git init' to compliment the existing functions to copy over a normal & large repo. Some performance tests don't need any existing repository data at all to be significant, e.g. tests which stress glob matches against single pathological revisions or files, which I'm about to add in a subsequent commit. Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/perf/README | 1 + t/perf/perf-lib.sh | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/t/perf/README b/t/perf/README index 49ea4349be..de2fe15696 100644 --- a/t/perf/README +++ b/t/perf/README @@ -106,6 +106,7 @@ sources perf-lib.sh: After that you will want to use some of the following: + test_perf_fresh_repo # sets up an empty repository test_perf_default_repo # sets up a "normal" repository test_perf_large_repo # sets up a "large" repository diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index ab4b8b06ae..b6fc880395 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -78,6 +78,10 @@ if test -z "$GIT_PERF_LARGE_REPO"; then GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/.. fi +test_perf_do_repo_symlink_config_ () { + test_have_prereq SYMLINKS || git config core.symlinks false +} + test_perf_create_repo_from () { test "$#" = 2 || error "bug in the test script: not 2 parameters to test-create-repo" @@ -102,15 +106,22 @@ test_perf_create_repo_from () { ) && ( cd "$repo" && - "$MODERN_GIT" init -q && { - test_have_prereq SYMLINKS || - git config core.symlinks false - } && + "$MODERN_GIT" init -q && + test_perf_do_repo_symlink_config_ && mv .git/hooks .git/hooks-disabled 2>/dev/null ) || error "failed to copy repository '$source' to '$repo'" } # call at least one of these to establish an appropriately-sized repository +test_perf_fresh_repo () { + repo="${1:-$TRASH_DIRECTORY}" + "$MODERN_GIT" init -q "$repo" && + ( + cd "$repo" && + test_perf_do_repo_symlink_config_ + ) +} + test_perf_default_repo () { test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO" } From 62ca75a6b9cb2e51b4eed7c551f13975b440a86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 11 May 2017 09:41:08 +0000 Subject: [PATCH 208/566] perf: add test showing exponential growth in path globbing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test showing that runtimes of the wildmatch() function used for globbing in git grow exponentially in the face of some pathological globs. This issue affects both globs matching filenames via e.g. ls-files, and globs matching refnames via e.g. for-each-ref. As noted in the test description this is a test to see whether Git suffers from the issue noted in an article Russ Cox posted today about common bugs in various glob implementations: https://research.swtch.com/glob Signed-off-by: Ævar Arnfjörð Bjarmason Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- t/perf/p0100-globbing.sh | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100755 t/perf/p0100-globbing.sh diff --git a/t/perf/p0100-globbing.sh b/t/perf/p0100-globbing.sh new file mode 100755 index 0000000000..dd18a9ce2b --- /dev/null +++ b/t/perf/p0100-globbing.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description="Tests pathological globbing performance + +Shows how Git's globbing performance performs when given the sort of +pathological patterns described in at https://research.swtch.com/glob +" + +. ./perf-lib.sh + +test_globs_big='10 25 50 75 100' +test_globs_small='1 2 3 4 5 6' + +test_perf_fresh_repo + +test_expect_success 'setup' ' + for i in $(test_seq 1 100) + do + printf "a" >>refname && + for j in $(test_seq 1 $i) + do + printf "a*" >>refglob.$i + done && + echo b >>refglob.$i + done && + test_commit test $(cat refname).t "" $(cat refname).t +' + +for i in $test_globs_small +do + test_perf "refglob((a*)^nb) against tag (a^100).t; n = $i" ' + git for-each-ref "refs/tags/$(cat refglob.'$i')b" + ' +done + +for i in $test_globs_small +do + test_perf "fileglob((a*)^nb) against file (a^100).t; n = $i" ' + git ls-files "$(cat refglob.'$i')b" + ' +done + +test_done From c08397e3aa46fd0f0da29dfe5b257839b9c5d1c8 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 11 May 2017 15:04:24 -0700 Subject: [PATCH 209/566] pathspec: remove PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE flag Since (ae8d08242 pathspec: pass directory indicator to match_pathspec_item()) the path matching logic has been able to cope with submodules without needing to strip off a trailing slash if a path refers to a submodule. Since the stripping the trailing slash is no longer necessary, remove the PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE flag. In addition, factor out the logic which dies if a path decends into a submodule so that it can still be used as a check after a pathspec struct has been initialized. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/add.c | 5 +++-- builtin/check-ignore.c | 4 +++- pathspec.c | 29 ----------------------------- pathspec.h | 14 +++----------- submodule.c | 33 +++++++++++++++++++++++++++++++++ submodule.h | 2 ++ 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index ec58e36796..86770d6af4 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -388,10 +388,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) */ parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | - PATHSPEC_SYMLINK_LEADING_PATH | - PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, + PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); + die_path_inside_submodule(&the_index, &pathspec); + if (add_new_files) { int baselen; diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 1d73d3ca3d..91040a4b04 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -4,6 +4,7 @@ #include "quote.h" #include "pathspec.h" #include "parse-options.h" +#include "submodule.h" static int quiet, verbose, stdin_paths, show_non_matching, no_index; static const char * const check_ignore_usage[] = { @@ -87,10 +88,11 @@ static int check_ignore(struct dir_struct *dir, parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, PATHSPEC_SYMLINK_LEADING_PATH | - PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE | PATHSPEC_KEEP_ORDER, prefix, argv); + die_path_inside_submodule(&the_index, &pathspec); + /* * look for pathspecs matching entries in the index, since these * should not be ignored, in order to be consistent with diff --git a/pathspec.c b/pathspec.c index 9b7634c5aa..e424312786 100644 --- a/pathspec.c +++ b/pathspec.c @@ -398,32 +398,6 @@ static void strip_submodule_slash_cheap(struct pathspec_item *item) } } -static void strip_submodule_slash_expensive(struct pathspec_item *item) -{ - int i; - - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - int ce_len = ce_namelen(ce); - - if (!S_ISGITLINK(ce->ce_mode)) - continue; - - if (item->len <= ce_len || item->match[ce_len] != '/' || - memcmp(ce->name, item->match, ce_len)) - continue; - - if (item->len == ce_len + 1) { - /* strip trailing slash */ - item->len--; - item->match[item->len] = '\0'; - } else { - die(_("Pathspec '%s' is in submodule '%.*s'"), - item->original, ce_len, ce->name); - } - } -} - /* * Perform the initialization of a pathspec_item based on a pathspec element. */ @@ -499,9 +473,6 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) strip_submodule_slash_cheap(item); - if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE) - strip_submodule_slash_expensive(item); - if (magic & PATHSPEC_LITERAL) { item->nowildcard_len = item->len; } else { diff --git a/pathspec.h b/pathspec.h index 55e976972c..3729efa85c 100644 --- a/pathspec.h +++ b/pathspec.h @@ -62,23 +62,15 @@ struct pathspec { #define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3) /* die if a symlink is part of the given path's directory */ #define PATHSPEC_SYMLINK_LEADING_PATH (1<<4) -/* - * This is like a combination of ..LEADING_PATH and .._SLASH_CHEAP - * (but not the same): it strips the trailing slash if the given path - * is a gitlink but also checks and dies if gitlink is part of the - * leading path (i.e. the given path goes beyond a submodule). It's - * safer than _SLASH_CHEAP and also more expensive. - */ -#define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5) -#define PATHSPEC_PREFIX_ORIGIN (1<<6) -#define PATHSPEC_KEEP_ORDER (1<<7) +#define PATHSPEC_PREFIX_ORIGIN (1<<5) +#define PATHSPEC_KEEP_ORDER (1<<6) /* * For the callers that just need pure paths from somewhere else, not * from command line. Global --*-pathspecs options are ignored. No * magic is parsed in each pathspec either. If PATHSPEC_LITERAL is * allowed, then it will automatically set for every pathspec. */ -#define PATHSPEC_LITERAL_PATH (1<<8) +#define PATHSPEC_LITERAL_PATH (1<<7) extern void parse_pathspec(struct pathspec *pathspec, unsigned magic_mask, diff --git a/submodule.c b/submodule.c index 885663c421..80851d0447 100644 --- a/submodule.c +++ b/submodule.c @@ -312,6 +312,39 @@ void die_in_unpopulated_submodule(const struct index_state *istate, } } +/* + * Dies if any paths in the provided pathspec descends into a submodule + */ +void die_path_inside_submodule(const struct index_state *istate, + const struct pathspec *ps) +{ + int i, j; + + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; + int ce_len = ce_namelen(ce); + + if (!S_ISGITLINK(ce->ce_mode)) + continue; + + for (j = 0; j < ps->nr ; j++) { + const struct pathspec_item *item = &ps->items[j]; + + if (item->len <= ce_len) + continue; + if (item->match[ce_len] != '/') + continue; + if (strncmp(ce->name, item->match, ce_len)) + continue; + if (item->len == ce_len + 1) + continue; + + die(_("Pathspec '%s' is in submodule '%.*s'"), + item->original, ce_len, ce->name); + } + } +} + int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst) { diff --git a/submodule.h b/submodule.h index d11b4da40f..266d81f1c4 100644 --- a/submodule.h +++ b/submodule.h @@ -51,6 +51,8 @@ extern int is_submodule_initialized(const char *path); extern int is_submodule_populated_gently(const char *path, int *return_error_code); extern void die_in_unpopulated_submodule(const struct index_state *istate, const char *prefix); +extern void die_path_inside_submodule(const struct index_state *istate, + const struct pathspec *ps); extern int parse_submodule_update_strategy(const char *value, struct submodule_update_strategy *dst); extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s); From cbca060e102aedcedbd1c4b5394aeed24885d5de Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 11 May 2017 15:04:25 -0700 Subject: [PATCH 210/566] ls-files: prevent prune_cache from overeagerly pruning submodules Since (ae8d08242 pathspec: pass directory indicator to match_pathspec_item()) the path matching logic has been able to cope with submodules without needing to strip off a trailing slash if a path refers to a submodule. ls-files is the only caller of 'parse_pathspec()' which relies on the behavior of the PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP flag because it uses the result to construct a common prefix of all provided pathspecs which is then used to prune the index of all entries which don't have that prefix. Since submodules entries in the index don't have a trailing slash 'prune_cache()' will be overeager and prune a submodule 'sub' if the common prefix is 'sub/'. To correct this behavior, only prune entries which don't match up to, but not including, a trailing slash of the common prefix. This is in preparation to remove the PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP flag in a later patch. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a6c70dbe9e..1f3d478443 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -97,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) { int len = max_prefix_len; - if (len >= ent->len) + if (len > ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); if (!dir_path_match(ent, &pathspec, len, ps_matched)) @@ -238,7 +238,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) strbuf_addstr(&name, super_prefix); strbuf_addstr(&name, ce->name); - if (len >= ce_namelen(ce)) + if (len > ce_namelen(ce)) die("git ls-files: internal error - cache entry not superset of prefix"); if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && @@ -403,6 +403,25 @@ static void prune_cache(const char *prefix, size_t prefixlen) active_nr = last - pos; } +static int get_common_prefix_len(const char *common_prefix) +{ + int common_prefix_len; + + if (!common_prefix) + return 0; + + common_prefix_len = strlen(common_prefix); + + /* + * If the prefix has a trailing slash, strip it so that submodules wont + * be pruned from the index. + */ + if (common_prefix[common_prefix_len - 1] == '/') + common_prefix_len--; + + return common_prefix_len; +} + /* * Read the tree specified with --with-tree option * (typically, HEAD) into stage #1 and then @@ -624,8 +643,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) "--error-unmatch"); parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD | - PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, + PATHSPEC_PREFER_CWD, prefix, argv); /* @@ -637,7 +655,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) max_prefix = NULL; else max_prefix = common_prefix(&pathspec); - max_prefix_len = max_prefix ? strlen(max_prefix) : 0; + max_prefix_len = get_common_prefix_len(max_prefix); + + prune_cache(max_prefix, max_prefix_len); /* Treat unmatching pathspec elements as errors */ if (pathspec.nr && error_unmatch) @@ -651,7 +671,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) show_killed || show_modified || show_resolve_undo)) show_cached = 1; - prune_cache(max_prefix, max_prefix_len); if (with_tree) { /* * Basic sanity check; show-stages and show-unmerged From 2249d4dbc197d45da5407cbc80b2461e49bb8785 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 11 May 2017 15:04:26 -0700 Subject: [PATCH 211/566] pathspec: remove PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP Since (ae8d08242 pathspec: pass directory indicator to match_pathspec_item()) the path matching logic has been able to cope with submodules without needing to strip off a trailing slash if a path refers to a submodule. Since stripping the slash is no longer necessary, remove the PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP flag. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/reset.c | 1 - builtin/rm.c | 3 +-- builtin/submodule--helper.c | 3 +-- pathspec.c | 15 --------------- pathspec.h | 10 ++++------ 5 files changed, 6 insertions(+), 26 deletions(-) diff --git a/builtin/reset.c b/builtin/reset.c index fc3b906c47..5db2adc4c4 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -236,7 +236,6 @@ static void parse_args(struct pathspec *pathspec, parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL | - PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP | (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0), prefix, argv); } diff --git a/builtin/rm.c b/builtin/rm.c index fb79dcab18..7c323d0123 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -271,8 +271,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) die(_("index file corrupt")); parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD | - PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, + PATHSPEC_PREFER_CWD, prefix, argv); refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 566a5b6a6f..8cc648d85b 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -233,8 +233,7 @@ static int module_list_compute(int argc, const char **argv, int i, result = 0; char *ps_matched = NULL; parse_pathspec(pathspec, 0, - PATHSPEC_PREFER_FULL | - PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, + PATHSPEC_PREFER_FULL, prefix, argv); if (pathspec->nr) diff --git a/pathspec.c b/pathspec.c index e424312786..1e5df2316e 100644 --- a/pathspec.c +++ b/pathspec.c @@ -386,18 +386,6 @@ static const char *parse_element_magic(unsigned *magic, int *prefix_len, return parse_short_magic(magic, elem); } -static void strip_submodule_slash_cheap(struct pathspec_item *item) -{ - if (item->len >= 1 && item->match[item->len - 1] == '/') { - int i = cache_name_pos(item->match, item->len - 1); - - if (i >= 0 && S_ISGITLINK(active_cache[i]->ce_mode)) { - item->len--; - item->match[item->len] = '\0'; - } - } -} - /* * Perform the initialization of a pathspec_item based on a pathspec element. */ @@ -470,9 +458,6 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, item->original = xstrdup(elt); } - if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) - strip_submodule_slash_cheap(item); - if (magic & PATHSPEC_LITERAL) { item->nowildcard_len = item->len; } else { diff --git a/pathspec.h b/pathspec.h index 3729efa85c..6671b95770 100644 --- a/pathspec.h +++ b/pathspec.h @@ -58,19 +58,17 @@ struct pathspec { #define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */ #define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */ #define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */ -/* strip the trailing slash if the given path is a gitlink */ -#define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3) /* die if a symlink is part of the given path's directory */ -#define PATHSPEC_SYMLINK_LEADING_PATH (1<<4) -#define PATHSPEC_PREFIX_ORIGIN (1<<5) -#define PATHSPEC_KEEP_ORDER (1<<6) +#define PATHSPEC_SYMLINK_LEADING_PATH (1<<3) +#define PATHSPEC_PREFIX_ORIGIN (1<<4) +#define PATHSPEC_KEEP_ORDER (1<<5) /* * For the callers that just need pure paths from somewhere else, not * from command line. Global --*-pathspecs options are ignored. No * magic is parsed in each pathspec either. If PATHSPEC_LITERAL is * allowed, then it will automatically set for every pathspec. */ -#define PATHSPEC_LITERAL_PATH (1<<7) +#define PATHSPEC_LITERAL_PATH (1<<6) extern void parse_pathspec(struct pathspec *pathspec, unsigned magic_mask, From 08de9151a8a67f29a3a5a36931298237d78ca736 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 11 May 2017 15:04:27 -0700 Subject: [PATCH 212/566] pathspec: convert find_pathspecs_matching_against_index to take an index Convert find_pathspecs_matching_against_index to take an index parameter. In addition mark pathspec.c with NO_THE_INDEX_COMPATIBILITY_MACROS now that it doesn't use any cache macros or reference 'the_index'. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/add.c | 4 ++-- builtin/check-ignore.c | 2 +- pathspec.c | 11 +++++++---- pathspec.h | 7 +++++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index 86770d6af4..0365a52098 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -136,7 +136,7 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, *dst++ = entry; } dir->nr = dst - dir->entries; - add_pathspec_matches_against_index(pathspec, seen); + add_pathspec_matches_against_index(pathspec, &the_index, seen); return seen; } @@ -418,7 +418,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) int i; if (!seen) - seen = find_pathspecs_matching_against_index(&pathspec); + seen = find_pathspecs_matching_against_index(&pathspec, &the_index); /* * file_exists() assumes exact match diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 91040a4b04..7629018a56 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -98,7 +98,7 @@ static int check_ignore(struct dir_struct *dir, * should not be ignored, in order to be consistent with * 'git status', 'git add' etc. */ - seen = find_pathspecs_matching_against_index(&pathspec); + seen = find_pathspecs_matching_against_index(&pathspec, &the_index); for (i = 0; i < pathspec.nr; i++) { full_path = pathspec.items[i].match; exclude = NULL; diff --git a/pathspec.c b/pathspec.c index 1e5df2316e..828405021f 100644 --- a/pathspec.c +++ b/pathspec.c @@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "dir.h" #include "pathspec.h" @@ -17,6 +18,7 @@ * to use find_pathspecs_matching_against_index() instead. */ void add_pathspec_matches_against_index(const struct pathspec *pathspec, + const struct index_state *istate, char *seen) { int num_unmatched = 0, i; @@ -32,8 +34,8 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec, num_unmatched++; if (!num_unmatched) return; - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; ce_path_match(ce, pathspec, seen); } } @@ -46,10 +48,11 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec, * nature of the "closest" (i.e. most specific) matches which each of the * given pathspecs achieves against all items in the index. */ -char *find_pathspecs_matching_against_index(const struct pathspec *pathspec) +char *find_pathspecs_matching_against_index(const struct pathspec *pathspec, + const struct index_state *istate) { char *seen = xcalloc(pathspec->nr, 1); - add_pathspec_matches_against_index(pathspec, seen); + add_pathspec_matches_against_index(pathspec, istate, seen); return seen; } diff --git a/pathspec.h b/pathspec.h index 6671b95770..60e6500401 100644 --- a/pathspec.h +++ b/pathspec.h @@ -96,7 +96,10 @@ static inline int ps_strcmp(const struct pathspec_item *item, return strcmp(s1, s2); } -extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec); -extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen); +extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, + const struct index_state *istate, + char *seen); +extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec, + const struct index_state *istate); #endif /* PATHSPEC_H */ From 6c486862636be1fe2d5785451c52f5379b0bad24 Mon Sep 17 00:00:00 2001 From: Jean-Noel Avila Date: Thu, 11 May 2017 14:06:32 +0200 Subject: [PATCH 213/566] usability: don't ask questions if no reply is required There has been a bug report by a corporate user that stated that "spelling mistake of stash followed by a yes prints character 'y' infinite times." This analysis was false. When the spelling of a command contains errors, the git program tries to help the user by providing candidates which are close to the unexisting command. E.g Git prints the following: git: 'stahs' is not a git command. See 'git --help'. Did you mean this? stash and then exits. The problem with this hint is that it is not formally indicated as an hint and the user is in fact encouraged to reply to the question, whereas the Git command is already finished. The user was unlucky enough that it was the command he was looking for, and replied "yes" on the command line, effectively launching the `yes` program. The initial error is that the Git programs, when launched in command-line mode (without interaction) must not ask questions, because these questions would normally require a user input as a reply that they won't handle indeed. That's a source of confusion on UX level. To improve the general usability of the Git suite, the following rule was applied: if the sentence * appears in a non-interactive session * is printed last before exit * is a question addressing the user ("you") the sentence is turned into affirmative and proposes the option. The basic rewording of the question sentences has been extended to other spots found in the source. Requested at https://github.com/git/git-scm.com/issues/999 by rpai1 Signed-off-by: Jean-Noel Avila Signed-off-by: Junio C Hamano --- builtin/am.c | 5 +++-- builtin/checkout.c | 5 ++--- help.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 31fb60578f..38944f7902 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1313,7 +1313,7 @@ static int parse_mail(struct am_state *state, const char *mail) } if (is_empty_file(am_path(state, "patch"))) { - printf_ln(_("Patch is empty. Was it split wrong?")); + printf_ln(_("Patch is empty.")); die_user_resolve(state); } @@ -1941,7 +1941,8 @@ static void am_resolve(struct am_state *state) if (unmerged_cache()) { printf_ln(_("You still have unmerged paths in your index.\n" - "Did you forget to use 'git add'?")); + "You should 'git add' each file with resolved conflicts to mark them as such.\n" + "You might run `git rm` on a file to accept \"deleted by them\" for it.")); die_user_resolve(state); } diff --git a/builtin/checkout.c b/builtin/checkout.c index 81f07c3ef2..8d313ceb52 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1259,9 +1259,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) * new_branch && argc > 1 will be caught later. */ if (opts.new_branch && argc == 1) - die(_("Cannot update paths and switch to branch '%s' at the same time.\n" - "Did you intend to checkout '%s' which can not be resolved as commit?"), - opts.new_branch, argv[0]); + die(_("'%s' is not a commit and a branch '%s' cannot be created from it"), + argv[0], opts.new_branch); if (opts.force_detach) die(_("git checkout: --detach does not take a path argument '%s'"), diff --git a/help.c b/help.c index bc6cd19cf3..a07f01e6f9 100644 --- a/help.c +++ b/help.c @@ -411,8 +411,8 @@ const char *help_unknown_cmd(const char *cmd) if (SIMILAR_ENOUGH(best_similarity)) { fprintf_ln(stderr, - Q_("\nDid you mean this?", - "\nDid you mean one of these?", + Q_("\nThe most similar command is", + "\nThe most similar commands are", n)); for (i = 0; i < n; i++) From 9932242f597b2990117430546e411793090e22e2 Mon Sep 17 00:00:00 2001 From: Jean-Noel Avila Date: Thu, 11 May 2017 14:06:33 +0200 Subject: [PATCH 214/566] read-tree -m: make error message for merging 0 trees less smart aleck "git read-tree -m" requires a tree argument to name the tree to be merged in. Git uses a cutesy error message to say so and why: $ git read-tree -m warning: read-tree: emptying the index with no arguments is deprecated; use --empty fatal: just how do you expect me to merge 0 trees? $ git read-tree -m --empty fatal: just how do you expect me to merge 0 trees? When lucky, that could produce an ah-hah moment for the user, but it's more likely to irritate and distract them. Instead, tell the user plainly that the tree argument is required. Also document this requirement in the git-read-tree(1) manpage where there is room to explain it in a more straightforward way. Signed-off-by: Jean-Noel Avila Helped-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-read-tree.txt | 2 +- builtin/read-tree.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index fa1d557e5b..6bb725bffb 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -131,7 +131,7 @@ Merging ------- If `-m` is specified, 'git read-tree' can perform 3 kinds of merge, a single tree merge if only 1 tree is given, a -fast-forward merge with 2 trees, or a 3-way merge if 3 trees are +fast-forward merge with 2 trees, or a 3-way merge if 3 or more trees are provided. diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 8ba64bc785..8eae1e88e8 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -197,9 +197,10 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) setup_work_tree(); if (opts.merge) { - if (stage < 2) - die("just how do you expect me to merge %d trees?", stage-1); switch (stage - 1) { + case 0: + die("you must specify at least one tree to merge"); + break; case 1: opts.fn = opts.prefix ? bind_merge : oneway_merge; break; From 6963893943dacc1a532d047c83ce905818dece46 Mon Sep 17 00:00:00 2001 From: Jean-Noel Avila Date: Thu, 11 May 2017 14:06:34 +0200 Subject: [PATCH 215/566] git-filter-branch: be more direct in an error message git-filter-branch requires the specification of a branch by one way or another. If no branch appears to have been specified, we know the user got the usage wrong but we don't know what they were trying to do --- e.g. maybe they specified the ref to rewrite but in the wrong place. In this case, just state that the branch specification is missing. Signed-off-by: Jean-Noel Avila Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 2b8cdba157..aafaf708da 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -239,7 +239,7 @@ git rev-parse --no-flags --revs-only --symbolic-full-name \ sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads test -s "$tempdir"/heads || - die "Which ref do you want to rewrite?" + die "You must specify a ref to rewrite." GIT_INDEX_FILE="$(pwd)/../index" export GIT_INDEX_FILE From d8193743e0883e7331c102a4d04cee5324eb3b5f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 12 May 2017 23:28:50 -0400 Subject: [PATCH 216/566] usage.c: add BUG() function There's a convention in Git's code base to write assertions as: if (...some_bad_thing...) die("BUG: the terrible thing happened"); with the idea that users should never see a "BUG:" message (but if they, it at least gives a clue what happened). We use die() here because it's convenient, but there are a few draw-backs: 1. Without parsing the messages, it's hard for callers to distinguish BUG assertions from regular errors. For instance, it would be nice if the test suite could check that we don't hit any assertions, but test_must_fail will pass BUG deaths as OK. 2. It would be useful to add more debugging features to BUG assertions, like file/line numbers or dumping core. 3. The die() handler can be replaced, and might not actually exit the whole program (e.g., it may just pthread_exit()). This is convenient for normal errors, but for an assertion failure (which is supposed to never happen), we're probably better off taking down the whole process as quickly and cleanly as possible. We could address these by checking in die() whether the error message starts with "BUG", and behaving appropriately. But there's little advantage at that point to sharing the die() code, and only downsides (e.g., we can't change the BUG() interface independently). Moreover, converting all of the existing BUG calls reveals that the test suite does indeed trigger a few of them. Instead, this patch introduces a new BUG() function, which prints an error before dying via SIGABRT. This gives us test suite checking and core dumps. The function is actually a macro (when supported) so that we can show the file/line number. We can convert die("BUG") invocations to BUG() in further patches, dealing with any test fallouts individually. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-compat-util.h | 9 +++++++++ usage.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index bd04564a69..4575b3890b 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1064,6 +1064,15 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size, #define HAVE_VARIADIC_MACROS 1 #endif +#ifdef HAVE_VARIADIC_MACROS +__attribute__((format (printf, 3, 4))) NORETURN +void BUG_fl(const char *file, int line, const char *fmt, ...); +#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__) +#else +__attribute__((format (printf, 1, 2))) NORETURN +void BUG(const char *fmt, ...); +#endif + /* * Preserves errno, prints a message, but gives no warning for ENOENT. * Returns 0 on success, which includes trying to unlink an object that does diff --git a/usage.c b/usage.c index ad6d2910fb..7e6cb20280 100644 --- a/usage.c +++ b/usage.c @@ -201,3 +201,35 @@ void warning(const char *warn, ...) warn_routine(warn, params); va_end(params); } + +static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params) +{ + char prefix[256]; + + /* truncation via snprintf is OK here */ + if (file) + snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line); + else + snprintf(prefix, sizeof(prefix), "BUG: "); + + vreportf(prefix, fmt, params); + abort(); +} + +#ifdef HAVE_VARIADIC_MACROS +void BUG_fl(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + BUG_vfl(file, line, fmt, ap); + va_end(ap); +} +#else +void BUG(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + BUG_vfl(NULL, 0, fmt, ap); + va_end(ap); +} +#endif From 588a538ae554f61a37d43c972da75d0f7c3ed484 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 12 May 2017 23:29:18 -0400 Subject: [PATCH 217/566] setup_git_env: convert die("BUG") to BUG() Converting to BUG() makes it easier to detect and debug cases where we hit this assertion. Coupled with a new test in t1300, this shows that the test suite can detect such corner cases. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- environment.c | 2 +- t/t1300-repo-config.sh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/environment.c b/environment.c index ff6e4f06e9..1f0bda5afa 100644 --- a/environment.c +++ b/environment.c @@ -169,7 +169,7 @@ static void setup_git_env(void) git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) { if (!startup_info->have_repository) - die("BUG: setup_git_env called without repository"); + BUG("setup_git_env called without repository"); git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; } gitfile = read_gitfile(git_dir); diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index afcca0d52c..867704a642 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1539,4 +1539,10 @@ test_expect_success !MINGW '--show-origin blob ref' ' test_cmp expect output ' +test_expect_failure '--local requires a repo' ' + # we expect 128 to ensure that we do not simply + # fail to find anything and return code "1" + test_expect_code 128 nongit git config --local foo.bar +' + test_done From 25cd291963e4b0fae0eabe7fe02be693702d79bb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 12 May 2017 23:29:31 -0400 Subject: [PATCH 218/566] config: complain about --local outside of a git repo The "--local" option instructs git-config to read or modify the repository-level config. This doesn't make any sense if you're not actually in a repository. Older versions of Git would blindly try to read or write ".git/config". For reading, this would result in a quiet failure, since there was no config to read (and thus no matching config value). Writing would generally fail noisily, since ".git" was unlikely to exist. But since b1ef400ee (setup_git_env: avoid blind fall-back to ".git", 2016-10-20), we catch this in the call to git_pathdup() and die with an assertion. Dying is the right thing to do, but we should catch the problem early and give a more human-friendly error message. Note that even without --local, git-config will sometimes default to using local repository config (e.g., when writing). These cases are already protected by similar checks, and covered by a test in t1308. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/config.c | 3 +++ t/t1300-repo-config.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin/config.c b/builtin/config.c index 3a554ad50c..7f6c25d4d9 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -496,6 +496,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) usage_with_options(builtin_config_usage, builtin_config_options); } + if (use_local_config && nongit) + die(_("--local can only be used inside a git repository")); + if (given_config_source.file && !strcmp(given_config_source.file, "-")) { given_config_source.file = NULL; diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 867704a642..13b7851f7c 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1539,7 +1539,7 @@ test_expect_success !MINGW '--show-origin blob ref' ' test_cmp expect output ' -test_expect_failure '--local requires a repo' ' +test_expect_success '--local requires a repo' ' # we expect 128 to ensure that we do not simply # fail to find anything and return code "1" test_expect_code 128 nongit git config --local foo.bar From c74271aae74c951ab321aa16fe573ea3bcab86ba Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 14 May 2017 18:00:58 +0000 Subject: [PATCH 219/566] builtin/log: honor log.decorate The recent change that introduced autodecorating of refs accidentally broke the ability of users to set log.decorate = false to override it. When the git_log_config was traversed a second time with an option other than log.decorate, the decoration style would be set to the automatic style, even if the user had already overridden it. Instead of setting the option in config parsing, set it in init_log_defaults instead. Add a test for this case. The actual additional config option doesn't matter, but it needs to be something not already set in the configuration file. Signed-off-by: brian m. carlson Acked-by: Alex Henrie Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- builtin/log.c | 4 ++-- t/t4202-log.sh | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index d755a59600..cf2b736ca7 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -110,6 +110,8 @@ static void init_log_defaults(void) { init_grep_defaults(); init_diff_ui_defaults(); + + decoration_style = auto_decoration_style(); } static void cmd_log_init_defaults(struct rev_info *rev) @@ -410,8 +412,6 @@ static int git_log_config(const char *var, const char *value, void *cb) if (decoration_style < 0) decoration_style = 0; /* maybe warn? */ return 0; - } else { - decoration_style = auto_decoration_style(); } if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); diff --git a/t/t4202-log.sh b/t/t4202-log.sh index f577990716..1c7d6729c6 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -577,6 +577,18 @@ test_expect_success 'log.decorate configuration' ' ' +test_expect_success 'log.decorate config parsing' ' + git log --oneline --decorate=full >expect.full && + git log --oneline --decorate=short >expect.short && + + test_config log.decorate full && + test_config log.mailmap true && + git log --oneline >actual && + test_cmp expect.full actual && + git log --oneline --decorate=short >actual && + test_cmp expect.short actual +' + test_expect_success TTY 'log output on a TTY' ' git log --oneline --decorate >expect.short && From e3f43ce765c38f4be94239d07c8c3c596780c514 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 12 May 2017 23:48:18 -0400 Subject: [PATCH 220/566] usage.c: drop set_error_handle() The set_error_handle() function was introduced by 3b331e926 (vreportf: report to arbitrary filehandles, 2015-08-11) so that run-command could send post-fork, pre-exec errors to the parent's original stderr. That use went away in 79319b194 (run-command: eliminate calls to error handling functions in child, 2017-04-19), which pushes all of the error reporting to the parent. This leaves no callers of set_error_handle(). As we're not likely to add any new ones, let's drop it. Signed-off-by: Jeff King Acked-by: Brandon Williams Reviewed-by: Ramsay Jones Signed-off-by: Junio C Hamano --- git-compat-util.h | 1 - usage.c | 10 +--------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/git-compat-util.h b/git-compat-util.h index 8a4a3f85e7..f1f2a2d731 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -445,7 +445,6 @@ extern void (*get_error_routine(void))(const char *err, va_list params); extern void set_warn_routine(void (*routine)(const char *warn, va_list params)); extern void (*get_warn_routine(void))(const char *warn, va_list params); extern void set_die_is_recursing_routine(int (*routine)(void)); -extern void set_error_handle(FILE *); extern int starts_with(const char *str, const char *prefix); diff --git a/usage.c b/usage.c index ad6d2910fb..2623c078e1 100644 --- a/usage.c +++ b/usage.c @@ -6,12 +6,9 @@ #include "git-compat-util.h" #include "cache.h" -static FILE *error_handle; - void vreportf(const char *prefix, const char *err, va_list params) { char msg[4096]; - FILE *fh = error_handle ? error_handle : stderr; char *p; vsnprintf(msg, sizeof(msg), err, params); @@ -19,7 +16,7 @@ void vreportf(const char *prefix, const char *err, va_list params) if (iscntrl(*p) && *p != '\t' && *p != '\n') *p = '?'; } - fprintf(fh, "%s%s\n", prefix, msg); + fprintf(stderr, "%s%s\n", prefix, msg); } static NORETURN void usage_builtin(const char *err, va_list params) @@ -88,11 +85,6 @@ void set_die_is_recursing_routine(int (*routine)(void)) die_is_recursing = routine; } -void set_error_handle(FILE *fh) -{ - error_handle = fh; -} - void NORETURN usagef(const char *err, ...) { va_list params; From 7e936842f54c98eb41fb46c94da417065fe333f5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 13 May 2017 05:04:58 -0400 Subject: [PATCH 221/566] pkt-line: annotate packet_writel with LAST_ARG_MUST_BE_NULL packet_writel() takes a variable-sized list and reads to the first NULL. Let's let the compiler know so that it can help us catch mistakes in the callers. This should have been annotated similarly when it was a static function, but it's doubly important now that the function is available to the whole code-base. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- pkt-line.h | 1 + 1 file changed, 1 insertion(+) diff --git a/pkt-line.h b/pkt-line.h index b2965869ad..450183b649 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -25,6 +25,7 @@ void packet_buf_flush(struct strbuf *buf); void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3))); int packet_flush_gently(int fd); int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3))); +LAST_ARG_MUST_BE_NULL int packet_writel(int fd, const char *line, ...); int write_packetized_from_fd(int fd_in, int fd_out); int write_packetized_from_buf(const char *src_in, size_t len, int fd_out); From a810ea9945d5959b6cf73d117bc0f20fde58c556 Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:57 -0400 Subject: [PATCH 222/566] convert: split start_multi_file_filter() into two separate functions To enable future reuse of the filter..process infrastructure, split start_multi_file_filter() into two separate parts. start_multi_file_filter() will now only contain the generic logic to manage the creation and tracking of the child process in a hashmap. start_multi_file_filter_fn() is a protocol specific initialization function that will negotiate the multi-file-filter interface version and capabilities. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- convert.c | 58 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/convert.c b/convert.c index 0f1b5a6340..36401fe087 100644 --- a/convert.c +++ b/convert.c @@ -565,35 +565,14 @@ static void stop_multi_file_filter(struct child_process *process) finish_command(process); } -static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, const char *cmd) +static int start_multi_file_filter_fn(struct cmd2process *entry) { int err; - struct cmd2process *entry; - struct child_process *process; - const char *argv[] = { cmd, NULL }; struct string_list cap_list = STRING_LIST_INIT_NODUP; char *cap_buf; const char *cap_name; - - entry = xmalloc(sizeof(*entry)); - entry->cmd = cmd; - entry->supported_capabilities = 0; - process = &entry->process; - - child_process_init(process); - process->argv = argv; - process->use_shell = 1; - process->in = -1; - process->out = -1; - process->clean_on_exit = 1; - process->clean_on_exit_handler = stop_multi_file_filter; - - if (start_command(process)) { - error("cannot fork to run external filter '%s'", cmd); - return NULL; - } - - hashmap_entry_init(entry, strhash(cmd)); + struct child_process *process = &entry->process; + const char *cmd = entry->cmd; sigchain_push(SIGPIPE, SIG_IGN); @@ -642,6 +621,37 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons done: sigchain_pop(SIGPIPE); + return err; +} + +static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, const char *cmd) +{ + int err; + struct cmd2process *entry; + struct child_process *process; + const char *argv[] = { cmd, NULL }; + + entry = xmalloc(sizeof(*entry)); + entry->cmd = cmd; + entry->supported_capabilities = 0; + process = &entry->process; + + child_process_init(process); + process->argv = argv; + process->use_shell = 1; + process->in = -1; + process->out = -1; + process->clean_on_exit = 1; + process->clean_on_exit_handler = stop_multi_file_filter; + + if (start_command(process)) { + error("cannot fork to run external filter '%s'", cmd); + return NULL; + } + + hashmap_entry_init(entry, strhash(cmd)); + + err = start_multi_file_filter_fn(entry); if (err) { error("initialization for external filter '%s' failed", cmd); kill_multi_file_filter(hashmap, entry); From 1b0b46ee3b310b66023423ba32f023d923d7cb0e Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:58 -0400 Subject: [PATCH 223/566] convert: separate generic structures and variables from the filter specific ones To enable future reuse of the filter..process infrastructure, split the cmd2process structure into two separate parts. subprocess_entry will now contain the generic data required to manage the creation and tracking of the child process in a hashmap. cmd2process is a filter protocol specific structure that is used to track the negotiated capabilities of the filter. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- convert.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/convert.c b/convert.c index 36401fe087..bfb19beed5 100644 --- a/convert.c +++ b/convert.c @@ -496,26 +496,31 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le #define CAP_CLEAN (1u<<0) #define CAP_SMUDGE (1u<<1) -struct cmd2process { +struct subprocess_entry { struct hashmap_entry ent; /* must be the first member! */ - unsigned int supported_capabilities; const char *cmd; struct child_process process; }; +struct cmd2process { + struct subprocess_entry subprocess; /* must be the first member! */ + unsigned int supported_capabilities; +}; + static int cmd_process_map_initialized; static struct hashmap cmd_process_map; -static int cmd2process_cmp(const struct cmd2process *e1, - const struct cmd2process *e2, +static int cmd2process_cmp(const struct subprocess_entry *e1, + const struct subprocess_entry *e2, const void *unused) { return strcmp(e1->cmd, e2->cmd); } -static struct cmd2process *find_multi_file_filter_entry(struct hashmap *hashmap, const char *cmd) +static struct subprocess_entry *find_multi_file_filter_entry(struct hashmap *hashmap, const char *cmd) { - struct cmd2process key; + struct subprocess_entry key; + hashmap_entry_init(&key, strhash(cmd)); key.cmd = cmd; return hashmap_get(hashmap, &key, NULL); @@ -541,7 +546,7 @@ static void read_multi_file_filter_status(int fd, struct strbuf *status) } } -static void kill_multi_file_filter(struct hashmap *hashmap, struct cmd2process *entry) +static void kill_multi_file_filter(struct hashmap *hashmap, struct subprocess_entry *entry) { if (!entry) return; @@ -571,8 +576,8 @@ static int start_multi_file_filter_fn(struct cmd2process *entry) struct string_list cap_list = STRING_LIST_INIT_NODUP; char *cap_buf; const char *cap_name; - struct child_process *process = &entry->process; - const char *cmd = entry->cmd; + struct child_process *process = &entry->subprocess.process; + const char *cmd = entry->subprocess.cmd; sigchain_push(SIGPIPE, SIG_IGN); @@ -632,9 +637,9 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons const char *argv[] = { cmd, NULL }; entry = xmalloc(sizeof(*entry)); - entry->cmd = cmd; + entry->subprocess.cmd = cmd; entry->supported_capabilities = 0; - process = &entry->process; + process = &entry->subprocess.process; child_process_init(process); process->argv = argv; @@ -654,7 +659,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons err = start_multi_file_filter_fn(entry); if (err) { error("initialization for external filter '%s' failed", cmd); - kill_multi_file_filter(hashmap, entry); + kill_multi_file_filter(hashmap, &entry->subprocess); return NULL; } @@ -678,7 +683,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len hashmap_init(&cmd_process_map, (hashmap_cmp_fn) cmd2process_cmp, 0); entry = NULL; } else { - entry = find_multi_file_filter_entry(&cmd_process_map, cmd); + entry = (struct cmd2process *)find_multi_file_filter_entry(&cmd_process_map, cmd); } fflush(NULL); @@ -688,7 +693,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (!entry) return 0; } - process = &entry->process; + process = &entry->subprocess.process; if (!(wanted_capability & entry->supported_capabilities)) return 0; @@ -759,7 +764,7 @@ done: * Force shutdown and restart if another blob requires filtering. */ error("external filter '%s' failed", cmd); - kill_multi_file_filter(&cmd_process_map, entry); + kill_multi_file_filter(&cmd_process_map, &entry->subprocess); } } else { strbuf_swap(dst, &nbuf); From 7ddb9b2ca9f5a1ebb8a13744575ec3e6879c5f7e Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:27:59 -0400 Subject: [PATCH 224/566] convert: update generic functions to only use generic data structures Update all functions that are going to be moved into a reusable module so that they only work with the reusable data structures. Move code that is specific to the filter out into the filter specific functions. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- convert.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/convert.c b/convert.c index bfb19beed5..d15b10a3c5 100644 --- a/convert.c +++ b/convert.c @@ -556,7 +556,6 @@ static void kill_multi_file_filter(struct hashmap *hashmap, struct subprocess_en finish_command(&entry->process); hashmap_remove(hashmap, entry, NULL); - free(entry); } static void stop_multi_file_filter(struct child_process *process) @@ -570,14 +569,15 @@ static void stop_multi_file_filter(struct child_process *process) finish_command(process); } -static int start_multi_file_filter_fn(struct cmd2process *entry) +static int start_multi_file_filter_fn(struct subprocess_entry *subprocess) { int err; + struct cmd2process *entry = (struct cmd2process *)subprocess; struct string_list cap_list = STRING_LIST_INIT_NODUP; char *cap_buf; const char *cap_name; - struct child_process *process = &entry->subprocess.process; - const char *cmd = entry->subprocess.cmd; + struct child_process *process = &subprocess->process; + const char *cmd = subprocess->cmd; sigchain_push(SIGPIPE, SIG_IGN); @@ -629,17 +629,16 @@ done: return err; } -static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, const char *cmd) +typedef int(*subprocess_start_fn)(struct subprocess_entry *entry); +int start_multi_file_filter(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, + subprocess_start_fn startfn) { int err; - struct cmd2process *entry; struct child_process *process; const char *argv[] = { cmd, NULL }; - entry = xmalloc(sizeof(*entry)); - entry->subprocess.cmd = cmd; - entry->supported_capabilities = 0; - process = &entry->subprocess.process; + entry->cmd = cmd; + process = &entry->process; child_process_init(process); process->argv = argv; @@ -649,22 +648,23 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons process->clean_on_exit = 1; process->clean_on_exit_handler = stop_multi_file_filter; - if (start_command(process)) { + err = start_command(process); + if (err) { error("cannot fork to run external filter '%s'", cmd); - return NULL; + return err; } hashmap_entry_init(entry, strhash(cmd)); - err = start_multi_file_filter_fn(entry); + err = startfn(entry); if (err) { error("initialization for external filter '%s' failed", cmd); - kill_multi_file_filter(hashmap, &entry->subprocess); - return NULL; + kill_multi_file_filter(hashmap, entry); + return err; } hashmap_add(hashmap, entry); - return entry; + return 0; } static int apply_multi_file_filter(const char *path, const char *src, size_t len, @@ -689,9 +689,13 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len fflush(NULL); if (!entry) { - entry = start_multi_file_filter(&cmd_process_map, cmd); - if (!entry) + entry = xmalloc(sizeof(*entry)); + entry->supported_capabilities = 0; + + if (start_multi_file_filter(&cmd_process_map, &entry->subprocess, cmd, start_multi_file_filter_fn)) { + free(entry); return 0; + } } process = &entry->subprocess.process; @@ -765,6 +769,7 @@ done: */ error("external filter '%s' failed", cmd); kill_multi_file_filter(&cmd_process_map, &entry->subprocess); + free(entry); } } else { strbuf_swap(dst, &nbuf); From f514d7d177f7cabbacc3f2cda96ca211266ac2ff Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:28:00 -0400 Subject: [PATCH 225/566] convert: rename reusable sub-process functions Do a mechanical rename of the functions that will become the reusable sub-process module. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- convert.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/convert.c b/convert.c index d15b10a3c5..cfae45aeee 100644 --- a/convert.c +++ b/convert.c @@ -507,8 +507,8 @@ struct cmd2process { unsigned int supported_capabilities; }; -static int cmd_process_map_initialized; -static struct hashmap cmd_process_map; +static int subprocess_map_initialized; +static struct hashmap subprocess_map; static int cmd2process_cmp(const struct subprocess_entry *e1, const struct subprocess_entry *e2, @@ -517,7 +517,7 @@ static int cmd2process_cmp(const struct subprocess_entry *e1, return strcmp(e1->cmd, e2->cmd); } -static struct subprocess_entry *find_multi_file_filter_entry(struct hashmap *hashmap, const char *cmd) +static struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd) { struct subprocess_entry key; @@ -526,7 +526,7 @@ static struct subprocess_entry *find_multi_file_filter_entry(struct hashmap *has return hashmap_get(hashmap, &key, NULL); } -static void read_multi_file_filter_status(int fd, struct strbuf *status) +static void subprocess_read_status(int fd, struct strbuf *status) { struct strbuf **pair; char *line; @@ -546,7 +546,7 @@ static void read_multi_file_filter_status(int fd, struct strbuf *status) } } -static void kill_multi_file_filter(struct hashmap *hashmap, struct subprocess_entry *entry) +static void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry) { if (!entry) return; @@ -558,10 +558,10 @@ static void kill_multi_file_filter(struct hashmap *hashmap, struct subprocess_en hashmap_remove(hashmap, entry, NULL); } -static void stop_multi_file_filter(struct child_process *process) +static void subprocess_exit_handler(struct child_process *process) { sigchain_push(SIGPIPE, SIG_IGN); - /* Closing the pipe signals the filter to initiate a shutdown. */ + /* Closing the pipe signals the subprocess to initiate a shutdown. */ close(process->in); close(process->out); sigchain_pop(SIGPIPE); @@ -630,7 +630,7 @@ done: } typedef int(*subprocess_start_fn)(struct subprocess_entry *entry); -int start_multi_file_filter(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, +int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, subprocess_start_fn startfn) { int err; @@ -646,11 +646,11 @@ int start_multi_file_filter(struct hashmap *hashmap, struct subprocess_entry *en process->in = -1; process->out = -1; process->clean_on_exit = 1; - process->clean_on_exit_handler = stop_multi_file_filter; + process->clean_on_exit_handler = subprocess_exit_handler; err = start_command(process); if (err) { - error("cannot fork to run external filter '%s'", cmd); + error("cannot fork to run subprocess '%s'", cmd); return err; } @@ -658,8 +658,8 @@ int start_multi_file_filter(struct hashmap *hashmap, struct subprocess_entry *en err = startfn(entry); if (err) { - error("initialization for external filter '%s' failed", cmd); - kill_multi_file_filter(hashmap, entry); + error("initialization for subprocess '%s' failed", cmd); + subprocess_stop(hashmap, entry); return err; } @@ -678,12 +678,12 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len struct strbuf filter_status = STRBUF_INIT; const char *filter_type; - if (!cmd_process_map_initialized) { - cmd_process_map_initialized = 1; - hashmap_init(&cmd_process_map, (hashmap_cmp_fn) cmd2process_cmp, 0); + if (!subprocess_map_initialized) { + subprocess_map_initialized = 1; + hashmap_init(&subprocess_map, (hashmap_cmp_fn) cmd2process_cmp, 0); entry = NULL; } else { - entry = (struct cmd2process *)find_multi_file_filter_entry(&cmd_process_map, cmd); + entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd); } fflush(NULL); @@ -692,7 +692,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len entry = xmalloc(sizeof(*entry)); entry->supported_capabilities = 0; - if (start_multi_file_filter(&cmd_process_map, &entry->subprocess, cmd, start_multi_file_filter_fn)) { + if (subprocess_start(&subprocess_map, &entry->subprocess, cmd, start_multi_file_filter_fn)) { free(entry); return 0; } @@ -737,7 +737,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (err) goto done; - read_multi_file_filter_status(process->out, &filter_status); + subprocess_read_status(process->out, &filter_status); err = strcmp(filter_status.buf, "success"); if (err) goto done; @@ -746,7 +746,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (err) goto done; - read_multi_file_filter_status(process->out, &filter_status); + subprocess_read_status(process->out, &filter_status); err = strcmp(filter_status.buf, "success"); done: @@ -768,7 +768,7 @@ done: * Force shutdown and restart if another blob requires filtering. */ error("external filter '%s' failed", cmd); - kill_multi_file_filter(&cmd_process_map, &entry->subprocess); + subprocess_stop(&subprocess_map, &entry->subprocess); free(entry); } } else { From 99605d62e8e7e568035dc953b24b79b3d52f0522 Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:28:01 -0400 Subject: [PATCH 226/566] sub-process: move sub-process functions into separate files Move the sub-proces functions into sub-process.h/c. Add documentation for the new module in Documentation/technical/api-sub-process.txt Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- Documentation/technical/api-sub-process.txt | 59 +++++++++++ Makefile | 1 + convert.c | 104 +------------------- sub-process.c | 102 +++++++++++++++++++ sub-process.h | 49 +++++++++ 5 files changed, 212 insertions(+), 103 deletions(-) create mode 100644 Documentation/technical/api-sub-process.txt create mode 100644 sub-process.c create mode 100644 sub-process.h diff --git a/Documentation/technical/api-sub-process.txt b/Documentation/technical/api-sub-process.txt new file mode 100644 index 0000000000..793508cf3e --- /dev/null +++ b/Documentation/technical/api-sub-process.txt @@ -0,0 +1,59 @@ +sub-process API +=============== + +The sub-process API makes it possible to run background sub-processes +for the entire lifetime of a Git invocation. If Git needs to communicate +with an external process multiple times, then this can reduces the process +invocation overhead. Git and the sub-process communicate through stdin and +stdout. + +The sub-processes are kept in a hashmap by command name and looked up +via the subprocess_find_entry function. If an existing instance can not +be found then a new process should be created and started. When the +parent git command terminates, all sub-processes are also terminated. + +This API is based on the run-command API. + +Data structures +--------------- + +* `struct subprocess_entry` + +The sub-process structure. Members should not be accessed directly. + +Types +----- + +'int(*subprocess_start_fn)(struct subprocess_entry *entry)':: + + User-supplied function to initialize the sub-process. This is + typically used to negotiate the interface version and capabilities. + + +Functions +--------- + +`cmd2process_cmp`:: + + Function to test two subprocess hashmap entries for equality. + +`subprocess_start`:: + + Start a subprocess and add it to the subprocess hashmap. + +`subprocess_stop`:: + + Kill a subprocess and remove it from the subprocess hashmap. + +`subprocess_find_entry`:: + + Find a subprocess in the subprocess hashmap. + +`subprocess_get_child_process`:: + + Get the underlying `struct child_process` from a subprocess. + +`subprocess_read_status`:: + + Helper function to read packets looking for the last "status=" + key/value pair. diff --git a/Makefile b/Makefile index 9f8b35ad41..add945b560 100644 --- a/Makefile +++ b/Makefile @@ -838,6 +838,7 @@ LIB_OBJS += streaming.o LIB_OBJS += string-list.o LIB_OBJS += submodule.o LIB_OBJS += submodule-config.o +LIB_OBJS += sub-process.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += tempfile.o diff --git a/convert.c b/convert.c index cfae45aeee..79d04907a5 100644 --- a/convert.c +++ b/convert.c @@ -4,6 +4,7 @@ #include "quote.h" #include "sigchain.h" #include "pkt-line.h" +#include "sub-process.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -496,12 +497,6 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le #define CAP_CLEAN (1u<<0) #define CAP_SMUDGE (1u<<1) -struct subprocess_entry { - struct hashmap_entry ent; /* must be the first member! */ - const char *cmd; - struct child_process process; -}; - struct cmd2process { struct subprocess_entry subprocess; /* must be the first member! */ unsigned int supported_capabilities; @@ -510,65 +505,6 @@ struct cmd2process { static int subprocess_map_initialized; static struct hashmap subprocess_map; -static int cmd2process_cmp(const struct subprocess_entry *e1, - const struct subprocess_entry *e2, - const void *unused) -{ - return strcmp(e1->cmd, e2->cmd); -} - -static struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd) -{ - struct subprocess_entry key; - - hashmap_entry_init(&key, strhash(cmd)); - key.cmd = cmd; - return hashmap_get(hashmap, &key, NULL); -} - -static void subprocess_read_status(int fd, struct strbuf *status) -{ - struct strbuf **pair; - char *line; - for (;;) { - line = packet_read_line(fd, NULL); - if (!line) - break; - pair = strbuf_split_str(line, '=', 2); - if (pair[0] && pair[0]->len && pair[1]) { - /* the last "status=" line wins */ - if (!strcmp(pair[0]->buf, "status=")) { - strbuf_reset(status); - strbuf_addbuf(status, pair[1]); - } - } - strbuf_list_free(pair); - } -} - -static void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry) -{ - if (!entry) - return; - - entry->process.clean_on_exit = 0; - kill(entry->process.pid, SIGTERM); - finish_command(&entry->process); - - hashmap_remove(hashmap, entry, NULL); -} - -static void subprocess_exit_handler(struct child_process *process) -{ - sigchain_push(SIGPIPE, SIG_IGN); - /* Closing the pipe signals the subprocess to initiate a shutdown. */ - close(process->in); - close(process->out); - sigchain_pop(SIGPIPE); - /* Finish command will wait until the shutdown is complete. */ - finish_command(process); -} - static int start_multi_file_filter_fn(struct subprocess_entry *subprocess) { int err; @@ -629,44 +565,6 @@ done: return err; } -typedef int(*subprocess_start_fn)(struct subprocess_entry *entry); -int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, - subprocess_start_fn startfn) -{ - int err; - struct child_process *process; - const char *argv[] = { cmd, NULL }; - - entry->cmd = cmd; - process = &entry->process; - - child_process_init(process); - process->argv = argv; - process->use_shell = 1; - process->in = -1; - process->out = -1; - process->clean_on_exit = 1; - process->clean_on_exit_handler = subprocess_exit_handler; - - err = start_command(process); - if (err) { - error("cannot fork to run subprocess '%s'", cmd); - return err; - } - - hashmap_entry_init(entry, strhash(cmd)); - - err = startfn(entry); - if (err) { - error("initialization for subprocess '%s' failed", cmd); - subprocess_stop(hashmap, entry); - return err; - } - - hashmap_add(hashmap, entry); - return 0; -} - static int apply_multi_file_filter(const char *path, const char *src, size_t len, int fd, struct strbuf *dst, const char *cmd, const unsigned int wanted_capability) diff --git a/sub-process.c b/sub-process.c new file mode 100644 index 0000000000..536b60cced --- /dev/null +++ b/sub-process.c @@ -0,0 +1,102 @@ +/* + * Generic implementation of background process infrastructure. + */ +#include "sub-process.h" +#include "sigchain.h" +#include "pkt-line.h" + +int cmd2process_cmp(const struct subprocess_entry *e1, + const struct subprocess_entry *e2, + const void *unused) +{ + return strcmp(e1->cmd, e2->cmd); +} + +struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd) +{ + struct subprocess_entry key; + + hashmap_entry_init(&key, strhash(cmd)); + key.cmd = cmd; + return hashmap_get(hashmap, &key, NULL); +} + +void subprocess_read_status(int fd, struct strbuf *status) +{ + struct strbuf **pair; + char *line; + for (;;) { + line = packet_read_line(fd, NULL); + if (!line) + break; + pair = strbuf_split_str(line, '=', 2); + if (pair[0] && pair[0]->len && pair[1]) { + /* the last "status=" line wins */ + if (!strcmp(pair[0]->buf, "status=")) { + strbuf_reset(status); + strbuf_addbuf(status, pair[1]); + } + } + strbuf_list_free(pair); + } +} + +void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry) +{ + if (!entry) + return; + + entry->process.clean_on_exit = 0; + kill(entry->process.pid, SIGTERM); + finish_command(&entry->process); + + hashmap_remove(hashmap, entry, NULL); +} + +static void subprocess_exit_handler(struct child_process *process) +{ + sigchain_push(SIGPIPE, SIG_IGN); + /* Closing the pipe signals the subprocess to initiate a shutdown. */ + close(process->in); + close(process->out); + sigchain_pop(SIGPIPE); + /* Finish command will wait until the shutdown is complete. */ + finish_command(process); +} + +int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, + subprocess_start_fn startfn) +{ + int err; + struct child_process *process; + const char *argv[] = { cmd, NULL }; + + entry->cmd = cmd; + process = &entry->process; + + child_process_init(process); + process->argv = argv; + process->use_shell = 1; + process->in = -1; + process->out = -1; + process->clean_on_exit = 1; + process->clean_on_exit_handler = subprocess_exit_handler; + + err = start_command(process); + if (err) { + error("cannot fork to run subprocess '%s'", cmd); + return err; + } + + hashmap_entry_init(entry, strhash(cmd)); + + err = startfn(entry); + if (err) { + error("initialization for subprocess '%s' failed", cmd); + subprocess_stop(hashmap, entry); + return err; + } + + hashmap_add(hashmap, entry); + return 0; +} diff --git a/sub-process.h b/sub-process.h new file mode 100644 index 0000000000..a88e782bfc --- /dev/null +++ b/sub-process.h @@ -0,0 +1,49 @@ +#ifndef SUBPROCESS_H +#define SUBPROCESS_H + +#include "git-compat-util.h" +#include "hashmap.h" +#include "run-command.h" + +/* + * Generic implementation of background process infrastructure. + * See Documentation/technical/api-background-process.txt. + */ + + /* data structures */ + +struct subprocess_entry { + struct hashmap_entry ent; /* must be the first member! */ + const char *cmd; + struct child_process process; +}; + +/* subprocess functions */ + +int cmd2process_cmp(const struct subprocess_entry *e1, + const struct subprocess_entry *e2, const void *unused); + +typedef int(*subprocess_start_fn)(struct subprocess_entry *entry); +int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, + subprocess_start_fn startfn); + +void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry); + +struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd); + +/* subprocess helper functions */ + +static inline struct child_process *subprocess_get_child_process( + struct subprocess_entry *entry) +{ + return &entry->process; +} + +/* + * Helper function that will read packets looking for "status=" + * key/value pairs and return the value from the last "status" packet + */ + +void subprocess_read_status(int fd, struct strbuf *status); + +#endif From 4f2a2e9f0e26c1c543d1f282d6e88b3d0f608d07 Mon Sep 17 00:00:00 2001 From: Ben Peart Date: Fri, 5 May 2017 11:28:02 -0400 Subject: [PATCH 227/566] convert: update subprocess_read_status() to not die on EOF Enable sub-processes to gracefully handle when the process dies by updating subprocess_read_status to return an error on EOF instead of dying. Update apply_multi_file_filter to take advantage of the revised subprocess_read_status. Signed-off-by: Ben Peart Signed-off-by: Junio C Hamano --- convert.c | 10 ++++++++-- sub-process.c | 10 +++++++--- sub-process.h | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/convert.c b/convert.c index 79d04907a5..f1e168bc30 100644 --- a/convert.c +++ b/convert.c @@ -635,7 +635,10 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (err) goto done; - subprocess_read_status(process->out, &filter_status); + err = subprocess_read_status(process->out, &filter_status); + if (err) + goto done; + err = strcmp(filter_status.buf, "success"); if (err) goto done; @@ -644,7 +647,10 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (err) goto done; - subprocess_read_status(process->out, &filter_status); + err = subprocess_read_status(process->out, &filter_status); + if (err) + goto done; + err = strcmp(filter_status.buf, "success"); done: diff --git a/sub-process.c b/sub-process.c index 536b60cced..92f8aea70a 100644 --- a/sub-process.c +++ b/sub-process.c @@ -21,13 +21,15 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch return hashmap_get(hashmap, &key, NULL); } -void subprocess_read_status(int fd, struct strbuf *status) +int subprocess_read_status(int fd, struct strbuf *status) { struct strbuf **pair; char *line; + int len; + for (;;) { - line = packet_read_line(fd, NULL); - if (!line) + len = packet_read_line_gently(fd, NULL, &line); + if ((len < 0) || !line) break; pair = strbuf_split_str(line, '=', 2); if (pair[0] && pair[0]->len && pair[1]) { @@ -39,6 +41,8 @@ void subprocess_read_status(int fd, struct strbuf *status) } strbuf_list_free(pair); } + + return (len < 0) ? len : 0; } void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry) diff --git a/sub-process.h b/sub-process.h index a88e782bfc..7d451e1cde 100644 --- a/sub-process.h +++ b/sub-process.h @@ -44,6 +44,6 @@ static inline struct child_process *subprocess_get_child_process( * key/value pairs and return the value from the last "status" packet */ -void subprocess_read_status(int fd, struct strbuf *status); +int subprocess_read_status(int fd, struct strbuf *status); #endif From 5e68729fd9a17e9e4037abdffa52454a5c64f343 Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Sat, 13 May 2017 11:54:51 +0200 Subject: [PATCH 228/566] doc: use https links to Wikipedia to avoid http redirects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sven Strickroth Reviewed-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/gitweb.txt | 2 +- bisect.c | 2 +- gitweb/gitweb.perl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt index 96156e5e1f..88450589af 100644 --- a/Documentation/gitweb.txt +++ b/Documentation/gitweb.txt @@ -84,7 +84,7 @@ separator (rules for Perl's "`split(" ", $line)`"). * Fields use modified URI encoding, defined in RFC 3986, section 2.1 (Percent-Encoding), or rather "Query string encoding" (see -http://en.wikipedia.org/wiki/Query_string#URL_encoding[]), the difference +https://en.wikipedia.org/wiki/Query_string#URL_encoding[]), the difference being that SP (" ") can be encoded as "{plus}" (and therefore "{plus}" has to be also percent-encoded). + diff --git a/bisect.c b/bisect.c index 30808cadf7..b79c1d464c 100644 --- a/bisect.c +++ b/bisect.c @@ -544,7 +544,7 @@ static unsigned get_prn(unsigned count) { /* * Custom integer square root from - * http://en.wikipedia.org/wiki/Integer_square_root + * https://en.wikipedia.org/wiki/Integer_square_root */ static int sqrti(int val) { diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7cf68f07b7..d8209c7a02 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -8085,7 +8085,7 @@ sub git_search_help {

Pattern is by default a normal string that is matched precisely (but without regard to case, except in the case of pickaxe). However, when you check the re checkbox, the pattern entered is recognized as the POSIX extended -regular expression (also case +regular expression (also case insensitive).

commit
From fdb69d33c43eb56a564a3e1c805dea4b29162667 Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Mon, 15 May 2017 10:32:20 -0700 Subject: [PATCH 229/566] fetch-pack: always allow fetching of literal SHA1s fetch-pack, when fetching a literal SHA-1 from a server that is not configured with uploadpack.allowtipsha1inwant (or similar), always returns an error message of the form "Server does not allow request for unadvertised object %s". However, it is sometimes the case that such object is advertised. This situation would occur, for example, if a user or a script was provided a SHA-1 instead of a branch or tag name for fetching, and wanted to invoke "git fetch" or "git fetch-pack" using that SHA-1. Teach fetch-pack to also check the SHA-1s of the refs in the received ref advertisement if a literal SHA-1 was given by the user. Helped-by: Jeff King Signed-off-by: Jonathan Tan Reviewed-by: Jonathan Nieder Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- fetch-pack.c | 40 ++++++++++++++++++++++++++++++++++++++-- t/t5500-fetch-pack.sh | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index afb8b05024..703e7ec78b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -15,6 +15,7 @@ #include "version.h" #include "prio-queue.h" #include "sha1-array.h" +#include "oidset.h" static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; @@ -592,13 +593,38 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args, } } +static void add_refs_to_oidset(struct oidset *oids, struct ref *refs) +{ + for (; refs; refs = refs->next) + oidset_insert(oids, &refs->old_oid); +} + +static int tip_oids_contain(struct oidset *tip_oids, + struct ref *unmatched, struct ref *newlist, + const struct object_id *id) +{ + /* + * Note that this only looks at the ref lists the first time it's + * called. This works out in filter_refs() because even though it may + * add to "newlist" between calls, the additions will always be for + * oids that are already in the set. + */ + if (!tip_oids->map.tablesize) { + add_refs_to_oidset(tip_oids, unmatched); + add_refs_to_oidset(tip_oids, newlist); + } + return oidset_contains(tip_oids, id); +} + static void filter_refs(struct fetch_pack_args *args, struct ref **refs, struct ref **sought, int nr_sought) { struct ref *newlist = NULL; struct ref **newtail = &newlist; + struct ref *unmatched = NULL; struct ref *ref, *next; + struct oidset tip_oids = OIDSET_INIT; int i; i = 0; @@ -631,7 +657,8 @@ static void filter_refs(struct fetch_pack_args *args, ref->next = NULL; newtail = &ref->next; } else { - free(ref); + ref->next = unmatched; + unmatched = ref; } } @@ -648,7 +675,9 @@ static void filter_refs(struct fetch_pack_args *args, continue; if ((allow_unadvertised_object_request & - (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) { + (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) || + tip_oids_contain(&tip_oids, unmatched, newlist, + &ref->old_oid)) { ref->match_status = REF_MATCHED; *newtail = copy_ref(ref); newtail = &(*newtail)->next; @@ -656,6 +685,13 @@ static void filter_refs(struct fetch_pack_args *args, ref->match_status = REF_UNADVERTISED_NOT_ALLOWED; } } + + oidset_clear(&tip_oids); + for (ref = unmatched; ref; ref = next) { + next = ref->next; + free(ref); + } + *refs = newlist; } diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index b5865b385d..80a1a3239a 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -547,6 +547,41 @@ test_expect_success 'fetch-pack can fetch a raw sha1' ' git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one) ' +test_expect_success 'fetch-pack can fetch a raw sha1 that is advertised as a ref' ' + rm -rf server client && + git init server && + test_commit -C server 1 && + + git init client && + git -C client fetch-pack ../server \ + $(git -C server rev-parse refs/heads/master) +' + +test_expect_success 'fetch-pack can fetch a raw sha1 overlapping a named ref' ' + rm -rf server client && + git init server && + test_commit -C server 1 && + test_commit -C server 2 && + + git init client && + git -C client fetch-pack ../server \ + $(git -C server rev-parse refs/tags/1) refs/tags/1 +' + +test_expect_success 'fetch-pack cannot fetch a raw sha1 that is not advertised as a ref' ' + rm -rf server && + + git init server && + test_commit -C server 5 && + git -C server tag -d 5 && + test_commit -C server 6 && + + git init client && + test_must_fail git -C client fetch-pack ../server \ + $(git -C server rev-parse refs/heads/master^) 2>err && + test_i18ngrep "Server does not allow request for unadvertised object" err +' + check_prot_path () { cat >expected <<-EOF && Diag: url=$1 From 1c002d0a9eb52f8e9cbecb8dce4edd86062df3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 13 May 2017 17:59:46 +0200 Subject: [PATCH 230/566] p0004: simplify calls of test-lazy-init-name-hash The test library puts helpers into $PATH, so we can simply call them without specifying their location. The suffix $X is also not necessary because .exe files on Windows can be started without specifying their extension, and on other platforms it's empty anyway. Signed-off-by: Rene Scharfe Acked-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/perf/p0004-lazy-init-name-hash.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh index 5afa8c8df3..716c951553 100755 --- a/t/perf/p0004-lazy-init-name-hash.sh +++ b/t/perf/p0004-lazy-init-name-hash.sh @@ -7,13 +7,13 @@ test_perf_large_repo test_checkout_worktree test_expect_success 'verify both methods build the same hashmaps' ' - $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --dump --single | sort >out.single && - $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --dump --multi | sort >out.multi && + test-lazy-init-name-hash --dump --single | sort >out.single && + test-lazy-init-name-hash --dump --multi | sort >out.multi && test_cmp out.single out.multi ' test_expect_success 'multithreaded should be faster' ' - $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --perf >out.perf + test-lazy-init-name-hash --perf >out.perf ' test_done From e1ebb569c61d17e2f721f32084fcc7b7394b1692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 13 May 2017 18:00:15 +0200 Subject: [PATCH 231/566] p0004: avoid using pipes The return code of commands on the producing end of a pipe is ignored. Evaluate the outcome of test-lazy-init-name-hash by calling sort separately. Signed-off-by: Rene Scharfe Acked-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/perf/p0004-lazy-init-name-hash.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh index 716c951553..576bdc3c4e 100755 --- a/t/perf/p0004-lazy-init-name-hash.sh +++ b/t/perf/p0004-lazy-init-name-hash.sh @@ -7,9 +7,11 @@ test_perf_large_repo test_checkout_worktree test_expect_success 'verify both methods build the same hashmaps' ' - test-lazy-init-name-hash --dump --single | sort >out.single && - test-lazy-init-name-hash --dump --multi | sort >out.multi && - test_cmp out.single out.multi + test-lazy-init-name-hash --dump --single >out.single && + test-lazy-init-name-hash --dump --multi >out.multi && + sort sorted.single && + sort sorted.multi && + test_cmp sorted.single sorted.multi ' test_expect_success 'multithreaded should be faster' ' From 48a6ace8f5a504cea47d4058744982f2bfb81f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 13 May 2017 18:02:32 +0200 Subject: [PATCH 232/566] p0004: use test_perf The perf test suite (more specifically: t/perf/aggregate.perl) requires each test script to write test results into a file, otherwise it aborts when aggregating. Add actual performance tests with test_perf to allow p0004 to be run together with other perf scripts. Calibrate the value for the parameter --count based on the size of the test repository, in order to get meaningful results with smaller repos yet still be able to finish the script against huge ones without having to wait for hours. Signed-off-by: Rene Scharfe Acked-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/perf/p0004-lazy-init-name-hash.sh | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh index 576bdc3c4e..d30c32f97b 100755 --- a/t/perf/p0004-lazy-init-name-hash.sh +++ b/t/perf/p0004-lazy-init-name-hash.sh @@ -18,4 +18,40 @@ test_expect_success 'multithreaded should be faster' ' test-lazy-init-name-hash --perf >out.perf ' +test_expect_success 'calibrate' ' + entries=$(wc -l Date: Sat, 13 May 2017 18:03:22 +0200 Subject: [PATCH 233/566] p0004: don't abort if multi-threaded is too slow If the single-threaded variant beats the multi-threaded one then we may have a performance bug, but that doesn't justify aborting the test. Drop that check; we can compare the results for --single and --multi using the actual performance tests. Signed-off-by: Rene Scharfe Acked-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/perf/p0004-lazy-init-name-hash.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh index d30c32f97b..3c2135a185 100755 --- a/t/perf/p0004-lazy-init-name-hash.sh +++ b/t/perf/p0004-lazy-init-name-hash.sh @@ -14,10 +14,6 @@ test_expect_success 'verify both methods build the same hashmaps' ' test_cmp sorted.single sorted.multi ' -test_expect_success 'multithreaded should be faster' ' - test-lazy-init-name-hash --perf >out.perf -' - test_expect_success 'calibrate' ' entries=$(wc -l Date: Sat, 13 May 2017 18:03:50 +0200 Subject: [PATCH 234/566] p0004: don't error out if test repo is too small Repositories with less than 4000 entries are always handled using a single thread, causing test-lazy-init-name-hash --multi to error out. Don't abort the whole test script in that case, but simply skip the multi-threaded performance check. We can still use it to compare the single-threaded speed of different versions in that case. Signed-off-by: Rene Scharfe Acked-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/perf/p0004-lazy-init-name-hash.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh index 3c2135a185..8de5a98cfc 100755 --- a/t/perf/p0004-lazy-init-name-hash.sh +++ b/t/perf/p0004-lazy-init-name-hash.sh @@ -8,10 +8,13 @@ test_checkout_worktree test_expect_success 'verify both methods build the same hashmaps' ' test-lazy-init-name-hash --dump --single >out.single && - test-lazy-init-name-hash --dump --multi >out.multi && - sort sorted.single && - sort sorted.multi && - test_cmp sorted.single sorted.multi + if test-lazy-init-name-hash --dump --multi >out.multi + then + test_set_prereq REPO_BIG_ENOUGH_FOR_MULTI && + sort sorted.single && + sort sorted.multi && + test_cmp sorted.single sorted.multi + fi ' test_expect_success 'calibrate' ' @@ -46,7 +49,7 @@ test_perf "single-threaded, $desc" " test-lazy-init-name-hash --single --count=$count " -test_perf "multi-threaded, $desc" " +test_perf REPO_BIG_ENOUGH_FOR_MULTI "multi-threaded, $desc" " test-lazy-init-name-hash --multi --count=$count " From 6489660b4bba7456fac0d0a41f5d6295c5900c5f Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Fri, 12 May 2017 15:38:26 -0700 Subject: [PATCH 235/566] send-email: support validate hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, send-email has support for rudimentary e-mail validation. Allow the user to add support for more validation by providing a sendemail-validate hook. Helped-by: Ævar Arnfjörð Bjarmason Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 1 + Documentation/githooks.txt | 8 +++++++ git-send-email.perl | 20 +++++++++++++++- t/t9001-send-email.sh | 40 ++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 9d66166f69..bb23b02caf 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -377,6 +377,7 @@ have been specified, in which case default to 'compose'. Currently, validation means the following: + -- + * Invoke the sendemail-validate hook if present (see linkgit:githooks[5]). * Warn of patches that contain lines longer than 998 characters; this is due to SMTP limits as described by http://www.ietf.org/rfc/rfc2821.txt. -- diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 706091a569..b2514f4d44 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -447,6 +447,14 @@ rebase:: The commits are guaranteed to be listed in the order that they were processed by rebase. +sendemail-validate +~~~~~~~~~~~~~~~~~~ + +This hook is invoked by 'git send-email'. It takes a single parameter, +the name of the file that holds the e-mail to be sent. Exiting with a +non-zero status causes 'git send-email' to abort before sending any +e-mails. + GIT --- diff --git a/git-send-email.perl b/git-send-email.perl index eea0a517f7..c314cc2b51 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -25,8 +25,9 @@ use Getopt::Long; use Text::ParseWords; use Term::ANSIColor; use File::Temp qw/ tempdir tempfile /; -use File::Spec::Functions qw(catfile); +use File::Spec::Functions qw(catdir catfile); use Error qw(:try); +use Cwd qw(abs_path cwd); use Git; use Git::I18N; @@ -1737,6 +1738,23 @@ sub unique_email_list { sub validate_patch { my $fn = shift; + + my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'), + 'sendemail-validate'); + my $hook_error; + if (-x $validate_hook) { + my $target = abs_path($fn); + # The hook needs a correct cwd and GIT_DIR. + my $cwd_save = cwd(); + chdir($repo->wc_path() or $repo->repo_path()) + or die("chdir: $!"); + local $ENV{"GIT_DIR"} = $repo->repo_path(); + $hook_error = "rejected by sendemail-validate hook" + if system($validate_hook, $target); + chdir($cwd_save) or die("chdir: $!"); + } + return $hook_error if $hook_error; + open(my $fh, '<', $fn) or die sprintf(__("unable to open %s: %s\n"), $fn, $!); while (my $line = <$fh>) { diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 60a80f60b2..15128c755a 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -1913,4 +1913,44 @@ test_expect_success $PREREQ 'leading and trailing whitespaces are removed' ' test_cmp expected-list actual-list ' +test_expect_success $PREREQ 'invoke hook' ' + mkdir -p .git/hooks && + + write_script .git/hooks/sendemail-validate <<-\EOF && + # test that we have the correct environment variable, pwd, and + # argument + case "$GIT_DIR" in + *.git) + true + ;; + *) + false + ;; + esac && + test -f 0001-add-master.patch && + grep "add master" "$1" + EOF + + mkdir subdir && + ( + # Test that it works even if we are not at the root of the + # working tree + cd subdir && + git send-email \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/../fake.sendmail" \ + ../0001-add-master.patch && + + # Verify error message when a patch is rejected by the hook + sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch && + git send-email \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/../fake.sendmail" \ + ../another.patch 2>err + test_i18ngrep "rejected by sendemail-validate hook" err + ) +' + test_done From 10c78a162fa821ee85203165b805ff46be454091 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 May 2017 11:52:09 +0900 Subject: [PATCH 236/566] Start post 2.13 cycle Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 97 +++++++++++++++++++++++++++++++ GIT-VERSION-GEN | 2 +- RelNotes | 2 +- 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 Documentation/RelNotes/2.14.0.txt diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt new file mode 100644 index 0000000000..bf5ed74775 --- /dev/null +++ b/Documentation/RelNotes/2.14.0.txt @@ -0,0 +1,97 @@ +Git 2.14 Release Notes +====================== + +Backward compatibility notes. + + * Use of an empty string as a pathspec element that is used for + 'everything matches' is still warned and Git asks users to use a + more explicit '.' for that instead. The hope is that existing + users will not mind this change, and eventually the warning can be + turned into a hard error, upgrading the deprecation into removal of + this (mis)feature. That is not scheduled to happen in the upcoming + release (yet). + + * Git now avoids blindly falling back to ".git" when the setup + sequence said we are _not_ in Git repository. A corner case that + happens to work right now may be broken by a call to die("BUG"). + We've tried hard to locate such cases and fixed them, but there + might still be cases that need to be addressed--bug reports are + greatly appreciated. + + +Updates since v2.13 +------------------- + +UI, Workflows & Features + + * The colors in which "git status --short --branch" showed the names + of the current branch and its remote-tracking branch are now + configurable. + + * "git clone" learned the "--no-tags" option not to fetch all tags + initially, and also set up the tagopt not to follow any tags in + subsequent fetches. + + * "git archive --format=zip" learned to use zip64 extension when + necessary to go beyond the 4GB limit. + (merge 867e40ff3a rs/large-zip later to maint). + + +Performance, Internal Implementation, Development Support etc. + + * The default packed-git limit value has been raised on larger + platforms to save "git fetch" from a (recoverable) failure while + "gc" is running in parallel. + + * Code to update the cache-tree has been tightened so that we won't + accidentally write out any 0{40} entry in the tree object. + (merge a96d3cc3f6 jk/no-null-sha1-in-cache-tree later to maint). + + * Attempt to allow us notice "fishy" situation where we fail to + remove the temporary directory used during the test. + + * Travis CI gained a task to format the documentation with both + AsciiDoc and AsciiDoctor. + (merge 505ad91304 ls/travis-doc-asciidoctor later to maint). + + * Some platforms have ulong that is smaller than time_t, and our + historical use of ulong for timestamp would mean they cannot + represent some timestamp that the platform allows. Invent a + separate and dedicated timestamp_t (so that we can distingiuish + timestamps and a vanilla ulongs, which along is already a good + move), and then declare uintmax_t is the type to be used as the + timestamp_t. + + +Also contains various documentation updates and code clean-ups. + + +Fixes since v2.13 +----------------- + +Unless otherwise noted, all the fixes since v2.13 in the maintenance +track are contained in this release (see the maintenance releases' +notes for details). + + * "git gc" did not interact well with "git worktree"-managed + per-worktree refs. + + * "git cherry-pick" and other uses of the sequencer machinery + mishandled a trailer block whose last line is an incomplete line. + This has been fixed so that an additional sign-off etc. are added + after completing the existing incomplete line. + (merge 44dc738a39 jt/use-trailer-api-in-commands later to maint). + + * The codepath in "git am" that is used when running "git rebase" + leaked memory held for the log message of the commits being rebased. + (merge 721f5f1e35 jk/am-leakfix later to maint). + + * "git clone --config var=val" is a way to populate the + per-repository configuration file of the new repository, but it did + not work well when val is an empty string. This has been fixed. + (merge db4eca1fea jn/clone-add-empty-config-from-command-line later to maint). + + * Other minor doc, test and build updates and code cleanups. + (merge 515360f9e9 jn/credential-doc-on-clear later to maint). + (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). + (merge e294e8959f jc/apply-fix-mismerge later to maint). diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index afcf9e9abf..4f94fc7574 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.13.0 +DEF_VER=v2.13.GIT LF=' ' diff --git a/RelNotes b/RelNotes index 125bf78f3b..983f6689bd 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes/2.13.0.txt \ No newline at end of file +Documentation/RelNotes/2.14.0.txt \ No newline at end of file From 0624c63ce6a488d3d8bccc4b1e79cc2093e2c4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Tue, 16 May 2017 08:28:46 +0000 Subject: [PATCH 237/566] config: match both symlink & realpath versions in IncludeIf.gitdir:* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the conditional inclusion mechanism to support e.g. gitdir:~/git_tree/repo where ~/git_tree is a symlink to /mnt/stuff/repo. This worked in the initial version of this facility[1], but regressed later in the series while solving a related bug[2]. Now gitdir: will match against the symlinked path (e.g. gitdir:~/git_tree/repo) in addition to the current /mnt/stuff/repo path. Since this is already in a release version note in the documentation that this behavior changed, so users who expect their configuration to work on both v2.13.0 and some future version of git with this fix aren't utterly confused. 1. commit 3efd0bedc6 ("config: add conditional include", 2017-03-01) 2. commit 86f9515708 ("config: resolve symlinks in conditional include's patterns", 2017-04-05) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/config.txt | 10 ++++++++++ config.c | 16 ++++++++++++++++ t/t1305-config-include.sh | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 475e874d51..302b8af192 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -140,6 +140,16 @@ A few more notes on matching via `gitdir` and `gitdir/i`: * Symlinks in `$GIT_DIR` are not resolved before matching. + * Both the symlink & realpath versions of paths will be matched + outside of `$GIT_DIR`. E.g. if ~/git is a symlink to + /mnt/storage/git, both `gitdir:~/git` and `gitdir:/mnt/storage/git` + will match. ++ +This was not the case in the initial release of this feature in +v2.13.0, which only matched the realpath version. Configuration that +wants to be compatible with the initial release of this feature needs +to either specify only the realpath version, or both versions. + * Note that "../" is not special and will match literally, which is unlikely what you want. diff --git a/config.c b/config.c index b4a3205da3..f978428695 100644 --- a/config.c +++ b/config.c @@ -214,6 +214,7 @@ static int include_by_gitdir(const struct config_options *opts, struct strbuf pattern = STRBUF_INIT; int ret = 0, prefix; const char *git_dir; + int already_tried_absolute = 0; if (opts->git_dir) git_dir = opts->git_dir; @@ -226,6 +227,7 @@ static int include_by_gitdir(const struct config_options *opts, strbuf_add(&pattern, cond, cond_len); prefix = prepare_include_condition_pattern(&pattern); +again: if (prefix < 0) goto done; @@ -245,6 +247,20 @@ static int include_by_gitdir(const struct config_options *opts, ret = !wildmatch(pattern.buf + prefix, text.buf + prefix, icase ? WM_CASEFOLD : 0, NULL); + if (!ret && !already_tried_absolute) { + /* + * We've tried e.g. matching gitdir:~/work, but if + * ~/work is a symlink to /mnt/storage/work + * strbuf_realpath() will expand it, so the rule won't + * match. Let's match against a + * strbuf_add_absolute_path() version of the path, + * which'll do the right thing + */ + strbuf_reset(&text); + strbuf_add_absolute_path(&text, git_dir); + already_tried_absolute = 1; + goto again; + } done: strbuf_release(&pattern); strbuf_release(&text); diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index 933915ec06..d9d2f545a4 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh @@ -273,6 +273,29 @@ test_expect_success SYMLINKS 'conditional include, relative path with symlinks' ) ' +test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' ' + ln -s foo bar && + ( + cd bar && + echo "[includeIf \"gitdir:bar/\"]path=bar7" >>.git/config && + echo "[test]seven=7" >.git/bar7 && + echo 7 >expect && + git config test.seven >actual && + test_cmp expect actual + ) +' + +test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icase' ' + ( + cd bar && + echo "[includeIf \"gitdir/i:BAR/\"]path=bar8" >>.git/config && + echo "[test]eight=8" >.git/bar8 && + echo 8 >expect && + git config test.eight >actual && + test_cmp expect actual + ) +' + test_expect_success 'include cycles are detected' ' cat >.gitconfig <<-\EOF && [test]value = gitconfig From 3851e4483f0d62e677703297218c9bb47325806f Mon Sep 17 00:00:00 2001 From: Thomas Gummerer Date: Tue, 16 May 2017 20:59:45 +0100 Subject: [PATCH 238/566] completion: add git stash push When introducing git stash push in f5727e26e4 ("stash: introduce push verb", 2017-02-19), I forgot to add it to the completion code. Add it now. Signed-off-by: Thomas Gummerer Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 21016bf8df..5ce23a2385 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2507,7 +2507,7 @@ _git_show_branch () _git_stash () { local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked' - local subcommands='save list show apply clear drop pop create branch' + local subcommands='push save list show apply clear drop pop create branch' local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then case "$cur" in @@ -2522,6 +2522,9 @@ _git_stash () esac else case "$subcommand,$cur" in + push,--*) + __gitcomp "$save_opts --message" + ;; save,--*) __gitcomp "$save_opts" ;; From c7018be5095f6ec8d2a8f7235cc28207aab64102 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 May 2017 11:52:20 +0900 Subject: [PATCH 239/566] test: allow skipping the remainder Because TAP output does not like to see the remainder of the test getting skipped after running one or more tests, bf4b7219 ("test-lib.sh: Add check for invalid use of 'skip_all' facility", 2012-09-01) made sure that test_done errors out when this happens. Instead, loosen the check so that we only pretend that the rest of the test script did not exist in such a case. We'd lose a bit of information (i.e. TAP does not notice that we are skipping some tests), but not very much (i.e. TAP wasn't told how many tests are skipped anyway). This will allow inclusion of lib-httpd.sh in the middle of a test, which will skip the remainder of the test scripts when tests that involve web server are declined with GIT_TEST_HTTPD=false, for example. Acked-by: Ramsay Jones Signed-off-by: Junio C Hamano --- t/test-lib.sh | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 13b5696822..30eb743719 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -745,20 +745,25 @@ test_done () { fi case "$test_failure" in 0) - # Maybe print SKIP message - if test -n "$skip_all" && test $test_count -gt 0 - then - error "Can't use skip_all after running some tests" - fi - test -z "$skip_all" || skip_all=" # SKIP $skip_all" - if test $test_external_has_tap -eq 0 then if test $test_remaining -gt 0 then say_color pass "# passed all $msg" fi - say "1..$test_count$skip_all" + + # Maybe print SKIP message + test -z "$skip_all" || skip_all="# SKIP $skip_all" + case "$test_count" in + 0) + say "1..$test_count${skip_all:+ $skip_all}" + ;; + *) + test -z "$skip_all" || + say_color warn "$skip_all" + say "1..$test_count" + ;; + esac fi test -d "$remove_trash" && From 2e397e4ddfff1fcb13b41665b9a3aed6bb99eea2 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 16 May 2017 20:11:03 -0700 Subject: [PATCH 240/566] t5545: enhance test coverage when no http server is installed In commit 438fc68462 ("push options: pass push options to the transport helper", 08-02-2017), the test coverage was reduced to run no tests at all if you lack a http server. Move the http initialization to the end, such that only http tests are skipped when a http server is missing. The test in between that tests submodule propagation is safe to run before the http tests as it makes its own test directories `parent` and `parent_upstream`. Noticed-by: Ramsay Jones Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- t/t5545-push-options.sh | 85 +++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh index f9232f5d0f..90a4b0d2fe 100755 --- a/t/t5545-push-options.sh +++ b/t/t5545-push-options.sh @@ -3,8 +3,6 @@ test_description='pushing to a repository using push options' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-httpd.sh -start_httpd mk_repo_pair () { rm -rf workbench upstream && @@ -102,46 +100,6 @@ test_expect_success 'two push options work' ' test_cmp expect upstream/.git/hooks/post-receive.push_options ' -test_expect_success 'push option denied properly by http server' ' - test_when_finished "rm -rf test_http_clone" && - test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" && - mk_repo_pair && - git -C upstream config receive.advertisePushOptions false && - git -C upstream config http.receivepack true && - cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && - git clone "$HTTPD_URL"/smart/upstream test_http_clone && - test_commit -C test_http_clone one && - test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual && - test_i18ngrep "the receiving end does not support push options" actual && - git -C test_http_clone push origin master -' - -test_expect_success 'push options work properly across http' ' - test_when_finished "rm -rf test_http_clone" && - test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" && - mk_repo_pair && - git -C upstream config receive.advertisePushOptions true && - git -C upstream config http.receivepack true && - cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && - git clone "$HTTPD_URL"/smart/upstream test_http_clone && - - test_commit -C test_http_clone one && - git -C test_http_clone push origin master && - git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect && - git -C test_http_clone rev-parse --verify master >actual && - test_cmp expect actual && - - test_commit -C test_http_clone two && - git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin master && - printf "asdf\nmore structured text\n" >expect && - test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options && - test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/post-receive.push_options && - - git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect && - git -C test_http_clone rev-parse --verify master >actual && - test_cmp expect actual -' - test_expect_success 'push options and submodules' ' test_when_finished "rm -rf parent" && test_when_finished "rm -rf parent_upstream" && @@ -182,6 +140,49 @@ test_expect_success 'push options and submodules' ' test_cmp expect parent_upstream/.git/hooks/post-receive.push_options ' +. "$TEST_DIRECTORY"/lib-httpd.sh +start_httpd + +test_expect_success 'push option denied properly by http server' ' + test_when_finished "rm -rf test_http_clone" && + test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" && + mk_repo_pair && + git -C upstream config receive.advertisePushOptions false && + git -C upstream config http.receivepack true && + cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && + git clone "$HTTPD_URL"/smart/upstream test_http_clone && + test_commit -C test_http_clone one && + test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual && + test_i18ngrep "the receiving end does not support push options" actual && + git -C test_http_clone push origin master +' + +test_expect_success 'push options work properly across http' ' + test_when_finished "rm -rf test_http_clone" && + test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" && + mk_repo_pair && + git -C upstream config receive.advertisePushOptions true && + git -C upstream config http.receivepack true && + cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && + git clone "$HTTPD_URL"/smart/upstream test_http_clone && + + test_commit -C test_http_clone one && + git -C test_http_clone push origin master && + git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect && + git -C test_http_clone rev-parse --verify master >actual && + test_cmp expect actual && + + test_commit -C test_http_clone two && + git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin master && + printf "asdf\nmore structured text\n" >expect && + test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options && + test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/post-receive.push_options && + + git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect && + git -C test_http_clone rev-parse --verify master >actual && + test_cmp expect actual +' + stop_httpd test_done From 71406ed4d6d21fbe0ee0e068159739a7476f3039 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 18 May 2017 01:02:09 -0400 Subject: [PATCH 241/566] t5400: avoid concurrent writes into a trace file One test in t5400 examines the packet exchange between git-push and git-receive-pack. The latter inherits the GIT_TRACE_PACKET environment variable, so that both processes dump trace data into the same file concurrently. This should not be a problem because the trace file is opened with O_APPEND. On Windows, however, O_APPEND is not atomic as it should be: it is emulated as lseek(SEEK_END) followed by write(). For this reason, the test is unreliable: it can happen that one process overwrites a line that was just written by the other process. As a consequence, the test sometimes does not find one or another line that is expected (and it is also successful occasionally). The test case is actually only interested in the output of git-push. To ensure that only git-push writes to the trace file, override the receive-pack command such that it does not even open the trace file. Reported-by: Johannes Sixt Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t5400-send-pack.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 3331e0f534..d375d7110d 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -288,7 +288,10 @@ test_expect_success 'receive-pack de-dupes .have lines' ' $shared .have EOF - GIT_TRACE_PACKET=$(pwd)/trace git push fork HEAD:foo && + GIT_TRACE_PACKET=$(pwd)/trace \ + git push \ + --receive-pack="unset GIT_TRACE_PACKET; git-receive-pack" \ + fork HEAD:foo && extract_ref_advertisement refs && test_cmp expect refs ' From d76650b8d16c9e5e7b6ee94e6922a3b99be74746 Mon Sep 17 00:00:00 2001 From: Brian Malehorn Date: Mon, 15 May 2017 23:06:49 -0700 Subject: [PATCH 242/566] interpret-trailers: honor the cut line If a commit message is edited with the "verbose" option, the buffer will have a cut line and diff after the log message, like so: my subject # ------------------------ >8 ------------------------ # Do not touch the line above. # Everything below will be removed. diff --git a/foo.txt b/foo.txt index 5716ca5..7601807 100644 --- a/foo.txt +++ b/foo.txt @@ -1 +1 @@ -bar +baz "git interpret-trailers" is unaware of the cut line, and assumes the trailer block would be at the end of the whole thing. This can easily be seen with: $ GIT_EDITOR='git interpret-trailers --in-place --trailer Acked-by:me' \ git commit --amend -v Teach "git interpret-trailers" to notice the cut-line and ignore the remainder of the input when looking for a place to add new trailer block. This makes it consistent with how "git commit -v -s" inserts a new Signed-off-by: line. This can be done by the same logic as the existing helper function, wt_status_truncate_message_at_cut_line(), uses, but it wants the caller to pass a strbuf to it. Because the function ignore_non_trailer() used by the command takes a pair, not a strbuf, steal the logic from wt_status_truncate_message_at_cut_line() to create a new wt_status_locate_end() helper function that takes pair, and make ignore_non_trailer() call it to help "interpret-trailers". Since there is only one caller of wt_status_truncate_message_at_cut_line() in cmd_commit(), rewrite it to call wt_status_locate_end() helper instead and remove the old helper that no longer has any caller. Signed-off-by: Brian Malehorn Signed-off-by: Junio C Hamano --- builtin/commit.c | 2 +- commit.c | 13 +++++++------ t/t7513-interpret-trailers.sh | 17 +++++++++++++++++ wt-status.c | 11 ++++++----- wt-status.h | 2 +- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index 1d805f5da8..8d1cac0629 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1735,7 +1735,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (verbose || /* Truncate the message just before the diff, if any. */ cleanup_mode == CLEANUP_SCISSORS) - wt_status_truncate_message_at_cut_line(&sb); + strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len)); if (cleanup_mode != CLEANUP_NONE) strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL); diff --git a/commit.c b/commit.c index 73c78c2b80..c14ddf191f 100644 --- a/commit.c +++ b/commit.c @@ -11,6 +11,7 @@ #include "commit-slab.h" #include "prio-queue.h" #include "sha1-lookup.h" +#include "wt-status.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@ -1648,10 +1649,9 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len /* * Inspect the given string and determine the true "end" of the log message, in * order to find where to put a new Signed-off-by: line. Ignored are - * trailing comment lines and blank lines, and also the traditional - * "Conflicts:" block that is not commented out, so that we can use - * "git commit -s --amend" on an existing commit that forgot to remove - * it. + * trailing comment lines and blank lines. To support "git commit -s + * --amend" on an existing commit, we also ignore "Conflicts:". To + * support "git commit -v", we truncate at cut lines. * * Returns the number of bytes from the tail to ignore, to be fed as * the second parameter to append_signoff(). @@ -1661,8 +1661,9 @@ int ignore_non_trailer(const char *buf, size_t len) int boc = 0; int bol = 0; int in_old_conflicts_block = 0; + size_t cutoff = wt_status_locate_end(buf, len); - while (bol < len) { + while (bol < cutoff) { const char *next_line = memchr(buf + bol, '\n', len - bol); if (!next_line) @@ -1688,5 +1689,5 @@ int ignore_non_trailer(const char *buf, size_t len) } bol = next_line - buf; } - return boc ? len - boc : 0; + return boc ? len - boc : len - cutoff; } diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh index 4dd1d7c520..0c6f91c433 100755 --- a/t/t7513-interpret-trailers.sh +++ b/t/t7513-interpret-trailers.sh @@ -1258,4 +1258,21 @@ test_expect_success 'with no command and no key' ' test_cmp expected actual ' +test_expect_success 'with cut line' ' + cat >expected <<-\EOF && + my subject + + review: Brian + sign: A U Thor + # ------------------------ >8 ------------------------ + ignore this + EOF + git interpret-trailers --trailer review:Brian >actual <<-\EOF && + my subject + # ------------------------ >8 ------------------------ + ignore this + EOF + test_cmp expected actual +' + test_done diff --git a/wt-status.c b/wt-status.c index 0375484962..f4e198e5ca 100644 --- a/wt-status.c +++ b/wt-status.c @@ -896,17 +896,18 @@ conclude: status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); } -void wt_status_truncate_message_at_cut_line(struct strbuf *buf) +size_t wt_status_locate_end(const char *s, size_t len) { const char *p; struct strbuf pattern = STRBUF_INIT; strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line); - if (starts_with(buf->buf, pattern.buf + 1)) - strbuf_setlen(buf, 0); - else if ((p = strstr(buf->buf, pattern.buf))) - strbuf_setlen(buf, p - buf->buf + 1); + if (starts_with(s, pattern.buf + 1)) + len = 0; + else if ((p = strstr(s, pattern.buf))) + len = p - s + 1; strbuf_release(&pattern); + return len; } void wt_status_add_cut_line(FILE *fp) diff --git a/wt-status.h b/wt-status.h index 6018c627b1..8a3864783b 100644 --- a/wt-status.h +++ b/wt-status.h @@ -112,7 +112,7 @@ struct wt_status_state { unsigned char cherry_pick_head_sha1[20]; }; -void wt_status_truncate_message_at_cut_line(struct strbuf *); +size_t wt_status_locate_end(const char *s, size_t len); void wt_status_add_cut_line(FILE *fp); void wt_status_prepare(struct wt_status *s); void wt_status_print(struct wt_status *s); From 5589e87fd8d2a74201157dad34d66fdcf7ce41a3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 May 2017 14:39:43 +0900 Subject: [PATCH 243/566] name-rev: change a "long" variable to timestamp_t Earlier dddbad72 ("timestamp_t: a new data type for timestamps", 2017-04-26) updated all in-core variables, fields and function return values that are used to store "seconds since epoch" to a new type timestamp_t. Unfortunately one variable "cutoff", which is used to keep track of the oldest timestamp of commit we saw on the command line, was "long" and left behind. Update it to timestamp_t as well. Signed-off-by: Junio C Hamano --- builtin/name-rev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 4437475017..1f6fcae121 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -15,7 +15,7 @@ typedef struct rev_name { int distance; } rev_name; -static long cutoff = LONG_MAX; +static timestamp_t cutoff = TIME_MAX; /* How many generations are maximally preferred over _one_ merge traversal? */ #define MERGE_TRAVERSAL_WEIGHT 65535 From 613a0e52ea11c4496e3e154de192486a18eac2e4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 02:12:12 -0400 Subject: [PATCH 244/566] ref-filter: resolve HEAD when parsing %(HEAD) atom If the user asks to display (or sort by) the %(HEAD) atom, ref-filter has to compare each refname to the value of HEAD. We do so by resolving HEAD fresh when calling populate_value() on each ref. If there are a large number of refs, this can have a measurable impact on runtime. Instead, let's resolve HEAD once when we realize we need the %(HEAD) atom, allowing us to do a simple string comparison for each ref. On a repository with 3000 branches (high, but an actual example found in the wild) this drops the best-of-five time to run "git branch >/dev/null" from 59ms to 48ms (~20% savings). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- ref-filter.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ref-filter.c b/ref-filter.c index 2a94d6da98..f4c6823727 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -92,6 +92,7 @@ static struct used_atom { unsigned int length; } objectname; struct refname_atom refname; + char *head; } u; } *used_atom; static int used_atom_cnt, need_tagged, need_symref; @@ -286,6 +287,12 @@ static void if_atom_parser(struct used_atom *atom, const char *arg) } } +static void head_atom_parser(struct used_atom *atom, const char *arg) +{ + unsigned char unused[GIT_SHA1_RAWSZ]; + + atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, unused, NULL); +} static struct { const char *name; @@ -324,7 +331,7 @@ static struct { { "push", FIELD_STR, remote_ref_atom_parser }, { "symref", FIELD_STR, refname_atom_parser }, { "flag" }, - { "HEAD" }, + { "HEAD", FIELD_STR, head_atom_parser }, { "color", FIELD_STR, color_atom_parser }, { "align", FIELD_STR, align_atom_parser }, { "end" }, @@ -1366,12 +1373,7 @@ static void populate_value(struct ref_array_item *ref) } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) { continue; } else if (!strcmp(name, "HEAD")) { - const char *head; - unsigned char sha1[20]; - - head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - sha1, NULL); - if (head && !strcmp(ref->refname, head)) + if (atom->u.head && !strcmp(ref->refname, atom->u.head)) v->s = "*"; else v->s = " "; From 4ab867b8fc8c19ae99c5414ffcbf57fb1609c94f Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Thu, 18 May 2017 11:02:32 +0100 Subject: [PATCH 245/566] rebase -i: fix reflog message When rebase -i was converted to C a bug was introduced into the code that creates the reflog message. Instead of saying rebase -i (finish): onto it says rebase -i (finish): onto as the strbuf is not reset between reading the value of and . Signed-off-by: Phillip Wood Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- sequencer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sequencer.c b/sequencer.c index d76dc9cb2b..907319d089 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2058,6 +2058,7 @@ cleanup_head_ref: res = error(_("could not read orig-head")); goto cleanup_head_ref; } + strbuf_reset(&buf); if (!read_oneliner(&buf, rebase_path_onto(), 0)) { res = error(_("could not read 'onto'")); goto cleanup_head_ref; From 79a62269815264165da265eeb8a65feb47dcf671 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Thu, 18 May 2017 11:02:33 +0100 Subject: [PATCH 246/566] rebase -i: silence stash apply The shell version of rebase -i silences the status output from 'git stash apply' when restoring the autostashed changes. The C version does not. Having the output from git stash apply on the screen is distracting as it makes it difficult to find the message from git rebase saying that the rebase succeeded. Also the status information that git stash prints talks about looking in .git/rebase-merge/done to see which commits have been applied. As .git/rebase-merge is removed shortly after the message is printed before rebase -i exits this is confusing. Signed-off-by: Phillip Wood Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- sequencer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sequencer.c b/sequencer.c index 907319d089..2456246822 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1898,6 +1898,8 @@ static int apply_autostash(struct replay_opts *opts) strbuf_trim(&stash_sha1); child.git_cmd = 1; + child.no_stdout = 1; + child.no_stderr = 1; argv_array_push(&child.args, "stash"); argv_array_push(&child.args, "apply"); argv_array_push(&child.args, stash_sha1.buf); From d096d7f1ef81f5c52d0c9d3070ad82b66de9c981 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Fri, 19 May 2017 15:32:48 +0100 Subject: [PATCH 247/566] rebase -i: add missing newline to end of message The message that's printed when auto-stashed changes are successfully restored was missing '\n' at the end. Signed-off-by: Phillip Wood Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- sequencer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sequencer.c b/sequencer.c index 2456246822..dc7573940a 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1904,7 +1904,7 @@ static int apply_autostash(struct replay_opts *opts) argv_array_push(&child.args, "apply"); argv_array_push(&child.args, stash_sha1.buf); if (!run_command(&child)) - printf(_("Applied autostash.")); + printf(_("Applied autostash.\n")); else { struct child_process store = CHILD_PROCESS_INIT; From edf3b90553f5c667cd8cb99aa809305470ba3bd7 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 8 May 2017 11:41:42 +0200 Subject: [PATCH 248/566] unpack-trees: preserve index extensions Make git checkout (and other unpack_tree operations) preserve the untracked cache. This is valuable for two reasons: 1. Often, an unpack_tree operation will not touch large parts of the working tree, and thus most of the untracked cache will continue to be valid. 2. Even if the untracked cache were entirely invalidated by such an operation, the user has signaled their intention to have such a cache, and we don't want to throw it away. [jes: backed out the watchman-specific parts] Signed-off-by: David Turner Signed-off-by: Ben Peart Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- cache.h | 1 + read-cache.c | 6 ++++++ t/t7063-status-untracked-cache.sh | 22 ++++++++++++++++++++++ unpack-trees.c | 1 + 4 files changed, 30 insertions(+) diff --git a/cache.h b/cache.h index e1f0e182ad..d41336dfd5 100644 --- a/cache.h +++ b/cache.h @@ -597,6 +597,7 @@ extern int read_index_unmerged(struct index_state *); #define CLOSE_LOCK (1 << 1) extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags); extern int discard_index(struct index_state *); +extern void move_index_extensions(struct index_state *dst, struct index_state *src); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change); diff --git a/read-cache.c b/read-cache.c index 0d0081a11b..79827a4d71 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2628,3 +2628,9 @@ void stat_validity_update(struct stat_validity *sv, int fd) fill_stat_data(sv->sd, &st); } } + +void move_index_extensions(struct index_state *dst, struct index_state *src) +{ + dst->untracked = src->untracked; + src->untracked = NULL; +} diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh index 0667bd9dd3..e5fb892f95 100755 --- a/t/t7063-status-untracked-cache.sh +++ b/t/t7063-status-untracked-cache.sh @@ -661,4 +661,26 @@ test_expect_success 'test ident field is working' ' test_i18ncmp ../expect ../err ' +test_expect_success 'untracked cache survives a checkout' ' + git commit --allow-empty -m empty && + test-dump-untracked-cache >../before && + test_when_finished "git checkout master" && + git checkout -b other_branch && + test-dump-untracked-cache >../after && + test_cmp ../before ../after && + test_commit test && + test-dump-untracked-cache >../before && + git checkout master && + test-dump-untracked-cache >../after && + test_cmp ../before ../after +' + +test_expect_success 'untracked cache survives a commit' ' + test-dump-untracked-cache >../before && + git add done/two && + git commit -m commit && + test-dump-untracked-cache >../after && + test_cmp ../before ../after +' + test_done diff --git a/unpack-trees.c b/unpack-trees.c index aa15111fef..17117bd0fd 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1391,6 +1391,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options WRITE_TREE_SILENT | WRITE_TREE_REPAIR); } + move_index_extensions(&o->result, o->dst_index); discard_index(o->dst_index); *o->dst_index = o->result; } else { From ba4dce784e4db4c00c5d54bb22c239080049b3ee Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Thu, 18 May 2017 22:44:51 -0400 Subject: [PATCH 249/566] config.txt: add an entry for log.showSignature The configuration variable log.showSignature is mentioned in git-log's manpage. Document it in git-config's manpage as well. Signed-off-by: Kyle Meyer Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index e43d147825..ef8e5b9458 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2057,6 +2057,10 @@ log.showRoot:: Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which normally hide the root commit will now show it. True by default. +log.showSignature:: + If true, makes linkgit:git-log[1], linkgit:git-show[1], and + linkgit:git-whatchanged[1] assume `--show-signature`. + log.mailmap:: If true, makes linkgit:git-log[1], linkgit:git-show[1], and linkgit:git-whatchanged[1] assume `--use-mailmap`. From a3ba6bf10abcaed1819d27fa45675c3a0e141f95 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 20 May 2017 04:30:25 -0400 Subject: [PATCH 250/566] revision.c: ignore broken tags with ignore_missing_links When peeling a tag for prepare_revision_walk(), we do not respect the ignore_missing_links flag. This can lead to a bogus error when pack-objects walks the possibly-broken unreachable-but-recent part of the object graph. The other link-following all happens via traverse_commit_list(), which explains why this case was missed. And our tests covered only broken links from commits. Let's be more comprehensive and cover broken tree entries (which do work) and tags (which shows off this bug). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 2 +- t/t6501-freshen-objects.sh | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/revision.c b/revision.c index 8a29cb03c5..7d57d440a1 100644 --- a/revision.c +++ b/revision.c @@ -230,7 +230,7 @@ static struct commit *handle_commit(struct rev_info *revs, die("bad tag"); object = parse_object(tag->tagged->oid.hash); if (!object) { - if (flags & UNINTERESTING) + if (revs->ignore_missing_links || (flags & UNINTERESTING)) return NULL; die("bad object %s", oid_to_hex(&tag->tagged->oid)); } diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh index cf076dcd94..394b169ead 100755 --- a/t/t6501-freshen-objects.sh +++ b/t/t6501-freshen-objects.sh @@ -129,7 +129,7 @@ for repack in '' true; do ' done -test_expect_success 'do not complain about existing broken links' ' +test_expect_success 'do not complain about existing broken links (commit)' ' cat >broken-commit <<-\EOF && tree 0000000000000000000000000000000000000001 parent 0000000000000000000000000000000000000002 @@ -144,4 +144,29 @@ test_expect_success 'do not complain about existing broken links' ' test_must_be_empty stderr ' +test_expect_success 'do not complain about existing broken links (tree)' ' + cat >broken-tree <<-\EOF && + 100644 blob 0000000000000000000000000000000000000003 foo + EOF + tree=$(git mktree --missing stderr && + git cat-file -e $tree && + test_must_be_empty stderr +' + +test_expect_success 'do not complain about existing broken links (tag)' ' + cat >broken-tag <<-\EOF && + object 0000000000000000000000000000000000000004 + type commit + tag broken + tagger whatever 1234 -0000 + + this is a broken tag + EOF + tag=$(git hash-object -t tag -w broken-tag) && + git gc 2>stderr && + git cat-file -e $tag && + test_must_be_empty stderr +' + test_done From 0ead000c3aca13a10ae51a3c74c866981e0d33b8 Mon Sep 17 00:00:00 2001 From: Dennis Kaarsemaker Date: Fri, 24 Mar 2017 22:37:32 +0100 Subject: [PATCH 251/566] send-email: Net::SMTP::SSL is obsolete, use only when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Net::SMTP itself can do the necessary SSL and STARTTLS bits just fine since version 1.28, and Net::SMTP::SSL is now deprecated. Since 1.28 isn't that old yet, keep the old code in place and use it when necessary. While we're in the area, mark some messages for translation that were not yet marked as such. Signed-off-by: Dennis Kaarsemaker Reviewed-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-send-email.perl | 54 +++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index eea0a517f7..0d90439d9a 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1353,10 +1353,12 @@ EOF die __("The required SMTP server is not properly defined.") } + require Net::SMTP; + my $use_net_smtp_ssl = version->parse($Net::SMTP::VERSION) < version->parse("1.28"); + $smtp_domain ||= maildomain(); + if ($smtp_encryption eq 'ssl') { $smtp_server_port ||= 465; # ssmtp - require Net::SMTP::SSL; - $smtp_domain ||= maildomain(); require IO::Socket::SSL; # Suppress "variable accessed once" warning. @@ -1368,34 +1370,48 @@ EOF # Net::SMTP::SSL->new() does not forward any SSL options IO::Socket::SSL::set_client_defaults( ssl_verify_params()); - $smtp ||= Net::SMTP::SSL->new($smtp_server, - Hello => $smtp_domain, - Port => $smtp_server_port, - Debug => $debug_net_smtp); + + if ($use_net_smtp_ssl) { + require Net::SMTP::SSL; + $smtp ||= Net::SMTP::SSL->new($smtp_server, + Hello => $smtp_domain, + Port => $smtp_server_port, + Debug => $debug_net_smtp); + } + else { + $smtp ||= Net::SMTP->new($smtp_server, + Hello => $smtp_domain, + Port => $smtp_server_port, + Debug => $debug_net_smtp, + SSL => 1); + } } else { - require Net::SMTP; - $smtp_domain ||= maildomain(); $smtp_server_port ||= 25; $smtp ||= Net::SMTP->new($smtp_server, Hello => $smtp_domain, Debug => $debug_net_smtp, Port => $smtp_server_port); if ($smtp_encryption eq 'tls' && $smtp) { - require Net::SMTP::SSL; - $smtp->command('STARTTLS'); - $smtp->response(); - if ($smtp->code == 220) { + if ($use_net_smtp_ssl) { + $smtp->command('STARTTLS'); + $smtp->response(); + if ($smtp->code != 220) { + die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message); + } + require Net::SMTP::SSL; $smtp = Net::SMTP::SSL->start_SSL($smtp, ssl_verify_params()) - or die "STARTTLS failed! ".IO::Socket::SSL::errstr(); - $smtp_encryption = ''; - # Send EHLO again to receive fresh - # supported commands - $smtp->hello($smtp_domain); - } else { - die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message); + or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr()); } + else { + $smtp->starttls(ssl_verify_params()) + or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr()); + } + $smtp_encryption = ''; + # Send EHLO again to receive fresh + # supported commands + $smtp->hello($smtp_domain); } } From 072473e6593783a291a7bb5249086985268e6b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:04 +0000 Subject: [PATCH 252/566] Makefile & configure: reword inaccurate comment about PCRE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reword an outdated & inaccurate comment which suggests that only git-grep can use PCRE. This comment was added back when PCRE support was initially added in commit 63e7e9d8b6 ("git-grep: Learn PCRE", 2011-05-09), and was true at the time. It hasn't been telling the full truth since git-log learned to use PCRE with --grep in commit 727b6fc3ed ("log --grep: accept --basic-regexp and --perl-regexp", 2012-10-03), and more importantly is likely to get more inaccurate over time as more use is made of PCRE in other areas. Reword it to be more future-proof, and to more clearly explain that this enables user-initiated runtime behavior. Copy/pasting this so much in configure.ac is lame, these Makefile-like flags aren't even used by autoconf, just the corresponding --with[out]-* options. But copy/pasting the comments that make sense for the Makefile to configure.ac where they make less sense is the pattern everything else follows in that file. I'm not going to war against that as part of this change, just following the existing pattern. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 6 ++++-- configure.ac | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e35542e631..eedadb8056 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,10 @@ all:: # Define NO_OPENSSL environment variable if you do not have OpenSSL. # This also implies BLK_SHA1. # -# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be -# able to use Perl-compatible regular expressions. +# Define USE_LIBPCRE if you have and want to use libpcre. Various +# commands such as log and grep offer runtime options to use +# Perl-compatible regular expressions instead of standard or extended +# POSIX regular expressions. # # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in # /foo/bar/include and /foo/bar/lib directories. diff --git a/configure.ac b/configure.ac index 128165529f..deeb968daa 100644 --- a/configure.ac +++ b/configure.ac @@ -250,8 +250,10 @@ AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)]) AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]), GIT_PARSE_WITH([openssl])) -# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be -# able to use Perl-compatible regular expressions. +# Define USE_LIBPCRE if you have and want to use libpcre. Various +# commands such as log and grep offer runtime options to use +# Perl-compatible regular expressions instead of standard or extended +# POSIX regular expressions. # # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in # /foo/bar/include and /foo/bar/lib directories. @@ -499,8 +501,10 @@ GIT_CONF_SUBST([NEEDS_SSL_WITH_CRYPTO]) GIT_CONF_SUBST([NO_OPENSSL]) # -# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be -# able to use Perl-compatible regular expressions. +# Define USE_LIBPCRE if you have and want to use libpcre. Various +# commands such as log and grep offer runtime options to use +# Perl-compatible regular expressions instead of standard or extended +# POSIX regular expressions. # if test -n "$USE_LIBPCRE"; then From d048cb13c237655d7637c36b5cbe1eed263987c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:05 +0000 Subject: [PATCH 253/566] grep & rev-list doc: stop promising libpcre for --perl-regexp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop promising in our grep & rev-list options documentation that we're always going to be using libpcre when given the --perl-regexp option. Instead talk about using "Perl-compatible regular expressions" and using these types of patterns using "a compile-time dependency". Saying "libpcre" means that we're talking about libpcre.so, which is always going to be v1. This change is part of an ongoing saga to add support for libpcre2, which comes with PCRE v2. In the future we might use some completely unrelated library to provide perl-compatible regular expression support. By wording the documentation differently and not promising any specific version of PCRE or even PCRE at all we have more wiggle room to change the implementation. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/git-grep.txt | 7 +++++-- Documentation/rev-list-options.txt | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 71f32f3508..5033483db4 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -161,8 +161,11 @@ OPTIONS -P:: --perl-regexp:: - Use Perl-compatible regexp for patterns. Requires libpcre to be - compiled in. + Use Perl-compatible regular expressions for patterns. ++ +Support for these types of regular expressions is an optional +compile-time dependency. If Git wasn't compiled with support for them +providing this option will cause it to die. -F:: --fixed-strings:: diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index a02f7324c0..a46f70c2b1 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -92,8 +92,12 @@ endif::git-rev-list[] pattern as a regular expression). --perl-regexp:: - Consider the limiting patterns to be Perl-compatible regular expressions. - Requires libpcre to be compiled in. + Consider the limiting patterns to be Perl-compatible regular + expressions. ++ +Support for these types of regular expressions is an optional +compile-time dependency. If Git wasn't compiled with support for them +providing this option will cause it to die. --remove-empty:: Stop when a given path disappears from the tree. From 3eb585c112a75c1b2d1ecf0e98e4c2ad12750afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:06 +0000 Subject: [PATCH 254/566] test-lib: rename the LIBPCRE prerequisite to PCRE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the LIBPCRE prerequisite to PCRE. This is for preparation for libpcre2 support, where having just "LIBPCRE" would be confusing as it implies v1 of the library. None of these tests are incompatible between versions 1 & 2 of libpcre, it's less confusing to give them a more general name to make it clear that they work on both library versions. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/README | 4 ++-- t/t7810-grep.sh | 28 ++++++++++++++-------------- t/t7812-grep-icase-non-ascii.sh | 4 ++-- t/t7813-grep-icase-iso.sh | 2 +- t/test-lib.sh | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/t/README b/t/README index ab386c3681..a90cb62583 100644 --- a/t/README +++ b/t/README @@ -803,9 +803,9 @@ use these, and "test_set_prereq" for how to define your own. Test is not run by root user, and an attempt to write to an unwritable file is expected to fail correctly. - - LIBPCRE + - PCRE - Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests + Git was compiled with support for PCRE. Wrap any tests that use git-grep --perl-regexp or git-grep -P in these. - CASE_INSENSITIVE_FS diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index cee42097b0..c84c4d99f9 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -275,7 +275,7 @@ do test_cmp expected actual ' - test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" ' + test_expect_success PCRE "grep $L with grep.patterntype=perl" ' echo "${HC}ab:a+b*c" >expected && git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" $H ab >actual && test_cmp expected actual @@ -1053,12 +1053,12 @@ hello.c:int main(int argc, const char **argv) hello.c: printf("Hello world.\n"); EOF -test_expect_success LIBPCRE 'grep --perl-regexp pattern' ' +test_expect_success PCRE 'grep --perl-regexp pattern' ' git grep --perl-regexp "\p{Ps}.*?\p{Pe}" hello.c >actual && test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -P pattern' ' +test_expect_success PCRE 'grep -P pattern' ' git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual && test_cmp expected actual ' @@ -1070,13 +1070,13 @@ test_expect_success 'grep pattern with grep.extendedRegexp=true' ' test_cmp empty actual ' -test_expect_success LIBPCRE 'grep -P pattern with grep.extendedRegexp=true' ' +test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' ' git -c grep.extendedregexp=true \ grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual && test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -P -v pattern' ' +test_expect_success PCRE 'grep -P -v pattern' ' { echo "ab:a+b*c" echo "ab:a+bc" @@ -1085,7 +1085,7 @@ test_expect_success LIBPCRE 'grep -P -v pattern' ' test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -P -i pattern' ' +test_expect_success PCRE 'grep -P -i pattern' ' cat >expected <<-EOF && hello.c: printf("Hello world.\n"); EOF @@ -1093,7 +1093,7 @@ test_expect_success LIBPCRE 'grep -P -i pattern' ' test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -P -w pattern' ' +test_expect_success PCRE 'grep -P -w pattern' ' { echo "hello_world:Hello world" echo "hello_world:HeLLo world" @@ -1118,11 +1118,11 @@ test_expect_success 'grep invalidpattern properly dies with grep.patternType=ext test_must_fail git -c grep.patterntype=extended grep "a[" ' -test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' ' +test_expect_success PCRE 'grep -P invalidpattern properly dies ' ' test_must_fail git grep -P "a[" ' -test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' ' +test_expect_success PCRE 'grep invalidpattern properly dies with grep.patternType=perl' ' test_must_fail git -c grep.patterntype=perl grep "a[" ' @@ -1191,13 +1191,13 @@ test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =e test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -G -F -E -P pattern' ' +test_expect_success PCRE 'grep -G -F -E -P pattern' ' echo "d0:0" >expected && git grep -G -F -E -P "[\d]" d0 >actual && test_cmp expected actual ' -test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' ' +test_expect_success PCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' ' echo "d0:0" >expected && git \ -c grep.patterntype=fixed \ @@ -1208,7 +1208,7 @@ test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, = test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' ' +test_expect_success PCRE 'grep -P pattern with grep.patternType=fixed' ' echo "ab:a+b*c" >expected && git \ -c grep.patterntype=fixed \ @@ -1343,12 +1343,12 @@ space: line with leading space2 space: line with leading space3 EOF -test_expect_success LIBPCRE 'grep -E "^ "' ' +test_expect_success PCRE 'grep -E "^ "' ' git grep -E "^ " space >actual && test_cmp expected actual ' -test_expect_success LIBPCRE 'grep -P "^ "' ' +test_expect_success PCRE 'grep -P "^ "' ' git grep -P "^ " space >actual && test_cmp expected actual ' diff --git a/t/t7812-grep-icase-non-ascii.sh b/t/t7812-grep-icase-non-ascii.sh index 169fd8d706..04a61cb8e0 100755 --- a/t/t7812-grep-icase-non-ascii.sh +++ b/t/t7812-grep-icase-non-ascii.sh @@ -20,13 +20,13 @@ test_expect_success REGEX_LOCALE 'grep literal string, no -F' ' git grep -i "TILRAUN: HALLÓ HEIMUR!" ' -test_expect_success GETTEXT_LOCALE,LIBPCRE 'grep pcre utf-8 icase' ' +test_expect_success GETTEXT_LOCALE,PCRE 'grep pcre utf-8 icase' ' git grep --perl-regexp "TILRAUN: H.lló Heimur!" && git grep --perl-regexp -i "TILRAUN: H.lló Heimur!" && git grep --perl-regexp -i "TILRAUN: H.LLÓ HEIMUR!" ' -test_expect_success GETTEXT_LOCALE,LIBPCRE 'grep pcre utf-8 string with "+"' ' +test_expect_success GETTEXT_LOCALE,PCRE 'grep pcre utf-8 string with "+"' ' test_write_lines "TILRAUN: Hallóó Heimur!" >file2 && git add file2 && git grep -l --perl-regexp "TILRAUN: H.lló+ Heimur!" >actual && diff --git a/t/t7813-grep-icase-iso.sh b/t/t7813-grep-icase-iso.sh index efef7fb81f..701e08a8e5 100755 --- a/t/t7813-grep-icase-iso.sh +++ b/t/t7813-grep-icase-iso.sh @@ -11,7 +11,7 @@ test_expect_success GETTEXT_ISO_LOCALE 'setup' ' export LC_ALL ' -test_expect_success GETTEXT_ISO_LOCALE,LIBPCRE 'grep pcre string' ' +test_expect_success GETTEXT_ISO_LOCALE,PCRE 'grep pcre string' ' git grep --perl-regexp -i "TILRAUN: H.ll Heimur!" && git grep --perl-regexp -i "TILRAUN: H.LL HEIMUR!" ' diff --git a/t/test-lib.sh b/t/test-lib.sh index 13b5696822..6abf1d1918 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1010,7 +1010,7 @@ esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PYTHON" && test_set_prereq PYTHON -test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE +test -n "$USE_LIBPCRE" && test_set_prereq PCRE test -z "$NO_GETTEXT" && test_set_prereq GETTEXT # Can we rely on git's output in the C locale? From 9df46763ef1ee46f2dc1de768b1403552779d011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:07 +0000 Subject: [PATCH 255/566] log: add exhaustive tests for pattern style options & config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add exhaustive tests for how the different grep.patternType options & the corresponding command-line options affect git-log. Before this change it was possible to patch revision.c so that the --basic-regexp option was synonymous with --extended-regexp, and --perl-regexp wasn't recognized at all, and still have 100% of the test suite pass. This was because the first test being modified here, added in commit 34a4ae55b2 ("log --grep: use the same helper to set -E/-F options as "git grep"", 2012-10-03), didn't actually check whether we'd enabled extended regular expressions as distinct from re-toggling non-fixed string support. Fix that by changing the pattern to a pattern that'll only match if --extended-regexp option is provided, but won't match under the default --basic-regexp option. Other potential regressions were possible since there were no tests for the rest of the combinations of grep.patternType configuration toggles & corresponding git-log command-line options. Add exhaustive tests for those. The patterns being passed to fixed/basic/extended/PCRE are carefully crafted to return the wrong thing if the grep engine were to pick any other matching method than the one it's told to use. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t4202-log.sh | 98 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/t/t4202-log.sh b/t/t4202-log.sh index f577990716..a8dce0ca2d 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -262,7 +262,30 @@ test_expect_success 'log --grep -i' ' test_expect_success 'log -F -E --grep= uses ere' ' echo second >expect && - git log -1 --pretty="tformat:%s" -F -E --grep=s.c.nd >actual && + # basic would need \(s\) to do the same + git log -1 --pretty="tformat:%s" -F -E --grep="(s).c.nd" >actual && + test_cmp expect actual +' + +test_expect_success PCRE 'log -F -E --perl-regexp --grep= uses PCRE' ' + test_when_finished "rm -rf num_commits" && + git init num_commits && + ( + cd num_commits && + test_commit 1d && + test_commit 2e + ) && + + # In PCRE \d in [\d] is like saying "0-9", and matches the 2 + # in 2e... + echo 2e >expect && + git -C num_commits log -1 --pretty="tformat:%s" -F -E --perl-regexp --grep="[\d]" >actual && + test_cmp expect actual && + + # ...in POSIX basic and extended it is the same as [d], + # i.e. "d", which matches 1d, but does not match 2e. + echo 1d >expect && + git -C num_commits log -1 --pretty="tformat:%s" -F -E --grep="[\d]" >actual && test_cmp expect actual ' @@ -280,6 +303,79 @@ test_expect_success 'log with grep.patternType configuration and command line' ' test_cmp expect actual ' +test_expect_success 'log with various grep.patternType configurations & command-lines' ' + git init pattern-type && + ( + cd pattern-type && + test_commit 1 file A && + + # The tagname is overridden here because creating a + # tag called "(1|2)" as test_commit would otherwise + # implicitly do would fail on e.g. MINGW. + test_commit "(1|2)" file B 2 && + + echo "(1|2)" >expect.fixed && + cp expect.fixed expect.basic && + cp expect.fixed expect.extended && + cp expect.fixed expect.perl && + + # A strcmp-like match with fixed. + git -c grep.patternType=fixed log --pretty=tformat:%s \ + --grep="(1|2)" >actual.fixed && + + # POSIX basic matches (, | and ) literally. + git -c grep.patternType=basic log --pretty=tformat:%s \ + --grep="(.|.)" >actual.basic && + + # POSIX extended needs to have | escaped to match it + # literally, whereas under basic this is the same as + # (|2), i.e. it would also match "1". This test checks + # for extended by asserting that it is not matching + # what basic would match. + git -c grep.patternType=extended log --pretty=tformat:%s \ + --grep="\|2" >actual.extended && + if test_have_prereq PCRE + then + # Only PCRE would match [\d]\| with only + # "(1|2)" due to [\d]. POSIX basic would match + # both it and "1" since similarly to the + # extended match above it is the same as + # \([\d]\|\). POSIX extended would + # match neither. + git -c grep.patternType=perl log --pretty=tformat:%s \ + --grep="[\d]\|" >actual.perl && + test_cmp expect.perl actual.perl + fi && + test_cmp expect.fixed actual.fixed && + test_cmp expect.basic actual.basic && + test_cmp expect.extended actual.extended && + + git log --pretty=tformat:%s -F \ + --grep="(1|2)" >actual.fixed.short-arg && + git log --pretty=tformat:%s -E \ + --grep="\|2" >actual.extended.short-arg && + test_cmp expect.fixed actual.fixed.short-arg && + test_cmp expect.extended actual.extended.short-arg && + + git log --pretty=tformat:%s --fixed-strings \ + --grep="(1|2)" >actual.fixed.long-arg && + git log --pretty=tformat:%s --basic-regexp \ + --grep="(.|.)" >actual.basic.long-arg && + git log --pretty=tformat:%s --extended-regexp \ + --grep="\|2" >actual.extended.long-arg && + if test_have_prereq PCRE + then + git log --pretty=tformat:%s --perl-regexp \ + --grep="[\d]\|" >actual.perl.long-arg && + test_cmp expect.perl actual.perl.long-arg + + fi && + test_cmp expect.fixed actual.fixed.long-arg && + test_cmp expect.basic actual.basic.long-arg && + test_cmp expect.extended actual.extended.long-arg + ) +' + cat > expect < Date: Sat, 20 May 2017 21:42:08 +0000 Subject: [PATCH 256/566] log: make --regexp-ignore-case work with --perl-regexp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the --regexp-ignore-case option work with --perl-regexp. This never worked, and there was no test for this. Fix the bug and add a test. When PCRE support was added in commit 63e7e9d8b6 ("git-grep: Learn PCRE", 2011-05-09) compile_pcre_regexp() would only check opt->ignore_case, but when the --perl-regexp option was added in commit 727b6fc3ed ("log --grep: accept --basic-regexp and --perl-regexp", 2012-10-03) the code didn't set the opt->ignore_case. Change the test suite to test for -i and --invert-regexp with basic/extended/perl patterns in addition to fixed, which was the only patternType that was tested for before in combination with those options. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- revision.c | 1 + t/t4202-log.sh | 60 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/revision.c b/revision.c index 7ff61ff5f7..c96265d89d 100644 --- a/revision.c +++ b/revision.c @@ -1991,6 +1991,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) { revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE; } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) { + revs->grep_filter.ignore_case = 1; revs->grep_filter.regflags |= REG_ICASE; DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE); } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) { diff --git a/t/t4202-log.sh b/t/t4202-log.sh index a8dce0ca2d..547f4c19a7 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -231,14 +231,47 @@ second initial EOF test_expect_success 'log --invert-grep --grep' ' - git log --pretty="tformat:%s" --invert-grep --grep=th --grep=Sec >actual && - test_cmp expect actual + # Fixed + git -c grep.patternType=fixed log --pretty="tformat:%s" --invert-grep --grep=th --grep=Sec >actual && + test_cmp expect actual && + + # POSIX basic + git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual && + test_cmp expect actual && + + # POSIX extended + git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual && + test_cmp expect actual && + + # PCRE + if test_have_prereq PCRE + then + git -c grep.patternType=perl log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual && + test_cmp expect actual + fi ' test_expect_success 'log --invert-grep --grep -i' ' echo initial >expect && - git log --pretty="tformat:%s" --invert-grep -i --grep=th --grep=Sec >actual && - test_cmp expect actual + + # Fixed + git -c grep.patternType=fixed log --pretty="tformat:%s" --invert-grep -i --grep=th --grep=Sec >actual && + test_cmp expect actual && + + # POSIX basic + git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep -i --grep=t[h] --grep=S[e]c >actual && + test_cmp expect actual && + + # POSIX extended + git -c grep.patternType=extended log --pretty="tformat:%s" --invert-grep -i --grep=t[h] --grep=S[e]c >actual && + test_cmp expect actual && + + # PCRE + if test_have_prereq PCRE + then + git -c grep.patternType=perl log --pretty="tformat:%s" --invert-grep -i --grep=t[h] --grep=S[e]c >actual && + test_cmp expect actual + fi ' test_expect_success 'log --grep option parsing' ' @@ -256,8 +289,25 @@ test_expect_success 'log -i --grep' ' test_expect_success 'log --grep -i' ' echo Second >expect && + + # Fixed git log -1 --pretty="tformat:%s" --grep=sec -i >actual && - test_cmp expect actual + test_cmp expect actual && + + # POSIX basic + git -c grep.patternType=basic log -1 --pretty="tformat:%s" --grep=s[e]c -i >actual && + test_cmp expect actual && + + # POSIX extended + git -c grep.patternType=extended log -1 --pretty="tformat:%s" --grep=s[e]c -i >actual && + test_cmp expect actual && + + # PCRE + if test_have_prereq PCRE + then + git -c grep.patternType=perl log -1 --pretty="tformat:%s" --grep=s[e]c -i >actual && + test_cmp expect actual + fi ' test_expect_success 'log -F -E --grep= uses ere' ' From 9001c1920c9b812efc671bdc0c313d0a3418d8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:09 +0000 Subject: [PATCH 257/566] grep: add a test asserting that --perl-regexp dies when !PCRE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test asserting that when --perl-regexp (and -P for grep) is given to git-grep & git-log that we die with an error. In developing the PCRE v2 series I introduced a regression where -P would (through control-flow fall-through) become synonymous with basic POSIX matching. I.e. 'git grep -P '[\d]' would match "d" instead of digits. The entire test suite would still pass with this serious regression, since everything that tested for --perl-regexp would be guarded by the PCRE prerequisite, fix that blind-spot by adding tests under !PCRE asserting that git must die when given --perl-regexp or -P. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t4202-log.sh | 4 +++- t/t7810-grep.sh | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 547f4c19a7..dbed3efeee 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -418,7 +418,9 @@ test_expect_success 'log with various grep.patternType configurations & command- git log --pretty=tformat:%s --perl-regexp \ --grep="[\d]\|" >actual.perl.long-arg && test_cmp expect.perl actual.perl.long-arg - + else + test_must_fail git log --perl-regexp \ + --grep="[\d]\|" fi && test_cmp expect.fixed actual.fixed.long-arg && test_cmp expect.basic actual.basic.long-arg && diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index c84c4d99f9..8d69113695 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -281,6 +281,10 @@ do test_cmp expected actual ' + test_expect_success !PCRE "grep $L with grep.patterntype=perl errors without PCRE" ' + test_must_fail git -c grep.patterntype=perl grep "foo.*bar" + ' + test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" ' echo "${HC}ab:abc" >expected && git \ @@ -1058,11 +1062,19 @@ test_expect_success PCRE 'grep --perl-regexp pattern' ' test_cmp expected actual ' +test_expect_success !PCRE 'grep --perl-regexp pattern errors without PCRE' ' + test_must_fail git grep --perl-regexp "foo.*bar" +' + test_expect_success PCRE 'grep -P pattern' ' git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual && test_cmp expected actual ' +test_expect_success !PCRE 'grep -P pattern errors without PCRE' ' + test_must_fail git grep -P "foo.*bar" +' + test_expect_success 'grep pattern with grep.extendedRegexp=true' ' >empty && test_must_fail git -c grep.extendedregexp=true \ From 4aeb720d3f997bf09cff2c8c92f74da400950b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:10 +0000 Subject: [PATCH 258/566] grep: add a test for backreferences in PCRE patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test for backreferences such as (.)\1 in PCRE patterns. This test ensures that the PCRE_NO_AUTO_CAPTURE option isn't turned on. Before this change turning it on would break these sort of patterns, but wouldn't break any tests. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7810-grep.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 8d69113695..daa906b9b0 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -1114,6 +1114,13 @@ test_expect_success PCRE 'grep -P -w pattern' ' test_cmp expected actual ' +test_expect_success PCRE 'grep -P backreferences work (the PCRE NO_AUTO_CAPTURE flag is not set)' ' + git grep -P -h "(?P.)(?P=one)" hello_world >actual && + test_cmp hello_world actual && + git grep -P -h "(.)\1" hello_world >actual && + test_cmp hello_world actual +' + test_expect_success 'grep -G invalidpattern properly dies ' ' test_must_fail git grep -G "a[" ' From e01b4dab01e4d0c8ae57647279c1075ec56d2b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:11 +0000 Subject: [PATCH 259/566] grep: change non-ASCII -i test to stop using --debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change a non-ASCII case-insensitive test case to stop using --debug, and instead simply test for the expected results. The test coverage remains the same with this change, but the test won't break due to internal refactoring. This test was added in commit 793dc676e0 ("grep/icase: avoid kwsset when -F is specified", 2016-06-25). It was asserting that the regex must be compiled with compile_fixed_regexp(), instead test for the expected results, allowing the underlying implementation to change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7812-grep-icase-non-ascii.sh | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/t/t7812-grep-icase-non-ascii.sh b/t/t7812-grep-icase-non-ascii.sh index 04a61cb8e0..0059a1f837 100755 --- a/t/t7812-grep-icase-non-ascii.sh +++ b/t/t7812-grep-icase-non-ascii.sh @@ -36,29 +36,14 @@ test_expect_success GETTEXT_LOCALE,PCRE 'grep pcre utf-8 string with "+"' ' ' test_expect_success REGEX_LOCALE 'grep literal string, with -F' ' - git grep --debug -i -F "TILRAUN: Halló Heimur!" 2>&1 >/dev/null | - grep fixed >debug1 && - test_write_lines "fixed TILRAUN: Halló Heimur!" >expect1 && - test_cmp expect1 debug1 && - - git grep --debug -i -F "TILRAUN: HALLÓ HEIMUR!" 2>&1 >/dev/null | - grep fixed >debug2 && - test_write_lines "fixed TILRAUN: HALLÓ HEIMUR!" >expect2 && - test_cmp expect2 debug2 + git grep -i -F "TILRAUN: Halló Heimur!" && + git grep -i -F "TILRAUN: HALLÓ HEIMUR!" ' test_expect_success REGEX_LOCALE 'grep string with regex, with -F' ' - test_write_lines "^*TILR^AUN:.* \\Halló \$He[]imur!\$" >file && - - git grep --debug -i -F "^*TILR^AUN:.* \\Halló \$He[]imur!\$" 2>&1 >/dev/null | - grep fixed >debug1 && - test_write_lines "fixed \\^*TILR^AUN:\\.\\* \\\\Halló \$He\\[]imur!\\\$" >expect1 && - test_cmp expect1 debug1 && - - git grep --debug -i -F "^*TILR^AUN:.* \\HALLÓ \$HE[]IMUR!\$" 2>&1 >/dev/null | - grep fixed >debug2 && - test_write_lines "fixed \\^*TILR^AUN:\\.\\* \\\\HALLÓ \$HE\\[]IMUR!\\\$" >expect2 && - test_cmp expect2 debug2 + test_write_lines "TILRAUN: Halló Heimur [abc]!" >file3 && + git add file3 && + git grep -i -F "TILRAUN: Halló Heimur [abc]!" file3 ' test_expect_success REGEX_LOCALE 'pickaxe -i on non-ascii' ' From c5813658f762792bcd0f418ba0dac50f0626d2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:12 +0000 Subject: [PATCH 260/566] grep: add tests for --threads=N and grep.threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests for --threads=N being supplied on the command-line, or when grep.threads=N being supplied in the configuration. When the threading support was made run-time configurable in commit 89f09dd34e ("grep: add --threads= option and grep.threads configuration", 2015-12-15) no tests were added for it. In developing a change to the grep code I was able to make '--threads=1 ` segfault, while the test suite still passed. This change fixes that blind spot in the tests. In addition to asserting that asking for N threads shouldn't segfault, test that the grep output given any N is the same. The choice to test only 1..10 as opposed to 1..8 or 1..16 or whatever is arbitrary. Testing 1..1024 works locally for me (but gets noticeably slower as more threads are spawned). Given the structure of the code there's no reason to test an arbitrary number of threads, only 0, 1 and >=2 are special modes of operation. A later patch introduces a PTHREADS test prerequisite which is true under NO_PTHREADS=UnfortunatelyYes, but even under NO_PTHREADS it's fine to test --threads=N, we'll just ignore it and not use threading. So these tests also make sense under that mode to assert that --threads=N without pthreads still returns expected results. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7810-grep.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index daa906b9b0..561709ef6a 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -775,6 +775,22 @@ test_expect_success 'grep -W with userdiff' ' test_cmp expected actual ' +for threads in $(test_seq 0 10) +do + test_expect_success "grep --threads=$threads & -c grep.threads=$threads" " + git grep --threads=$threads . >actual.$threads && + if test $threads -ge 1 + then + test_cmp actual.\$(($threads - 1)) actual.$threads + fi && + git -c grep.threads=$threads grep . >actual.$threads && + if test $threads -ge 1 + then + test_cmp actual.\$(($threads - 1)) actual.$threads + fi + " +done + test_expect_success 'grep from a subdirectory to search wider area (1)' ' mkdir -p s && ( From 5d52a30edaa8bbfd502e88250e3170673b07204b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:13 +0000 Subject: [PATCH 261/566] grep: amend submodule recursion test for regex engine testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend the submodule recursion test to prepare it for subsequent tests of whether it passes along the grep.patternType to the submodule greps. This is the result of searching & replacing: foobar -> (1|2)d(3|4) foo -> (1|2) bar -> (3|4) Currently there's no tests for whether e.g. -P or -E is correctly passed along, tests for that will be added in a follow-up change, but first add content to the tests which will match differently under different regex engines. Reuse the pattern established in an earlier commit of mine in this series ("log: add exhaustive tests for pattern style options & config", 2017-04-07). The pattern "(.|.)[\d]" will match this content differently under fixed/basic/extended & perl. This test code was originally added in commit 0281e487fd ("grep: optionally recurse into submodules", 2016-12-16). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7814-grep-recurse-submodules.sh | 166 ++++++++++++++--------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 5b6eb3a65e..1472855e1d 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -9,13 +9,13 @@ submodules. . ./test-lib.sh test_expect_success 'setup directory structure and submodule' ' - echo "foobar" >a && + echo "(1|2)d(3|4)" >a && mkdir b && - echo "bar" >b/b && + echo "(3|4)" >b/b && git add a b && git commit -m "add a and b" && git init submodule && - echo "foobar" >submodule/a && + echo "(1|2)d(3|4)" >submodule/a && git -C submodule add a && git -C submodule commit -m "add a" && git submodule add ./submodule && @@ -24,18 +24,18 @@ test_expect_success 'setup directory structure and submodule' ' test_expect_success 'grep correctly finds patterns in a submodule' ' cat >expect <<-\EOF && - a:foobar - b/b:bar - submodule/a:foobar + a:(1|2)d(3|4) + b/b:(3|4) + submodule/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules >actual && + git grep -e "(3|4)" --recurse-submodules >actual && test_cmp expect actual ' test_expect_success 'grep and basic pathspecs' ' cat >expect <<-\EOF && - submodule/a:foobar + submodule/a:(1|2)d(3|4) EOF git grep -e. --recurse-submodules -- submodule >actual && @@ -44,7 +44,7 @@ test_expect_success 'grep and basic pathspecs' ' test_expect_success 'grep and nested submodules' ' git init submodule/sub && - echo "foobar" >submodule/sub/a && + echo "(1|2)d(3|4)" >submodule/sub/a && git -C submodule/sub add a && git -C submodule/sub commit -m "add a" && git -C submodule submodule add ./sub && @@ -54,117 +54,117 @@ test_expect_success 'grep and nested submodules' ' git commit -m "updated submodule" && cat >expect <<-\EOF && - a:foobar - b/b:bar - submodule/a:foobar - submodule/sub/a:foobar + a:(1|2)d(3|4) + b/b:(3|4) + submodule/a:(1|2)d(3|4) + submodule/sub/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules >actual && + git grep -e "(3|4)" --recurse-submodules >actual && test_cmp expect actual ' test_expect_success 'grep and multiple patterns' ' cat >expect <<-\EOF && - a:foobar - submodule/a:foobar - submodule/sub/a:foobar + a:(1|2)d(3|4) + submodule/a:(1|2)d(3|4) + submodule/sub/a:(1|2)d(3|4) EOF - git grep -e "bar" --and -e "foo" --recurse-submodules >actual && + git grep -e "(3|4)" --and -e "(1|2)" --recurse-submodules >actual && test_cmp expect actual ' test_expect_success 'grep and multiple patterns' ' cat >expect <<-\EOF && - b/b:bar + b/b:(3|4) EOF - git grep -e "bar" --and --not -e "foo" --recurse-submodules >actual && + git grep -e "(3|4)" --and --not -e "(1|2)" --recurse-submodules >actual && test_cmp expect actual ' test_expect_success 'basic grep tree' ' cat >expect <<-\EOF && - HEAD:a:foobar - HEAD:b/b:bar - HEAD:submodule/a:foobar - HEAD:submodule/sub/a:foobar + HEAD:a:(1|2)d(3|4) + HEAD:b/b:(3|4) + HEAD:submodule/a:(1|2)d(3|4) + HEAD:submodule/sub/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD >actual && + git grep -e "(3|4)" --recurse-submodules HEAD >actual && test_cmp expect actual ' test_expect_success 'grep tree HEAD^' ' cat >expect <<-\EOF && - HEAD^:a:foobar - HEAD^:b/b:bar - HEAD^:submodule/a:foobar + HEAD^:a:(1|2)d(3|4) + HEAD^:b/b:(3|4) + HEAD^:submodule/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD^ >actual && + git grep -e "(3|4)" --recurse-submodules HEAD^ >actual && test_cmp expect actual ' test_expect_success 'grep tree HEAD^^' ' cat >expect <<-\EOF && - HEAD^^:a:foobar - HEAD^^:b/b:bar + HEAD^^:a:(1|2)d(3|4) + HEAD^^:b/b:(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD^^ >actual && + git grep -e "(3|4)" --recurse-submodules HEAD^^ >actual && test_cmp expect actual ' test_expect_success 'grep tree and pathspecs' ' cat >expect <<-\EOF && - HEAD:submodule/a:foobar - HEAD:submodule/sub/a:foobar + HEAD:submodule/a:(1|2)d(3|4) + HEAD:submodule/sub/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD -- submodule >actual && + git grep -e "(3|4)" --recurse-submodules HEAD -- submodule >actual && test_cmp expect actual ' test_expect_success 'grep tree and pathspecs' ' cat >expect <<-\EOF && - HEAD:submodule/a:foobar - HEAD:submodule/sub/a:foobar + HEAD:submodule/a:(1|2)d(3|4) + HEAD:submodule/sub/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD -- "submodule*a" >actual && + git grep -e "(3|4)" --recurse-submodules HEAD -- "submodule*a" >actual && test_cmp expect actual ' test_expect_success 'grep tree and more pathspecs' ' cat >expect <<-\EOF && - HEAD:submodule/a:foobar + HEAD:submodule/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD -- "submodul?/a" >actual && + git grep -e "(3|4)" --recurse-submodules HEAD -- "submodul?/a" >actual && test_cmp expect actual ' test_expect_success 'grep tree and more pathspecs' ' cat >expect <<-\EOF && - HEAD:submodule/sub/a:foobar + HEAD:submodule/sub/a:(1|2)d(3|4) EOF - git grep -e "bar" --recurse-submodules HEAD -- "submodul*/sub/a" >actual && + git grep -e "(3|4)" --recurse-submodules HEAD -- "submodul*/sub/a" >actual && test_cmp expect actual ' test_expect_success !MINGW 'grep recurse submodule colon in name' ' git init parent && test_when_finished "rm -rf parent" && - echo "foobar" >"parent/fi:le" && + echo "(1|2)d(3|4)" >"parent/fi:le" && git -C parent add "fi:le" && git -C parent commit -m "add fi:le" && git init "su:b" && test_when_finished "rm -rf su:b" && - echo "foobar" >"su:b/fi:le" && + echo "(1|2)d(3|4)" >"su:b/fi:le" && git -C "su:b" add "fi:le" && git -C "su:b" commit -m "add fi:le" && @@ -172,30 +172,30 @@ test_expect_success !MINGW 'grep recurse submodule colon in name' ' git -C parent commit -m "add submodule" && cat >expect <<-\EOF && - fi:le:foobar - su:b/fi:le:foobar + fi:le:(1|2)d(3|4) + su:b/fi:le:(1|2)d(3|4) EOF - git -C parent grep -e "foobar" --recurse-submodules >actual && + git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual && test_cmp expect actual && cat >expect <<-\EOF && - HEAD:fi:le:foobar - HEAD:su:b/fi:le:foobar + HEAD:fi:le:(1|2)d(3|4) + HEAD:su:b/fi:le:(1|2)d(3|4) EOF - git -C parent grep -e "foobar" --recurse-submodules HEAD >actual && + git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules HEAD >actual && test_cmp expect actual ' test_expect_success 'grep history with moved submoules' ' git init parent && test_when_finished "rm -rf parent" && - echo "foobar" >parent/file && + echo "(1|2)d(3|4)" >parent/file && git -C parent add file && git -C parent commit -m "add file" && git init sub && test_when_finished "rm -rf sub" && - echo "foobar" >sub/file && + echo "(1|2)d(3|4)" >sub/file && git -C sub add file && git -C sub commit -m "add file" && @@ -203,82 +203,82 @@ test_expect_success 'grep history with moved submoules' ' git -C parent commit -m "add submodule" && cat >expect <<-\EOF && - dir/sub/file:foobar - file:foobar + dir/sub/file:(1|2)d(3|4) + file:(1|2)d(3|4) EOF - git -C parent grep -e "foobar" --recurse-submodules >actual && + git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual && test_cmp expect actual && git -C parent mv dir/sub sub-moved && git -C parent commit -m "moved submodule" && cat >expect <<-\EOF && - file:foobar - sub-moved/file:foobar + file:(1|2)d(3|4) + sub-moved/file:(1|2)d(3|4) EOF - git -C parent grep -e "foobar" --recurse-submodules >actual && + git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual && test_cmp expect actual && cat >expect <<-\EOF && - HEAD^:dir/sub/file:foobar - HEAD^:file:foobar + HEAD^:dir/sub/file:(1|2)d(3|4) + HEAD^:file:(1|2)d(3|4) EOF - git -C parent grep -e "foobar" --recurse-submodules HEAD^ >actual && + git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules HEAD^ >actual && test_cmp expect actual ' test_expect_success 'grep using relative path' ' test_when_finished "rm -rf parent sub" && git init sub && - echo "foobar" >sub/file && + echo "(1|2)d(3|4)" >sub/file && git -C sub add file && git -C sub commit -m "add file" && git init parent && - echo "foobar" >parent/file && + echo "(1|2)d(3|4)" >parent/file && git -C parent add file && mkdir parent/src && - echo "foobar" >parent/src/file2 && + echo "(1|2)d(3|4)" >parent/src/file2 && git -C parent add src/file2 && git -C parent submodule add ../sub && git -C parent commit -m "add files and submodule" && # From top works cat >expect <<-\EOF && - file:foobar - src/file2:foobar - sub/file:foobar + file:(1|2)d(3|4) + src/file2:(1|2)d(3|4) + sub/file:(1|2)d(3|4) EOF - git -C parent grep --recurse-submodules -e "foobar" >actual && + git -C parent grep --recurse-submodules -e "(1|2)d(3|4)" >actual && test_cmp expect actual && # Relative path to top cat >expect <<-\EOF && - ../file:foobar - file2:foobar - ../sub/file:foobar + ../file:(1|2)d(3|4) + file2:(1|2)d(3|4) + ../sub/file:(1|2)d(3|4) EOF - git -C parent/src grep --recurse-submodules -e "foobar" -- .. >actual && + git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" -- .. >actual && test_cmp expect actual && # Relative path to submodule cat >expect <<-\EOF && - ../sub/file:foobar + ../sub/file:(1|2)d(3|4) EOF - git -C parent/src grep --recurse-submodules -e "foobar" -- ../sub >actual && + git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" -- ../sub >actual && test_cmp expect actual ' test_expect_success 'grep from a subdir' ' test_when_finished "rm -rf parent sub" && git init sub && - echo "foobar" >sub/file && + echo "(1|2)d(3|4)" >sub/file && git -C sub add file && git -C sub commit -m "add file" && git init parent && mkdir parent/src && - echo "foobar" >parent/src/file && + echo "(1|2)d(3|4)" >parent/src/file && git -C parent add src/file && git -C parent submodule add ../sub src/sub && git -C parent submodule add ../sub sub && @@ -286,19 +286,19 @@ test_expect_success 'grep from a subdir' ' # Verify grep from root works cat >expect <<-\EOF && - src/file:foobar - src/sub/file:foobar - sub/file:foobar + src/file:(1|2)d(3|4) + src/sub/file:(1|2)d(3|4) + sub/file:(1|2)d(3|4) EOF - git -C parent grep --recurse-submodules -e "foobar" >actual && + git -C parent grep --recurse-submodules -e "(1|2)d(3|4)" >actual && test_cmp expect actual && # Verify grep from a subdir works cat >expect <<-\EOF && - file:foobar - sub/file:foobar + file:(1|2)d(3|4) + sub/file:(1|2)d(3|4) EOF - git -C parent/src grep --recurse-submodules -e "foobar" >actual && + git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" >actual && test_cmp expect actual ' From 5ee6f1a21b60be8a7c7d24ad6aa60939ca5efbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:14 +0000 Subject: [PATCH 262/566] grep: add tests for grep pattern types being passed to submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add testing for grep pattern types being correctly passed to submodules. The pattern "(.|.)[\d]" matches differently under fixed (not at all), and then matches different lines under basic/extended & perl regular expressions, so this change asserts that the pattern type is passed along correctly. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7814-grep-recurse-submodules.sh | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 1472855e1d..3a58197f47 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -313,4 +313,53 @@ test_incompatible_with_recurse_submodules () test_incompatible_with_recurse_submodules --untracked test_incompatible_with_recurse_submodules --no-index +test_expect_success 'grep --recurse-submodules should pass the pattern type along' ' + # Fixed + test_must_fail git grep -F --recurse-submodules -e "(.|.)[\d]" && + test_must_fail git -c grep.patternType=fixed grep --recurse-submodules -e "(.|.)[\d]" && + + # Basic + git grep -G --recurse-submodules -e "(.|.)[\d]" >actual && + cat >expect <<-\EOF && + a:(1|2)d(3|4) + submodule/a:(1|2)d(3|4) + submodule/sub/a:(1|2)d(3|4) + EOF + test_cmp expect actual && + git -c grep.patternType=basic grep --recurse-submodules -e "(.|.)[\d]" >actual && + test_cmp expect actual && + + # Extended + git grep -E --recurse-submodules -e "(.|.)[\d]" >actual && + cat >expect <<-\EOF && + .gitmodules:[submodule "submodule"] + .gitmodules: path = submodule + .gitmodules: url = ./submodule + a:(1|2)d(3|4) + submodule/.gitmodules:[submodule "sub"] + submodule/a:(1|2)d(3|4) + submodule/sub/a:(1|2)d(3|4) + EOF + test_cmp expect actual && + git -c grep.patternType=extended grep --recurse-submodules -e "(.|.)[\d]" >actual && + test_cmp expect actual && + git -c grep.extendedRegexp=true grep --recurse-submodules -e "(.|.)[\d]" >actual && + test_cmp expect actual && + + # Perl + if test_have_prereq PCRE + then + git grep -P --recurse-submodules -e "(.|.)[\d]" >actual && + cat >expect <<-\EOF && + a:(1|2)d(3|4) + b/b:(3|4) + submodule/a:(1|2)d(3|4) + submodule/sub/a:(1|2)d(3|4) + EOF + test_cmp expect actual && + git -c grep.patternType=perl grep --recurse-submodules -e "(.|.)[\d]" >actual && + test_cmp expect actual + fi +' + test_done From 77f6f4406fef43130e9c72e32ac5e30c5bbe0b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:15 +0000 Subject: [PATCH 263/566] grep: add a test helper function for less verbose -f \0 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper function to make the tests which check for patterns with \0 in them more succinct. Right now this isn't a big win, but subsequent commits will add a lot more of these tests. The helper is based on the match() function in t3070-wildmatch.sh. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7008-grep-binary.sh | 58 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index 9c9c378119..df93d8e44c 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -4,6 +4,29 @@ test_description='git grep in binary files' . ./test-lib.sh +nul_match () { + matches=$1 + flags=$2 + pattern=$3 + pattern_human=$(echo "$pattern" | sed 's/Q//g') + + if test "$matches" = 1 + then + test_expect_success "git grep -f f $flags '$pattern_human' a" " + printf '$pattern' | q_to_nul >f && + git grep -f f $flags a + " + elif test "$matches" = 0 + then + test_expect_success "git grep -f f $flags '$pattern_human' a" " + printf '$pattern' | q_to_nul >f && + test_must_fail git grep -f f $flags a + " + else + test_expect_success "PANIC: Test framework error. Unknown matches value $matches" 'false' + fi +} + test_expect_success 'setup' " echo 'binaryQfile' | q_to_nul >a && git add a && @@ -69,35 +92,12 @@ test_expect_failure 'git grep .fi a' ' git grep .fi a ' -test_expect_success 'git grep -F yf a' " - printf 'yQf' | q_to_nul >f && - git grep -f f -F a -" - -test_expect_success 'git grep -F yx a' " - printf 'yQx' | q_to_nul >f && - test_must_fail git grep -f f -F a -" - -test_expect_success 'git grep -Fi Yf a' " - printf 'YQf' | q_to_nul >f && - git grep -f f -Fi a -" - -test_expect_success 'git grep -Fi Yx a' " - printf 'YQx' | q_to_nul >f && - test_must_fail git grep -f f -Fi a -" - -test_expect_success 'git grep yf a' " - printf 'yQf' | q_to_nul >f && - git grep -f f a -" - -test_expect_success 'git grep yx a' " - printf 'yQx' | q_to_nul >f && - test_must_fail git grep -f f a -" +nul_match 1 '-F' 'yQf' +nul_match 0 '-F' 'yQx' +nul_match 1 '-Fi' 'YQf' +nul_match 0 '-Fi' 'YQx' +nul_match 1 '' 'yQf' +nul_match 0 '' 'yQx' test_expect_success 'grep respects binary diff attribute' ' echo text >t && From 12fc32faa8b51d33f3793c45ea20ab02b9717574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:16 +0000 Subject: [PATCH 264/566] grep: prepare for testing binary regexes containing rx metacharacters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add setup code needed for testing regexes that contain both binary data and regex metacharacters. The POSIX regcomp() function inherently can't support that, because it takes a \0-delimited char *, but other regex engines APIs like PCRE v2 take a pattern/length pair, and are thus able to handle \0s in patterns as well as any other character. When kwset was imported in commit 9eceddeec6 ("Use kwset in grep", 2011-08-21) this limitation was fixed, but at the expense of introducing the undocumented limitation that any pattern containing \0 implicitly becomes a fixed match (equivalent to -F having been provided). That's not something we'd like to keep in the future. The inability to match patterns containing \0 is a leaky implementation detail. So add tests as a first step towards changing that. In order to test that \0-patterns can properly match as regexes the test string needs to have some regex metacharacters in it. There were other blind spots in the tests. The code around kwset specially handles case-insensitive & non-ASCII data, but there were no tests for this. Fix all of that by amending the text being matched to contain both regex metacharacters & non-ASCII data. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7008-grep-binary.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index df93d8e44c..20370d6e0c 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -28,7 +28,7 @@ nul_match () { } test_expect_success 'setup' " - echo 'binaryQfile' | q_to_nul >a && + echo 'binaryQfileQm[*]cQ*æQð' | q_to_nul >a && git add a && git commit -m. " @@ -162,7 +162,7 @@ test_expect_success 'grep does not honor textconv' ' ' test_expect_success 'grep --textconv honors textconv' ' - echo "a:binaryQfile" >expect && + echo "a:binaryQfileQm[*]cQ*æQð" >expect && git grep --textconv Qfile >actual && test_cmp expect actual ' @@ -172,7 +172,7 @@ test_expect_success 'grep --no-textconv does not honor textconv' ' ' test_expect_success 'grep --textconv blob honors textconv' ' - echo "HEAD:a:binaryQfile" >expect && + echo "HEAD:a:binaryQfileQm[*]cQ*æQð" >expect && git grep --textconv Qfile HEAD:a >actual && test_cmp expect actual ' From 966be95549374916fc2736897b99c4918899b8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:17 +0000 Subject: [PATCH 265/566] grep: add tests to fix blind spots with \0 patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address a big blind spot in the tests for patterns containing \0. The is_fixed() function considers any string that contains \0 fixed, even if it contains regular expression metacharacters, those patterns are currently matched with kwset. Before this change removing that memchr(s, 0, len) check from is_fixed() wouldn't change the result of any of the tests, since regcomp() will happily match the part before the \0. The kwset path is dependent on whether the the -i flag is on, and whether the pattern has any non-ASCII characters, but none of this was tested for. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7008-grep-binary.sh | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index 20370d6e0c..615e7e0162 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -22,6 +22,18 @@ nul_match () { printf '$pattern' | q_to_nul >f && test_must_fail git grep -f f $flags a " + elif test "$matches" = T1 + then + test_expect_failure "git grep -f f $flags '$pattern_human' a" " + printf '$pattern' | q_to_nul >f && + git grep -f f $flags a + " + elif test "$matches" = T0 + then + test_expect_failure "git grep -f f $flags '$pattern_human' a" " + printf '$pattern' | q_to_nul >f && + test_must_fail git grep -f f $flags a + " else test_expect_success "PANIC: Test framework error. Unknown matches value $matches" 'false' fi @@ -98,6 +110,65 @@ nul_match 1 '-Fi' 'YQf' nul_match 0 '-Fi' 'YQx' nul_match 1 '' 'yQf' nul_match 0 '' 'yQx' +nul_match 1 '' 'æQð' +nul_match 1 '-F' 'eQm[*]c' +nul_match 1 '-Fi' 'EQM[*]C' + +# Regex patterns that would match but shouldn't with -F +nul_match 0 '-F' 'yQ[f]' +nul_match 0 '-F' '[y]Qf' +nul_match 0 '-Fi' 'YQ[F]' +nul_match 0 '-Fi' '[Y]QF' +nul_match 0 '-F' 'æQ[ð]' +nul_match 0 '-F' '[æ]Qð' +nul_match 0 '-Fi' 'ÆQ[Ð]' +nul_match 0 '-Fi' '[Æ]QÐ' + +# kwset is disabled on -i & non-ASCII. No way to match non-ASCII \0 +# patterns case-insensitively. +nul_match T1 '-i' 'ÆQÐ' + +# \0 implicitly disables regexes. This is an undocumented internal +# limitation. +nul_match T1 '' 'yQ[f]' +nul_match T1 '' '[y]Qf' +nul_match T1 '-i' 'YQ[F]' +nul_match T1 '-i' '[Y]Qf' +nul_match T1 '' 'æQ[ð]' +nul_match T1 '' '[æ]Qð' +nul_match T1 '-i' 'ÆQ[Ð]' + +# ... because of \0 implicitly disabling regexes regexes that +# should/shouldn't match don't do the right thing. +nul_match T1 '' 'eQm.*cQ' +nul_match T1 '-i' 'EQM.*cQ' +nul_match T0 '' 'eQm[*]c' +nul_match T0 '-i' 'EQM[*]C' + +# Due to the REG_STARTEND extension when kwset() is disabled on -i & +# non-ASCII the string will be matched in its entirety, but the +# pattern will be cut off at the first \0. +nul_match 0 '-i' 'NOMATCHQð' +nul_match T0 '-i' '[Æ]QNOMATCH' +nul_match T0 '-i' '[æ]QNOMATCH' +# Matches, but for the wrong reasons, just stops at [æ] +nul_match 1 '-i' '[Æ]Qð' +nul_match 1 '-i' '[æ]Qð' + +# Ensure that the matcher doesn't regress to something that stops at +# \0 +nul_match 0 '-F' 'yQ[f]' +nul_match 0 '-Fi' 'YQ[F]' +nul_match 0 '' 'yQNOMATCH' +nul_match 0 '' 'QNOMATCH' +nul_match 0 '-i' 'YQNOMATCH' +nul_match 0 '-i' 'QNOMATCH' +nul_match 0 '-F' 'æQ[ð]' +nul_match 0 '-Fi' 'ÆQ[Ð]' +nul_match 0 '' 'yQNÓMATCH' +nul_match 0 '' 'QNÓMATCH' +nul_match 0 '-i' 'YQNÓMATCH' +nul_match 0 '-i' 'QNÓMATCH' test_expect_success 'grep respects binary diff attribute' ' echo text >t && From 88b6197d0b4f5ad480e9585baa984cac51f4771f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:18 +0000 Subject: [PATCH 266/566] perf: add a GIT_PERF_MAKE_COMMAND for when *_MAKE_OPTS won't do MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a git GIT_PERF_MAKE_COMMAND variable to compliment the existing GIT_PERF_MAKE_OPTS facility. This allows specifying an arbitrary shell command to execute instead of 'make'. This is useful e.g. in cases where the name, semantics or defaults of a Makefile flag have changed over time. It can even be used to change the contents of the tree, useful for monkeypatching ancient versions of git to get them to build. This opens Pandora's box in some ways, it's now possible to "jailbreak" the perf environment and e.g. modify the source tree via this arbitrary instead of just issuing a custom "make" command, such a command has to be re-entrant in the sense that subsequent perf runs will re-use the possibly modified tree. It would be pointless to try to mitigate or work around that caveat in a tool purely aimed at Git developers, so this change makes no attempt to do so. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 3 +++ t/perf/README | 17 ++++++++++++++++- t/perf/run | 11 +++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index eedadb8056..d1587452f1 100644 --- a/Makefile +++ b/Makefile @@ -2272,6 +2272,9 @@ endif ifdef GIT_PERF_MAKE_OPTS @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+ endif +ifdef GIT_PERF_MAKE_COMMAND + @echo GIT_PERF_MAKE_COMMAND=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_COMMAND)))'\' >>$@+ +endif ifdef GIT_INTEROP_MAKE_OPTS @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+ endif diff --git a/t/perf/README b/t/perf/README index 49ea4349be..0b6a8d2906 100644 --- a/t/perf/README +++ b/t/perf/README @@ -60,7 +60,22 @@ You can set the following variables (also in your config.mak): GIT_PERF_MAKE_OPTS Options to use when automatically building a git tree for - performance testing. E.g., -j6 would be useful. + performance testing. E.g., -j6 would be useful. Passed + directly to make as "make $GIT_PERF_MAKE_OPTS". + + GIT_PERF_MAKE_COMMAND + An arbitrary command that'll be run in place of the make + command, if set the GIT_PERF_MAKE_OPTS variable is + ignored. Useful in cases where source tree changes might + require issuing a different make command to different + revisions. + + This can be (ab)used to monkeypatch or otherwise change the + tree about to be built. Note that the build directory can be + re-used for subsequent runs so the make command might get + executed multiple times on the same tree, but don't count on + any of that, that's an implementation detail that might change + in the future. GIT_PERF_REPO GIT_PERF_LARGE_REPO diff --git a/t/perf/run b/t/perf/run index c788d713ae..b61024a830 100755 --- a/t/perf/run +++ b/t/perf/run @@ -37,8 +37,15 @@ build_git_rev () { cp "../../$config" "build/$rev/" fi done - (cd build/$rev && make $GIT_PERF_MAKE_OPTS) || - die "failed to build revision '$mydir'" + ( + cd build/$rev && + if test -n "$GIT_PERF_MAKE_COMMAND" + then + sh -c "$GIT_PERF_MAKE_COMMAND" + else + make $GIT_PERF_MAKE_OPTS + fi + ) || die "failed to build revision '$mydir'" } run_dirs_helper () { From b11ad029cb67070f04e131b3afb739da5d086509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 21:42:19 +0000 Subject: [PATCH 267/566] perf: emit progress output when unpacking & building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend the t/perf/run output so that in addition to the "Running N tests" heading currently being emitted, it also emits "Unpacking $rev" and "Building $rev" when setting up the build/$rev directory & when building it, respectively. This makes it easier to see what's going on and what revision is being tested as the output scrolls by. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/perf/run | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/perf/run b/t/perf/run index b61024a830..beb4acc0e4 100755 --- a/t/perf/run +++ b/t/perf/run @@ -24,6 +24,7 @@ run_one_dir () { unpack_git_rev () { rev=$1 + echo "=== Unpacking $rev in build/$rev ===" mkdir -p build/$rev (cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) | (cd build/$rev && tar x) @@ -37,6 +38,7 @@ build_git_rev () { cp "../../$config" "build/$rev/" fi done + echo "=== Building $rev ===" ( cd build/$rev && if test -n "$GIT_PERF_MAKE_COMMAND" From a0103914c228cee430b37faab212d1769e8218a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 20 May 2017 11:54:28 +0000 Subject: [PATCH 268/566] sha1dc: update from upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update sha1dc from the latest version by the upstream maintainer[1]. This version includes a commit of mine which allows for replacing the local modifications done to the upstream files in git.git with macro definitions to monkeypatch it in place. It also brings in a change[2] upstream made for the breakage 2.13.0 introduced on SPARC and other platforms that forbid unaligned access[3]. This means that the code customizations done since the initial import in commit 28dc98e343 ("sha1dc: add collision-detecting sha1 implementation", 2017-03-16) can be done purely via Makefile definitions and by including the content of our own sha1dc_git.[ch] in sha1dc/sha1.c via a macro. 1. https://github.com/cr-marcstevens/sha1collisiondetection/commit/cc465543b310e5f59a1d534381690052e8509b22 2. https://github.com/cr-marcstevens/sha1collisiondetection/commit/33a694a9ee1b79c24be45f9eab5ac0e1aeeaf271 3. "Git 2.13.0 segfaults on Solaris SPARC due to DC_SHA1=YesPlease being on by default" (https://public-inbox.org/git/CACBZZX6nmKK8af0-UpjCKWV4R+hV-uk2xWXVA5U+_UQ3VXU03g@mail.gmail.com/) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 9 ++++- sha1dc/sha1.c | 93 ++++++++++++++++++++++++++++++---------------- sha1dc/sha1.h | 90 +++++++++++++++++++------------------------- sha1dc/ubc_check.c | 13 ++++++- sha1dc/ubc_check.h | 14 +++++-- sha1dc_git.c | 24 ++++++++++++ sha1dc_git.h | 19 ++++++++++ 7 files changed, 173 insertions(+), 89 deletions(-) create mode 100644 sha1dc_git.c create mode 100644 sha1dc_git.h diff --git a/Makefile b/Makefile index e35542e631..ffa6da71b7 100644 --- a/Makefile +++ b/Makefile @@ -1414,7 +1414,14 @@ else DC_SHA1 := YesPlease LIB_OBJS += sha1dc/sha1.o LIB_OBJS += sha1dc/ubc_check.o - BASIC_CFLAGS += -DSHA1_DC + BASIC_CFLAGS += \ + -DSHA1_DC \ + -DSHA1DC_NO_STANDARD_INCLUDES \ + -DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \ + -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \ + -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C="\"sha1dc_git.c\"" \ + -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H="\"sha1dc_git.h\"" \ + -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\"" endif endif endif diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c index 35e9dd5bf4..3dff80ac72 100644 --- a/sha1dc/sha1.c +++ b/sha1dc/sha1.c @@ -5,9 +5,23 @@ * https://opensource.org/licenses/MIT ***/ -#include "cache.h" -#include "sha1dc/sha1.h" -#include "sha1dc/ubc_check.h" +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#include +#include +#include +#endif + +#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C +#include SHA1DC_CUSTOM_INCLUDE_SHA1_C +#endif + +#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT +#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1 +#endif + +#include "sha1.h" +#include "ubc_check.h" /* @@ -18,16 +32,31 @@ If you are compiling on a big endian platform and your compiler does not define one of these, you will have to add whatever macros your tool chain defines to indicate Big-Endianness. */ -#if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ - (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) || \ - defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ - defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) - -#define SHA1DC_BIGENDIAN 1 -#else +#ifdef SHA1DC_BIGENDIAN #undef SHA1DC_BIGENDIAN +#endif +#if (!defined SHA1DC_FORCE_LITTLEENDIAN) && \ + ((defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) || \ + defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) || defined(SHA1DC_FORCE_BIGENDIAN)) + +#define SHA1DC_BIGENDIAN + #endif /*ENDIANNESS SELECTION*/ +#if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \ + defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \ + defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \ + defined(__386) || defined(_M_X64) || defined(_M_AMD64)) + +#define SHA1DC_ALLOW_UNALIGNED_ACCESS + +#endif /*UNALIGNMENT DETECTION*/ + + #define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n)))) #define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n)))) @@ -36,11 +65,11 @@ #define sha1_mix(W, t) (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1)) -#if defined(SHA1DC_BIGENDIAN) +#ifdef SHA1DC_BIGENDIAN #define sha1_load(m, t, temp) { temp = m[t]; } #else #define sha1_load(m, t, temp) { temp = m[t]; sha1_bswap32(temp); } -#endif /* !defined(SHA1DC_BIGENDIAN) */ +#endif #define sha1_store(W, t, x) *(volatile uint32_t *)&W[t] = x @@ -869,6 +898,11 @@ static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], co ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \ } +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4127) /* Complier complains about the checks in the above macro being constant. */ +#endif + #ifdef DOSTORESTATE0 SHA1_RECOMPRESS(0) #endif @@ -1189,6 +1223,10 @@ SHA1_RECOMPRESS(78) SHA1_RECOMPRESS(79) #endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) { switch (step) @@ -1662,7 +1700,7 @@ void SHA1DCInit(SHA1_CTX* ctx) ctx->ihv[3] = 0x10325476; ctx->ihv[4] = 0xC3D2E1F0; ctx->found_collision = 0; - ctx->safe_hash = 0; + ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT; ctx->ubc_check = 1; ctx->detect_coll = 1; ctx->reduced_round_coll = 0; @@ -1710,6 +1748,7 @@ void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback) void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len) { unsigned left, fill; + if (len == 0) return; @@ -1728,7 +1767,13 @@ void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len) while (len >= 64) { ctx->total += 64; + +#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) sha1_process(ctx, (uint32_t*)(buf)); +#else + memcpy(ctx->buffer, buf, 64); + sha1_process(ctx, (uint32_t*)(ctx->buffer)); +#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */ buf += 64; len -= 64; } @@ -1788,22 +1833,6 @@ int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx) return ctx->found_collision; } -void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx) -{ - if (!SHA1DCFinal(hash, ctx)) - return; - die("SHA-1 appears to be part of a collision attack: %s", - sha1_to_hex(hash)); -} - -void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len) -{ - const char *data = vdata; - /* We expect an unsigned long, but sha1dc only takes an int */ - while (len > INT_MAX) { - SHA1DCUpdate(ctx, data, INT_MAX); - data += INT_MAX; - len -= INT_MAX; - } - SHA1DCUpdate(ctx, data, len); -} +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C +#endif diff --git a/sha1dc/sha1.h b/sha1dc/sha1.h index bd8bd928fb..a0ff5d1305 100644 --- a/sha1dc/sha1.h +++ b/sha1dc/sha1.h @@ -4,6 +4,7 @@ * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ + #ifndef SHA1DC_SHA1_H #define SHA1DC_SHA1_H @@ -11,36 +12,30 @@ extern "C" { #endif -/* uses SHA-1 message expansion to expand the first 16 words of W[] to 80 words */ -/* void sha1_message_expansion(uint32_t W[80]); */ +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#endif -/* sha-1 compression function; first version takes a message block pre-parsed as 16 32-bit integers, second version takes an already expanded message) */ -/* void sha1_compression(uint32_t ihv[5], const uint32_t m[16]); -void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]); */ - -/* same as sha1_compression_W, but additionally store intermediate states */ +/* sha-1 compression function that takes an already expanded message, and additionally store intermediate states */ /* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h */ void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]); /* -// function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) -// where 0 <= T < 80 -// me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference) -// state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block -// the function will return: -// ihvin: the reconstructed input chaining value -// ihvout: the reconstructed output chaining value +// Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]). +// Where 0 <= T < 80 +// me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference.) +// state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block. +// The function will return: +// ihvin: The reconstructed input chaining value. +// ihvout: The reconstructed output chaining value. */ typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*); -/* table of sha1_recompression_step_0, ... , sha1_recompression_step_79 */ -/* extern sha1_recompression_type sha1_recompression_step[80];*/ - -/* a callback function type that can be set to be called when a collision block has been found: */ +/* A callback function type that can be set to be called when a collision block has been found: */ /* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */ typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); -/* the SHA-1 context */ +/* The SHA-1 context. */ typedef struct { uint64_t total; uint32_t ihv[5]; @@ -59,30 +54,34 @@ typedef struct { uint32_t states[80][5]; } SHA1_CTX; -/* initialize SHA-1 context */ +/* Initialize SHA-1 context. */ void SHA1DCInit(SHA1_CTX*); /* -// function to enable safe SHA-1 hashing: -// collision attacks are thwarted by hashing a detected near-collision block 3 times -// think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: -// the best collision attacks against SHA-1 have complexity about 2^60, -// thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would 2^180 -// an attacker would be better off using a generic birthday search of complexity 2^80 -// -// enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected -// but it will result in a different SHA-1 hash for messages where a collision attack was detected -// this will automatically invalidate SHA-1 based digital signature forgeries -// enabled by default + Function to enable safe SHA-1 hashing: + Collision attacks are thwarted by hashing a detected near-collision block 3 times. + Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: + The best collision attacks against SHA-1 have complexity about 2^60, + thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. + An attacker would be better off using a generic birthday search of complexity 2^80. + + Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected, + but it will result in a different SHA-1 hash for messages where a collision attack was detected. + This will automatically invalidate SHA-1 based digital signature forgeries. + Enabled by default. */ void SHA1DCSetSafeHash(SHA1_CTX*, int); -/* function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up) */ -/* enabled by default */ +/* + Function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up). + Enabled by default + */ void SHA1DCSetUseUBC(SHA1_CTX*, int); -/* function to disable or enable the use of Collision Detection */ -/* enabled by default */ +/* + Function to disable or enable the use of Collision Detection. + Enabled by default. + */ void SHA1DCSetUseDetectColl(SHA1_CTX*, int); /* function to disable or enable the detection of reduced-round SHA-1 collisions */ @@ -100,23 +99,12 @@ void SHA1DCUpdate(SHA1_CTX*, const char*, size_t); /* returns: 0 = no collision detected, otherwise = collision found => warn user for active attack */ int SHA1DCFinal(unsigned char[20], SHA1_CTX*); -/* - * Same as SHA1DCFinal, but convert collision attack case into a verbose die(). - */ -void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *); - -/* - * Same as SHA1DCUpdate, but adjust types to match git's usual interface. - */ -void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len); - -#define platform_SHA_CTX SHA1_CTX -#define platform_SHA1_Init SHA1DCInit -#define platform_SHA1_Update git_SHA1DCUpdate -#define platform_SHA1_Final git_SHA1DCFinal - #if defined(__cplusplus) } #endif -#endif /* SHA1DC_SHA1_H */ +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H +#endif + +#endif diff --git a/sha1dc/ubc_check.c b/sha1dc/ubc_check.c index 089dd4743d..b3beff2afb 100644 --- a/sha1dc/ubc_check.c +++ b/sha1dc/ubc_check.c @@ -24,8 +24,13 @@ // ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section */ -#include "git-compat-util.h" -#include "sha1dc/ubc_check.h" +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#endif +#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C +#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C +#endif +#include "ubc_check.h" static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0; static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1; @@ -361,3 +366,7 @@ if (mask) { dvmask[0]=mask; } + +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C +#endif diff --git a/sha1dc/ubc_check.h b/sha1dc/ubc_check.h index b64c306d77..d7e17dc734 100644 --- a/sha1dc/ubc_check.h +++ b/sha1dc/ubc_check.h @@ -20,13 +20,17 @@ // thus one needs to do the recompression check for each DV that has its bit set */ -#ifndef UBC_CHECK_H -#define UBC_CHECK_H +#ifndef SHA1DC_UBC_CHECK_H +#define SHA1DC_UBC_CHECK_H #if defined(__cplusplus) extern "C" { #endif +#ifndef SHA1DC_NO_STANDARD_INCLUDES +#include +#endif + #define DVMASKSIZE 1 typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t; extern dv_info_t sha1_dvs[]; @@ -41,4 +45,8 @@ void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]); } #endif -#endif /* UBC_CHECK_H */ +#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H +#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H +#endif + +#endif diff --git a/sha1dc_git.c b/sha1dc_git.c new file mode 100644 index 0000000000..4d32b4f77e --- /dev/null +++ b/sha1dc_git.c @@ -0,0 +1,24 @@ +/* + * This code is included at the end of sha1dc/sha1.c with the + * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C macro. + */ + +void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx) +{ + if (!SHA1DCFinal(hash, ctx)) + return; + die("SHA-1 appears to be part of a collision attack: %s", + sha1_to_hex(hash)); +} + +void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len) +{ + const char *data = vdata; + /* We expect an unsigned long, but sha1dc only takes an int */ + while (len > INT_MAX) { + SHA1DCUpdate(ctx, data, INT_MAX); + data += INT_MAX; + len -= INT_MAX; + } + SHA1DCUpdate(ctx, data, len); +} diff --git a/sha1dc_git.h b/sha1dc_git.h new file mode 100644 index 0000000000..a8a5c1da16 --- /dev/null +++ b/sha1dc_git.h @@ -0,0 +1,19 @@ +/* + * This code is included at the end of sha1dc/sha1.h with the + * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H macro. + */ + +/* + * Same as SHA1DCFinal, but convert collision attack case into a verbose die(). + */ +void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *); + +/* + * Same as SHA1DCUpdate, but adjust types to match git's usual interface. + */ +void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len); + +#define platform_SHA_CTX SHA1_CTX +#define platform_SHA1_Init SHA1DCInit +#define platform_SHA1_Update git_SHA1DCUpdate +#define platform_SHA1_Final git_SHA1DCFinal From 3d7dd2d3b6ccc8903a37cffe3a2f39cf1be21c86 Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Sun, 21 May 2017 23:25:39 +0100 Subject: [PATCH 269/566] usage: add NORETURN to BUG() function definitions Commit d8193743e0 ("usage.c: add BUG() function", 12-05-2017) added the BUG() functions and macros as a replacement for calls to die("BUG: .."). The use of NORETURN on the declarations (in git-compat-util.h) and the lack of NORETURN on the function definitions, however, leads sparse to complain thus: SP usage.c usage.c:220:6: error: symbol 'BUG_fl' redeclared with different type (originally declared at git-compat-util.h:1074) - different modifiers In order to suppress the sparse error, add the NORETURN to the function definitions. Signed-off-by: Ramsay Jones Signed-off-by: Junio C Hamano --- usage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usage.c b/usage.c index 7e6cb20280..1f63e033e9 100644 --- a/usage.c +++ b/usage.c @@ -217,7 +217,7 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis } #ifdef HAVE_VARIADIC_MACROS -void BUG_fl(const char *file, int line, const char *fmt, ...) +NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -225,7 +225,7 @@ void BUG_fl(const char *file, int line, const char *fmt, ...) va_end(ap); } #else -void BUG(const char *fmt, ...) +NORETURN void BUG(const char *fmt, ...) { va_list ap; va_start(ap, fmt); From b3487ccc0bffc28e134333b164d0d84fdd15d9f4 Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Thu, 18 May 2017 04:21:49 -0400 Subject: [PATCH 270/566] t7300: clean -d should skip dirs with ignored files If git sees a directory which contains only untracked and ignored files, clean -d should not remove that directory. It was recently discovered that this is *not* true of git clean -d, and it's possible that this has never worked correctly; this test and its accompanying patch series aims to fix that. Signed-off-by: Samuel Lijin Signed-off-by: Junio C Hamano --- t/t7300-clean.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index b89fd2a6ad..3a2d709c29 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -653,4 +653,20 @@ test_expect_success 'git clean -d respects pathspecs (pathspec is prefix of dir) test_path_is_dir foobar ' +test_expect_failure 'git clean -d skips untracked dirs containing ignored files' ' + echo /foo/bar >.gitignore && + echo ignoreme >>.gitignore && + rm -rf foo && + mkdir -p foo/a/aa/aaa foo/b/bb/bbb && + touch foo/bar foo/baz foo/a/aa/ignoreme foo/b/ignoreme foo/b/bb/1 foo/b/bb/2 && + git clean -df && + test_path_is_dir foo && + test_path_is_file foo/bar && + test_path_is_missing foo/baz && + test_path_is_file foo/a/aa/ignoreme && + test_path_is_missing foo/a/aa/aaa && + test_path_is_file foo/b/ignoreme && + test_path_is_missing foo/b/bb +' + test_done From 0a81d4a55917514091727696a09ac649d03b57ff Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Thu, 18 May 2017 04:21:50 -0400 Subject: [PATCH 271/566] t7061: status --ignored should search untracked dirs Per eb8c5b87, `status --ignored` by design does not list ignored files if they are in a directory which contains only ignored and untracked files (which is itself considered to be untracked) without `-uall`. This does not make sense for `--ignored`, which claims to "Show ignored files as well." Thus we revisit eb8c5b87 and decide that for such directories, `status --ignored` will list the directory as untracked *and* list all ignored files within said directory even without `-uall`. Signed-off-by: Samuel Lijin Signed-off-by: Junio C Hamano --- t/t7061-wtstatus-ignore.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh index cdc0747bf0..15e7592b6b 100755 --- a/t/t7061-wtstatus-ignore.sh +++ b/t/t7061-wtstatus-ignore.sh @@ -9,9 +9,10 @@ cat >expected <<\EOF ?? actual ?? expected ?? untracked/ +!! untracked/ignored EOF -test_expect_success 'status untracked directory with --ignored' ' +test_expect_failure 'status untracked directory with --ignored' ' echo "ignored" >.gitignore && mkdir untracked && : >untracked/ignored && @@ -20,7 +21,7 @@ test_expect_success 'status untracked directory with --ignored' ' test_cmp expected actual ' -test_expect_success 'same with gitignore starting with BOM' ' +test_expect_failure 'same with gitignore starting with BOM' ' printf "\357\273\277ignored\n" >.gitignore && mkdir -p untracked && : >untracked/ignored && From df5bcdf83aeb94718602ebc8c0f597166bb493f1 Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Thu, 18 May 2017 04:21:51 -0400 Subject: [PATCH 272/566] dir: recurse into untracked dirs for ignored files We consider directories containing only untracked and ignored files to be themselves untracked, which in the usual case means we don't have to search these directories. This is problematic when we want to collect ignored files with DIR_SHOW_IGNORED_TOO, though, so we teach read_directory_recursive() to recurse into untracked directories to find the ignored files they contain when DIR_SHOW_IGNORED_TOO is set. This has the side effect of also collecting all untracked files in untracked directories as well. Signed-off-by: Samuel Lijin Signed-off-by: Junio C Hamano --- dir.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dir.c b/dir.c index aeeb5ce104..6b4eeef584 100644 --- a/dir.c +++ b/dir.c @@ -1747,7 +1747,10 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, dir_state = state; /* recurse into subdir if instructed by treat_path */ - if (state == path_recurse) { + if ((state == path_recurse) || + ((state == path_untracked) && + (dir->flags & DIR_SHOW_IGNORED_TOO) && + (get_dtype(cdir.de, path.buf, path.len) == DT_DIR))) { struct untracked_cache_dir *ud; ud = lookup_untracked(dir->untracked, untracked, path.buf + baselen, From fb898888491b83c9a3396fb559032ca78807a0c0 Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Thu, 18 May 2017 04:21:52 -0400 Subject: [PATCH 273/566] dir: hide untracked contents of untracked dirs When we taught read_directory_recursive() to recurse into untracked directories in search of ignored files given DIR_SHOW_IGNORED_TOO, that had the side effect of teaching it to collect the untracked contents of untracked directories. It doesn't always make sense to return these, though (we do need them for `clean -d`), so we introduce a flag (DIR_KEEP_UNTRACKED_CONTENTS) to control whether or not read_directory() strips dir->entries of the untracked contents of untracked dirs. We also introduce check_contains() to check if one dir_entry corresponds to a path which contains the path corresponding to another dir_entry. This also fixes known breakages in t7061, since status --ignored now searches untracked directories for ignored files. Signed-off-by: Samuel Lijin Signed-off-by: Junio C Hamano --- .../technical/api-directory-listing.txt | 6 ++++ dir.c | 31 +++++++++++++++++++ dir.h | 3 +- t/t7061-wtstatus-ignore.sh | 4 +-- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt index 7f8e78d916..6c77b4920c 100644 --- a/Documentation/technical/api-directory-listing.txt +++ b/Documentation/technical/api-directory-listing.txt @@ -33,6 +33,12 @@ The notable options are: Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]` in addition to untracked files in `entries[]`. +`DIR_KEEP_UNTRACKED_CONTENTS`::: + + Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the + untracked contents of untracked directories are also returned in + `entries[]`. + `DIR_COLLECT_IGNORED`::: Special mode for git-add. Return ignored files in `ignored[]` and diff --git a/dir.c b/dir.c index 6b4eeef584..61561ea91d 100644 --- a/dir.c +++ b/dir.c @@ -1813,6 +1813,14 @@ static int cmp_name(const void *p1, const void *p2) return name_compare(e1->name, e1->len, e2->name, e2->len); } +/* check if *out lexically strictly contains *in */ +static int check_contains(const struct dir_entry *out, const struct dir_entry *in) +{ + return (out->len < in->len) && + (out->name[out->len - 1] == '/') && + !memcmp(out->name, in->name, out->len); +} + static int treat_leading_path(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec) @@ -2028,6 +2036,29 @@ int read_directory(struct dir_struct *dir, const char *path, read_directory_recursive(dir, path, len, untracked, 0, pathspec); QSORT(dir->entries, dir->nr, cmp_name); QSORT(dir->ignored, dir->ignored_nr, cmp_name); + + /* + * If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will + * also pick up untracked contents of untracked dirs; by default + * we discard these, but given DIR_KEEP_UNTRACKED_CONTENTS we do not. + */ + if ((dir->flags & DIR_SHOW_IGNORED_TOO) && + !(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) { + int i, j; + + /* remove from dir->entries untracked contents of untracked dirs */ + for (i = j = 0; j < dir->nr; j++) { + if (i && check_contains(dir->entries[i - 1], dir->entries[j])) { + free(dir->entries[j]); + dir->entries[j] = NULL; + } else { + dir->entries[i++] = dir->entries[j]; + } + } + + dir->nr = i; + } + if (dir->untracked) { static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS); trace_printf_key(&trace_untracked_stats, diff --git a/dir.h b/dir.h index bf23a470af..650e54bdf6 100644 --- a/dir.h +++ b/dir.h @@ -151,7 +151,8 @@ struct dir_struct { DIR_NO_GITLINKS = 1<<3, DIR_COLLECT_IGNORED = 1<<4, DIR_SHOW_IGNORED_TOO = 1<<5, - DIR_COLLECT_KILLED_ONLY = 1<<6 + DIR_COLLECT_KILLED_ONLY = 1<<6, + DIR_KEEP_UNTRACKED_CONTENTS = 1<<7 } flags; struct dir_entry **entries; struct dir_entry **ignored; diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh index 15e7592b6b..fc6013ba3c 100755 --- a/t/t7061-wtstatus-ignore.sh +++ b/t/t7061-wtstatus-ignore.sh @@ -12,7 +12,7 @@ cat >expected <<\EOF !! untracked/ignored EOF -test_expect_failure 'status untracked directory with --ignored' ' +test_expect_success 'status untracked directory with --ignored' ' echo "ignored" >.gitignore && mkdir untracked && : >untracked/ignored && @@ -21,7 +21,7 @@ test_expect_failure 'status untracked directory with --ignored' ' test_cmp expected actual ' -test_expect_failure 'same with gitignore starting with BOM' ' +test_expect_success 'same with gitignore starting with BOM' ' printf "\357\273\277ignored\n" >.gitignore && mkdir -p untracked && : >untracked/ignored && From bbf504a9957e8a2a262619641ffa30348d71a76f Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Thu, 18 May 2017 04:21:53 -0400 Subject: [PATCH 274/566] dir: expose cmp_name() and check_contains() We want to use cmp_name() and check_contains() (which both compare `struct dir_entry`s, the former in terms of the sort order, the latter in terms of whether one lexically contains another) outside of dir.c, so we have to (1) change their linkage and (2) rename them as appropriate for the global namespace. The second is achieved by renaming cmp_name() to cmp_dir_entry() and check_contains() to check_dir_entry_contains(). Signed-off-by: Samuel Lijin Signed-off-by: Junio C Hamano --- dir.c | 11 ++++++----- dir.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dir.c b/dir.c index 61561ea91d..31c6e1dac0 100644 --- a/dir.c +++ b/dir.c @@ -1805,7 +1805,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, return dir_state; } -static int cmp_name(const void *p1, const void *p2) +int cmp_dir_entry(const void *p1, const void *p2) { const struct dir_entry *e1 = *(const struct dir_entry **)p1; const struct dir_entry *e2 = *(const struct dir_entry **)p2; @@ -1814,7 +1814,7 @@ static int cmp_name(const void *p1, const void *p2) } /* check if *out lexically strictly contains *in */ -static int check_contains(const struct dir_entry *out, const struct dir_entry *in) +int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in) { return (out->len < in->len) && (out->name[out->len - 1] == '/') && @@ -2034,8 +2034,8 @@ int read_directory(struct dir_struct *dir, const char *path, dir->untracked = NULL; if (!len || treat_leading_path(dir, path, len, pathspec)) read_directory_recursive(dir, path, len, untracked, 0, pathspec); - QSORT(dir->entries, dir->nr, cmp_name); - QSORT(dir->ignored, dir->ignored_nr, cmp_name); + QSORT(dir->entries, dir->nr, cmp_dir_entry); + QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry); /* * If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will @@ -2048,7 +2048,8 @@ int read_directory(struct dir_struct *dir, const char *path, /* remove from dir->entries untracked contents of untracked dirs */ for (i = j = 0; j < dir->nr; j++) { - if (i && check_contains(dir->entries[i - 1], dir->entries[j])) { + if (i && + check_dir_entry_contains(dir->entries[i - 1], dir->entries[j])) { free(dir->entries[j]); dir->entries[j] = NULL; } else { diff --git a/dir.h b/dir.h index 650e54bdf6..edb5fda586 100644 --- a/dir.h +++ b/dir.h @@ -327,6 +327,9 @@ static inline int dir_path_match(const struct dir_entry *ent, has_trailing_dir); } +int cmp_dir_entry(const void *p1, const void *p2); +int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in); + void untracked_cache_invalidate_path(struct index_state *, const char *); void untracked_cache_remove_from_index(struct index_state *, const char *); void untracked_cache_add_to_index(struct index_state *, const char *); From 1eb437020a2c098a7c12da4c05082fbea10d98c9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 23 May 2017 13:51:32 +0900 Subject: [PATCH 275/566] Second batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index bf5ed74775..4fc8f0db81 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -62,6 +62,9 @@ Performance, Internal Implementation, Development Support etc. move), and then declare uintmax_t is the type to be used as the timestamp_t. + * We can trigger Windows auto-build tester (credits: Dscho & + Microsoft) from our existing Travis CI tester now. + Also contains various documentation updates and code clean-ups. @@ -91,7 +94,33 @@ notes for details). not work well when val is an empty string. This has been fixed. (merge db4eca1fea jn/clone-add-empty-config-from-command-line later to maint). + * Setting "log.decorate=false" in the configuration file did not take + effect in v2.13, which has been corrected. + (merge c74271aae7 ah/log-decorate-default-to-auto later to maint). + + * A few codepaths in "checkout" and "am" working on an unborn branch + tried to access an uninitialized piece of memory. + (merge 57e0ef0e0e rs/checkout-am-fix-unborn later to maint). + + * The Web interface to gmane news archive is long gone, even though + the articles are still accessible via NTTP. Replace the links with + ones to public-inbox.org. Because their message identification is + based on the actual message-id, it is likely that it will be easier + to migrate away from it if/when necessary. + (merge 5840eb9d14 ab/doc-replace-gmane-links later to maint). + + * The receive-pack program now makes sure that the push certificate + records the same set of push options used for pushing. + (merge cbaf82cc6b jt/push-options-doc later to maint). + + * Tests have been updated to pass under GETTEXT_POISON (a mechanism + to ensure that output strings that should not be translated are + not translated by mistake), and TravisCI is told to run them. + (merge b8e188f6f5 ab/fix-poison-tests later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 515360f9e9 jn/credential-doc-on-clear later to maint). (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). (merge e294e8959f jc/apply-fix-mismerge later to maint). + (merge 7f1b225153 bw/submodule-with-bs-path later to maint). + (merge c8f7c8b704 tb/dedup-crlf-tests later to maint). From 7e95fcb4b571b52fb15300c9f8ab6425b472b856 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Mon, 22 May 2017 12:01:14 -0700 Subject: [PATCH 276/566] t5531: fix test description The description of the test was not enclosed in single quotes, which broke the coloring scheme that I am used to. Upon closer inspection the test is good, but the description is a bit vague. So extend the description of the first test. While at it align the description of the file to match what we actually test in the file. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- t/t5531-deep-submodule-push.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index f55137f76f..62160ab0ca 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='unpack-objects' +test_description='test push with submodules' . ./test-lib.sh @@ -27,7 +27,7 @@ test_expect_success setup ' ) ' -test_expect_success push ' +test_expect_success 'push works with recorded gitlink' ' ( cd work && git push ../pub.git master From b275da816c7eaf91b38c7de8757e461f4c92c0a4 Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Mon, 22 May 2017 21:45:33 +0200 Subject: [PATCH 277/566] Documentation: fix reference to ifExists for interpret-trailers The manual for "git interpret-trailers" mentioned a non-existing literal `overwrite` for its config option `trailer.ifexists`. The correct name for that choice is `replace`. Signed-off-by: Andreas Heiduk Signed-off-by: Junio C Hamano --- Documentation/git-interpret-trailers.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt index 09074c75a4..31cdeaecdf 100644 --- a/Documentation/git-interpret-trailers.txt +++ b/Documentation/git-interpret-trailers.txt @@ -123,7 +123,7 @@ trailer.ifexists:: same in the message. + The valid values for this option are: `addIfDifferentNeighbor` (this -is the default), `addIfDifferent`, `add`, `overwrite` or `doNothing`. +is the default), `addIfDifferent`, `add`, `replace` or `doNothing`. + With `addIfDifferentNeighbor`, a new trailer will be added only if no trailer with the same (, ) pair is above or below the line From 23739aa2b3d2546c3b3ff0e06eb5171b37e31a90 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:31 +0200 Subject: [PATCH 278/566] t3600: clean up permissions test properly The test of failing `git rm -f` removes the write permissions on the test directory, but fails to restore them if the test fails. This means that the test temporary directory cannot be cleaned up, which means that subsequent attempts to run the test fail mysteriously. Instead, do the cleanup in a `test_when_finished` block so that it can't be skipped. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- t/t3600-rm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 5f9913ba33..f8568f8841 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -97,9 +97,9 @@ test_expect_success FUNNYNAMES \ embedded'" test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' ' + test_when_finished "chmod 775 ." && chmod a-w . && - test_must_fail git rm -f baz && - chmod 775 . + test_must_fail git rm -f baz ' test_expect_success \ From fd2ce9c01c91a093fbc8f7e444d4d80c0d89432a Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:32 +0200 Subject: [PATCH 279/566] refs.h: clarify docstring for the ref_transaction_update()-related fns In particular, make it clear that they make copies of the sha1 arguments. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/refs.h b/refs.h index 685a979a0e..ec8c6bfbbb 100644 --- a/refs.h +++ b/refs.h @@ -427,6 +427,19 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err); * * refname -- the name of the reference to be affected. * + * new_sha1 -- the SHA-1 that should be set to be the new value of + * the reference. Some functions allow this parameter to be + * NULL, meaning that the reference is not changed, or + * null_sha1, meaning that the reference should be deleted. A + * copy of this value is made in the transaction. + * + * old_sha1 -- the SHA-1 value that the reference must have before + * the update. Some functions allow this parameter to be NULL, + * meaning that the old value of the reference is not checked, + * or null_sha1, meaning that the reference must not exist + * before the update. A copy of this value is made in the + * transaction. + * * flags -- flags affecting the update, passed to * update_ref_lock(). Can be REF_NODEREF, which means that * symbolic references should not be followed. From e186057138666058a2c67c3389509c9430d95b24 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:33 +0200 Subject: [PATCH 280/566] ref_iterator_begin_fn(): fix docstring The iterator returned by this function only includes references whose names start with the whole prefix, not all of those in `find_containing_dir(prefix)` as the old docstring claimed. This docstring was probably copy-pasted from old ref-cache code, which had the old specification. But now, `cache_ref_iterator_begin()` (from which the files reference iterator gets its values) automatically wraps its output using `prefix_ref_iterator_begin()` when necessary, so it has the stricter behavior. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/refs-internal.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/refs/refs-internal.h b/refs/refs-internal.h index b6b291cf00..7020e51cb7 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -515,9 +515,10 @@ typedef int rename_ref_fn(struct ref_store *ref_store, const char *logmsg); /* - * Iterate over the references in the specified ref_store that are - * within find_containing_dir(prefix). If prefix is NULL or the empty - * string, iterate over all references in the submodule. + * Iterate over the references in `ref_store` whose names start with + * `prefix`. `prefix` is matched as a literal string, without regard + * for path separators. If prefix is NULL or the empty string, iterate + * over all references in `ref_store`. */ typedef struct ref_iterator *ref_iterator_begin_fn( struct ref_store *ref_store, From 04aea8d4df199836da3802f08cb5738eae66fa6c Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:34 +0200 Subject: [PATCH 281/566] files-backend: use `die("BUG: ...")`, not `die("internal error: ...")` The former is by far more common in our codebase. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index cb1f528cbe..fa5d2b6f08 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -105,7 +105,7 @@ static void clear_packed_ref_cache(struct files_ref_store *refs) struct packed_ref_cache *packed_refs = refs->packed; if (packed_refs->lock) - die("internal error: packed-ref cache cleared while locked"); + die("BUG: packed-ref cache cleared while locked"); refs->packed = NULL; release_packed_ref_cache(packed_refs); } @@ -397,7 +397,7 @@ static void add_packed_ref(struct files_ref_store *refs, struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs); if (!packed_ref_cache->lock) - die("internal error: packed refs not locked"); + die("BUG: packed refs not locked"); add_ref_entry(get_packed_ref_dir(packed_ref_cache), create_ref_entry(refname, oid, REF_ISPACKED, 1)); } @@ -1324,7 +1324,7 @@ static int commit_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "commit_packed_refs"); if (!packed_ref_cache->lock) - die("internal error: packed-refs not locked"); + die("BUG: packed-refs not locked"); out = fdopen_lock_file(packed_ref_cache->lock, "w"); if (!out) @@ -1367,7 +1367,7 @@ static void rollback_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "rollback_packed_refs"); if (!packed_ref_cache->lock) - die("internal error: packed-refs not locked"); + die("BUG: packed-refs not locked"); rollback_lock_file(packed_ref_cache->lock); packed_ref_cache->lock = NULL; release_packed_ref_cache(packed_ref_cache); From b9c8e7f2fb6ee19defeaa2927a0af42b525d8b33 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:35 +0200 Subject: [PATCH 282/566] prefix_ref_iterator: don't trim too much The `trim` parameter can be set independently of `prefix`. So if some caller were to set `trim` to be greater than `strlen(prefix)`, we could end up pointing the `refname` field of the iterator past the NUL of the actual reference name string. That can't happen currently, because `trim` is always set either to zero or to `strlen(prefix)`. But even the latter could lead to confusion, if a refname is exactly equal to the prefix, because then we would set the outgoing `refname` to the empty string. And we're about to decouple the `prefix` and `trim` arguments even more, so let's be cautious here. Report a bug if ever asked to trim a reference whose name is not longer than `trim`. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/iterator.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/refs/iterator.c b/refs/iterator.c index bce1f192f7..4cf449ef66 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -292,7 +292,23 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator) if (!starts_with(iter->iter0->refname, iter->prefix)) continue; - iter->base.refname = iter->iter0->refname + iter->trim; + if (iter->trim) { + /* + * It is nonsense to trim off characters that + * you haven't already checked for via a + * prefix check, whether via this + * `prefix_ref_iterator` or upstream in + * `iter0`). So if there wouldn't be at least + * one character left in the refname after + * trimming, report it as a bug: + */ + if (strlen(iter->iter0->refname) <= iter->trim) + die("BUG: attempt to trim too many characters"); + iter->base.refname = iter->iter0->refname + iter->trim; + } else { + iter->base.refname = iter->iter0->refname; + } + iter->base.oid = iter->iter0->oid; iter->base.flags = iter->iter0->flags; return ITER_OK; From c7599718167de62c437490e9ea300eeb9284a572 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:36 +0200 Subject: [PATCH 283/566] refs_ref_iterator_begin(): don't check prefixes redundantly The backend already correctly restricts its output to references whose names start with the prefix. By passing the prefix again to `prefix_ref_iterator`, we were forcing that iterator to do redundant prefix comparisons. So set it to the empty string. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/refs.c b/refs.c index 8af9641aa1..8494b1f20d 100644 --- a/refs.c +++ b/refs.c @@ -1247,7 +1247,13 @@ struct ref_iterator *refs_ref_iterator_begin( struct ref_iterator *iter; iter = refs->be->iterator_begin(refs, prefix, flags); - iter = prefix_ref_iterator_begin(iter, prefix, trim); + + /* + * `iterator_begin()` already takes care of prefix, but we + * might need to do some trimming: + */ + if (trim) + iter = prefix_ref_iterator_begin(iter, "", trim); return iter; } From 43a2dfde76a4a47ffa31be11fd5cd7fe0b57bb84 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:37 +0200 Subject: [PATCH 284/566] refs: use `size_t` indexes when iterating over ref transaction updates Eliminate any chance of integer overflow on platforms where the two types have different sizes. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 2 +- refs/files-backend.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 8494b1f20d..71139ba74e 100644 --- a/refs.c +++ b/refs.c @@ -848,7 +848,7 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err) void ref_transaction_free(struct ref_transaction *transaction) { - int i; + size_t i; if (!transaction) return; diff --git a/refs/files-backend.c b/refs/files-backend.c index fa5d2b6f08..b2559b5585 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2850,7 +2850,8 @@ static int files_transaction_commit(struct ref_store *ref_store, struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "ref_transaction_commit"); - int ret = 0, i; + size_t i; + int ret = 0; struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; struct string_list_item *ref_to_delete; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; @@ -3057,7 +3058,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "initial_ref_transaction_commit"); - int ret = 0, i; + size_t i; + int ret = 0; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; assert(err); From 64da41993a2c33e9187858808d5a6c87e6d6d101 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:38 +0200 Subject: [PATCH 285/566] ref_store: take a `msg` parameter when deleting references Just because the files backend can't retain reflogs for deleted references is no reason that they shouldn't be supported by the virtual method interface. Also, `delete_ref()` and `refs_delete_ref()` have already gained `msg` parameters. Now let's add them to `delete_refs()` and `refs_delete_refs()`. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- builtin/fetch.c | 2 +- builtin/remote.c | 4 ++-- refs.c | 11 ++++++----- refs.h | 12 +++++++----- refs/files-backend.c | 4 ++-- refs/refs-internal.h | 2 +- t/helper/test-ref-store.c | 3 ++- t/t1405-main-ref-store.sh | 2 +- t/t1406-submodule-ref-store.sh | 2 +- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index d4d573b985..47708451bc 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -941,7 +941,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, for (ref = stale_refs; ref; ref = ref->next) string_list_append(&refnames, ref->name); - result = delete_refs(&refnames, 0); + result = delete_refs("fetch: prune", &refnames, 0); string_list_clear(&refnames, 0); } diff --git a/builtin/remote.c b/builtin/remote.c index addf97ad29..5f52c5a6d6 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -786,7 +786,7 @@ static int rm(int argc, const char **argv) strbuf_release(&buf); if (!result) - result = delete_refs(&branches, REF_NODEREF); + result = delete_refs("remote: remove", &branches, REF_NODEREF); string_list_clear(&branches, 0); if (skipped.nr) { @@ -1301,7 +1301,7 @@ static int prune_remote(const char *remote, int dry_run) string_list_sort(&refs_to_prune); if (!dry_run) - result |= delete_refs(&refs_to_prune, 0); + result |= delete_refs("remote: prune", &refs_to_prune, 0); for_each_string_list_item(item, &states.stale) { const char *refname = item->util; diff --git a/refs.c b/refs.c index 71139ba74e..989462c972 100644 --- a/refs.c +++ b/refs.c @@ -1902,15 +1902,16 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, return refs->be->initial_transaction_commit(refs, transaction, err); } -int refs_delete_refs(struct ref_store *refs, struct string_list *refnames, - unsigned int flags) +int refs_delete_refs(struct ref_store *refs, const char *msg, + struct string_list *refnames, unsigned int flags) { - return refs->be->delete_refs(refs, refnames, flags); + return refs->be->delete_refs(refs, msg, refnames, flags); } -int delete_refs(struct string_list *refnames, unsigned int flags) +int delete_refs(const char *msg, struct string_list *refnames, + unsigned int flags) { - return refs_delete_refs(get_main_ref_store(), refnames, flags); + return refs_delete_refs(get_main_ref_store(), msg, refnames, flags); } int refs_rename_ref(struct ref_store *refs, const char *oldref, diff --git a/refs.h b/refs.h index ec8c6bfbbb..b62722fb81 100644 --- a/refs.h +++ b/refs.h @@ -331,7 +331,8 @@ int reflog_exists(const char *refname); * verify that the current value of the reference is old_sha1 before * deleting it. If old_sha1 is NULL, delete the reference if it * exists, regardless of its old value. It is an error for old_sha1 to - * be NULL_SHA1. flags is passed through to ref_transaction_delete(). + * be NULL_SHA1. msg and flags are passed through to + * ref_transaction_delete(). */ int refs_delete_ref(struct ref_store *refs, const char *msg, const char *refname, @@ -343,12 +344,13 @@ int delete_ref(const char *msg, const char *refname, /* * Delete the specified references. If there are any problems, emit * errors but attempt to keep going (i.e., the deletes are not done in - * an all-or-nothing transaction). flags is passed through to + * an all-or-nothing transaction). msg and flags are passed through to * ref_transaction_delete(). */ -int refs_delete_refs(struct ref_store *refs, struct string_list *refnames, - unsigned int flags); -int delete_refs(struct string_list *refnames, unsigned int flags); +int refs_delete_refs(struct ref_store *refs, const char *msg, + struct string_list *refnames, unsigned int flags); +int delete_refs(const char *msg, struct string_list *refnames, + unsigned int flags); /** Delete a reflog */ int refs_delete_reflog(struct ref_store *refs, const char *refname); diff --git a/refs/files-backend.c b/refs/files-backend.c index b2559b5585..fce8265aa7 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1595,7 +1595,7 @@ static int repack_without_refs(struct files_ref_store *refs, return ret; } -static int files_delete_refs(struct ref_store *ref_store, +static int files_delete_refs(struct ref_store *ref_store, const char *msg, struct string_list *refnames, unsigned int flags) { struct files_ref_store *refs = @@ -1627,7 +1627,7 @@ static int files_delete_refs(struct ref_store *ref_store, for (i = 0; i < refnames->nr; i++) { const char *refname = refnames->items[i].string; - if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags)) + if (refs_delete_ref(&refs->base, msg, refname, NULL, flags)) result |= error(_("could not remove reference %s"), refname); } diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 7020e51cb7..95edf6f234 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -508,7 +508,7 @@ typedef int create_symref_fn(struct ref_store *ref_store, const char *ref_target, const char *refs_heads_master, const char *logmsg); -typedef int delete_refs_fn(struct ref_store *ref_store, +typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg, struct string_list *refnames, unsigned int flags); typedef int rename_ref_fn(struct ref_store *ref_store, const char *oldref, const char *newref, diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index fba85e7da5..05d8c4d8af 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -93,12 +93,13 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv) static int cmd_delete_refs(struct ref_store *refs, const char **argv) { unsigned int flags = arg_flags(*argv++, "flags"); + const char *msg = *argv++; struct string_list refnames = STRING_LIST_INIT_NODUP; while (*argv) string_list_append(&refnames, *argv++); - return refs_delete_refs(refs, &refnames, flags); + return refs_delete_refs(refs, msg, &refnames, flags); } static int cmd_rename_ref(struct ref_store *refs, const char **argv) diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh index 490521f8cb..e8115df5ba 100755 --- a/t/t1405-main-ref-store.sh +++ b/t/t1405-main-ref-store.sh @@ -31,7 +31,7 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' ' test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' ' git rev-parse FOO -- && git rev-parse refs/tags/new-tag -- && - $RUN delete-refs 0 FOO refs/tags/new-tag && + $RUN delete-refs 0 nothing FOO refs/tags/new-tag && test_must_fail git rev-parse FOO -- && test_must_fail git rev-parse refs/tags/new-tag -- ' diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh index 13b5454c56..c32d4cc465 100755 --- a/t/t1406-submodule-ref-store.sh +++ b/t/t1406-submodule-ref-store.sh @@ -31,7 +31,7 @@ test_expect_success 'create_symref() not allowed' ' ' test_expect_success 'delete_refs() not allowed' ' - test_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag + test_must_fail $RUN delete-refs 0 nothing FOO refs/tags/new-tag ' test_expect_success 'rename_refs() not allowed' ' From 0978f4ba7fe571d96b9f13827bdac6c30eeebfa2 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:39 +0200 Subject: [PATCH 286/566] lockfile: add a new method, is_lock_file_locked() It will soon prove useful. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- lockfile.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lockfile.h b/lockfile.h index 7b715f9e77..572064939c 100644 --- a/lockfile.h +++ b/lockfile.h @@ -175,6 +175,14 @@ static inline int hold_lock_file_for_update( return hold_lock_file_for_update_timeout(lk, path, flags, 0); } +/* + * Return a nonzero value iff `lk` is currently locked. + */ +static inline int is_lock_file_locked(struct lock_file *lk) +{ + return is_tempfile_active(&lk->tempfile); +} + /* * Append an appropriate error message to `buf` following the failure * of `hold_lock_file_for_update()` to lock `path`. `err` should be the From 55c6bc37c90e134e9da39b8cce1f3084318374d3 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:40 +0200 Subject: [PATCH 287/566] files-backend: move `lock` member to `files_ref_store` Move the `lock` member from `packed_ref_cache` to `files_ref_store`, since at most one cache can have a locked "packed-refs" file associated with it. Rename it to `packed_refs_lock` to make its purpose clearer in its new home. More changes are coming here shortly. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index fce8265aa7..bfc555a417 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -43,15 +43,6 @@ struct packed_ref_cache { */ unsigned int referrers; - /* - * Iff the packed-refs file associated with this instance is - * currently locked for writing, this points at the associated - * lock (which is owned by somebody else). The referrer count - * is also incremented when the file is locked and decremented - * when it is unlocked. - */ - struct lock_file *lock; - /* The metadata from when this packed-refs cache was read */ struct stat_validity validity; }; @@ -70,6 +61,13 @@ struct files_ref_store { struct ref_cache *loose; struct packed_ref_cache *packed; + + /* + * Iff the packed-refs file associated with this instance is + * currently locked for writing, this points at the associated + * lock (which is owned by somebody else). + */ + struct lock_file *packed_refs_lock; }; /* Lock used for the main packed-refs file: */ @@ -104,7 +102,7 @@ static void clear_packed_ref_cache(struct files_ref_store *refs) if (refs->packed) { struct packed_ref_cache *packed_refs = refs->packed; - if (packed_refs->lock) + if (refs->packed_refs_lock) die("BUG: packed-ref cache cleared while locked"); refs->packed = NULL; release_packed_ref_cache(packed_refs); @@ -396,7 +394,7 @@ static void add_packed_ref(struct files_ref_store *refs, { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs); - if (!packed_ref_cache->lock) + if (!refs->packed_refs_lock) die("BUG: packed refs not locked"); add_ref_entry(get_packed_ref_dir(packed_ref_cache), create_ref_entry(refname, oid, REF_ISPACKED, 1)); @@ -1300,7 +1298,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags) * the packed-refs file. */ packed_ref_cache = get_packed_ref_cache(refs); - packed_ref_cache->lock = &packlock; + refs->packed_refs_lock = &packlock; /* Increment the reference count to prevent it from being freed: */ acquire_packed_ref_cache(packed_ref_cache); return 0; @@ -1323,10 +1321,10 @@ static int commit_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "commit_packed_refs"); - if (!packed_ref_cache->lock) + if (!refs->packed_refs_lock) die("BUG: packed-refs not locked"); - out = fdopen_lock_file(packed_ref_cache->lock, "w"); + out = fdopen_lock_file(refs->packed_refs_lock, "w"); if (!out) die_errno("unable to fdopen packed-refs descriptor"); @@ -1344,11 +1342,11 @@ static int commit_packed_refs(struct files_ref_store *refs) if (ok != ITER_DONE) die("error while iterating over references"); - if (commit_lock_file(packed_ref_cache->lock)) { + if (commit_lock_file(refs->packed_refs_lock)) { save_errno = errno; error = -1; } - packed_ref_cache->lock = NULL; + refs->packed_refs_lock = NULL; release_packed_ref_cache(packed_ref_cache); errno = save_errno; return error; @@ -1366,10 +1364,10 @@ static void rollback_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "rollback_packed_refs"); - if (!packed_ref_cache->lock) + if (!refs->packed_refs_lock) die("BUG: packed-refs not locked"); - rollback_lock_file(packed_ref_cache->lock); - packed_ref_cache->lock = NULL; + rollback_lock_file(refs->packed_refs_lock); + refs->packed_refs_lock = NULL; release_packed_ref_cache(packed_ref_cache); clear_packed_ref_cache(refs); } From 00d174489e9905411dbae5f895758f2ca489bd9f Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:41 +0200 Subject: [PATCH 288/566] files_ref_store: put the packed files lock directly in this struct Instead of using a global `lock_file` instance for the main "packed-refs" file and using a pointer in `files_ref_store` to keep track of whether it is locked, embed the `lock_file` instance directly in the `files_ref_store` struct and use the new `is_lock_file_locked()` function to keep track of whether it is locked. This keeps related data together and makes the main reference store less of a special case. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index bfc555a417..1db40432af 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -63,16 +63,12 @@ struct files_ref_store { struct packed_ref_cache *packed; /* - * Iff the packed-refs file associated with this instance is - * currently locked for writing, this points at the associated - * lock (which is owned by somebody else). + * Lock used for the "packed-refs" file. Note that this (and + * thus the enclosing `files_ref_store`) must not be freed. */ - struct lock_file *packed_refs_lock; + struct lock_file packed_refs_lock; }; -/* Lock used for the main packed-refs file: */ -static struct lock_file packlock; - /* * Increment the reference count of *packed_refs. */ @@ -102,7 +98,7 @@ static void clear_packed_ref_cache(struct files_ref_store *refs) if (refs->packed) { struct packed_ref_cache *packed_refs = refs->packed; - if (refs->packed_refs_lock) + if (is_lock_file_locked(&refs->packed_refs_lock)) die("BUG: packed-ref cache cleared while locked"); refs->packed = NULL; release_packed_ref_cache(packed_refs); @@ -394,7 +390,7 @@ static void add_packed_ref(struct files_ref_store *refs, { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs); - if (!refs->packed_refs_lock) + if (!is_lock_file_locked(&refs->packed_refs_lock)) die("BUG: packed refs not locked"); add_ref_entry(get_packed_ref_dir(packed_ref_cache), create_ref_entry(refname, oid, REF_ISPACKED, 1)); @@ -1288,7 +1284,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags) } if (hold_lock_file_for_update_timeout( - &packlock, files_packed_refs_path(refs), + &refs->packed_refs_lock, files_packed_refs_path(refs), flags, timeout_value) < 0) return -1; /* @@ -1298,7 +1294,6 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags) * the packed-refs file. */ packed_ref_cache = get_packed_ref_cache(refs); - refs->packed_refs_lock = &packlock; /* Increment the reference count to prevent it from being freed: */ acquire_packed_ref_cache(packed_ref_cache); return 0; @@ -1321,10 +1316,10 @@ static int commit_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "commit_packed_refs"); - if (!refs->packed_refs_lock) + if (!is_lock_file_locked(&refs->packed_refs_lock)) die("BUG: packed-refs not locked"); - out = fdopen_lock_file(refs->packed_refs_lock, "w"); + out = fdopen_lock_file(&refs->packed_refs_lock, "w"); if (!out) die_errno("unable to fdopen packed-refs descriptor"); @@ -1342,11 +1337,10 @@ static int commit_packed_refs(struct files_ref_store *refs) if (ok != ITER_DONE) die("error while iterating over references"); - if (commit_lock_file(refs->packed_refs_lock)) { + if (commit_lock_file(&refs->packed_refs_lock)) { save_errno = errno; error = -1; } - refs->packed_refs_lock = NULL; release_packed_ref_cache(packed_ref_cache); errno = save_errno; return error; @@ -1364,10 +1358,9 @@ static void rollback_packed_refs(struct files_ref_store *refs) files_assert_main_repository(refs, "rollback_packed_refs"); - if (!refs->packed_refs_lock) + if (!is_lock_file_locked(&refs->packed_refs_lock)) die("BUG: packed-refs not locked"); - rollback_lock_file(refs->packed_refs_lock); - refs->packed_refs_lock = NULL; + rollback_lock_file(&refs->packed_refs_lock); release_packed_ref_cache(packed_ref_cache); clear_packed_ref_cache(refs); } From c0ca9357640ae5efbdbfed4c5b476c820a839e85 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:42 +0200 Subject: [PATCH 289/566] files_transaction_cleanup(): new helper function Extract the cleanup functionality from `files_transaction_commit()` into a new function. It will soon have another caller. Use the common cleanup code even on early exit if the transaction is empty, to reduce code duplication. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 1db40432af..2c70de5209 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2834,6 +2834,27 @@ static int lock_ref_for_update(struct files_ref_store *refs, return 0; } +/* + * Unlock any references in `transaction` that are still locked, and + * mark the transaction closed. + */ +static void files_transaction_cleanup(struct ref_transaction *transaction) +{ + size_t i; + + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct ref_lock *lock = update->backend_data; + + if (lock) { + unlock_ref(lock); + update->backend_data = NULL; + } + } + + transaction->state = REF_TRANSACTION_CLOSED; +} + static int files_transaction_commit(struct ref_store *ref_store, struct ref_transaction *transaction, struct strbuf *err) @@ -2856,10 +2877,8 @@ static int files_transaction_commit(struct ref_store *ref_store, if (transaction->state != REF_TRANSACTION_OPEN) die("BUG: commit called for transaction that is not open"); - if (!transaction->nr) { - transaction->state = REF_TRANSACTION_CLOSED; - return 0; - } + if (!transaction->nr) + goto cleanup; /* * Fail if a refname appears more than once in the @@ -3005,15 +3024,11 @@ static int files_transaction_commit(struct ref_store *ref_store, clear_loose_ref_cache(refs); cleanup: + files_transaction_cleanup(transaction); strbuf_release(&sb); - transaction->state = REF_TRANSACTION_CLOSED; for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - struct ref_lock *lock = update->backend_data; - - if (lock) - unlock_ref(lock); if (update->flags & REF_DELETED_LOOSE) { /* From 8d4240d3c8a2d31b7bedda8408c0b3c217c76998 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:43 +0200 Subject: [PATCH 290/566] ref_transaction_commit(): check for valid `transaction->state` Move the check that `transaction->state` is valid from `files_transaction_commit()` to `ref_transaction_commit()`, where other future reference backends can benefit from it as well. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 12 ++++++++++++ refs/files-backend.c | 3 --- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 989462c972..f8f41ffb04 100644 --- a/refs.c +++ b/refs.c @@ -1694,6 +1694,18 @@ int ref_transaction_commit(struct ref_transaction *transaction, { struct ref_store *refs = transaction->ref_store; + switch (transaction->state) { + case REF_TRANSACTION_OPEN: + /* Good. */ + break; + case REF_TRANSACTION_CLOSED: + die("BUG: prepare called on a closed reference transaction"); + break; + default: + die("BUG: unexpected reference transaction state"); + break; + } + if (getenv(GIT_QUARANTINE_ENVIRONMENT)) { strbuf_addstr(err, _("ref updates forbidden inside quarantine environment")); diff --git a/refs/files-backend.c b/refs/files-backend.c index 2c70de5209..a4261d4683 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2874,9 +2874,6 @@ static int files_transaction_commit(struct ref_store *ref_store, assert(err); - if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: commit called for transaction that is not open"); - if (!transaction->nr) goto cleanup; From 30173b8851bb7203de938a638386cb9e6d7c501b Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:44 +0200 Subject: [PATCH 291/566] ref_transaction_prepare(): new optional step for reference updates In the future, compound reference stores will sometimes need to modify references in two different reference stores at the same time, meaning that a single logical reference transaction might have to be implemented as two internal sub-transactions. They won't want to call `ref_transaction_commit()` for the two sub-transactions one after the other, because that wouldn't be atomic (the first commit could succeed and the second one fail). Instead, they will want to prepare both sub-transactions (i.e., obtain any necessary locks and do any pre-checks), and only if both prepare steps succeed, then commit both sub-transactions. Start preparing for that day by adding a new, optional `ref_transaction_prepare()` step to the reference transaction sequence, which obtains the locks and does any prechecks, reporting any errors that occur. Also add a `ref_transaction_abort()` function that can be used to abort a sub-transaction even if it has already been prepared. That is on the side of the public-facing API. On the side of the `ref_store` VTABLE, get rid of `transaction_commit` and instead add methods `transaction_prepare`, `transaction_finish`, and `transaction_abort`. A `ref_transaction_commit()` now basically calls methods `transaction_prepare` then `transaction_finish`. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 74 +++++++++++++++++++++++++-- refs.h | 118 ++++++++++++++++++++++++++++++++++--------- refs/files-backend.c | 63 ++++++++++++++++++----- refs/refs-internal.h | 45 +++++++++++++---- 4 files changed, 250 insertions(+), 50 deletions(-) diff --git a/refs.c b/refs.c index f8f41ffb04..b3860a9e33 100644 --- a/refs.c +++ b/refs.c @@ -853,6 +853,19 @@ void ref_transaction_free(struct ref_transaction *transaction) if (!transaction) return; + switch (transaction->state) { + case REF_TRANSACTION_OPEN: + case REF_TRANSACTION_CLOSED: + /* OK */ + break; + case REF_TRANSACTION_PREPARED: + die("BUG: free called on a prepared reference transaction"); + break; + default: + die("BUG: unexpected reference transaction state"); + break; + } + for (i = 0; i < transaction->nr; i++) { free(transaction->updates[i]->msg); free(transaction->updates[i]); @@ -1689,8 +1702,8 @@ int create_symref(const char *ref_target, const char *refs_heads_master, refs_heads_master, logmsg); } -int ref_transaction_commit(struct ref_transaction *transaction, - struct strbuf *err) +int ref_transaction_prepare(struct ref_transaction *transaction, + struct strbuf *err) { struct ref_store *refs = transaction->ref_store; @@ -1698,6 +1711,9 @@ int ref_transaction_commit(struct ref_transaction *transaction, case REF_TRANSACTION_OPEN: /* Good. */ break; + case REF_TRANSACTION_PREPARED: + die("BUG: prepare called twice on reference transaction"); + break; case REF_TRANSACTION_CLOSED: die("BUG: prepare called on a closed reference transaction"); break; @@ -1712,7 +1728,59 @@ int ref_transaction_commit(struct ref_transaction *transaction, return -1; } - return refs->be->transaction_commit(refs, transaction, err); + return refs->be->transaction_prepare(refs, transaction, err); +} + +int ref_transaction_abort(struct ref_transaction *transaction, + struct strbuf *err) +{ + struct ref_store *refs = transaction->ref_store; + int ret = 0; + + switch (transaction->state) { + case REF_TRANSACTION_OPEN: + /* No need to abort explicitly. */ + break; + case REF_TRANSACTION_PREPARED: + ret = refs->be->transaction_abort(refs, transaction, err); + break; + case REF_TRANSACTION_CLOSED: + die("BUG: abort called on a closed reference transaction"); + break; + default: + die("BUG: unexpected reference transaction state"); + break; + } + + ref_transaction_free(transaction); + return ret; +} + +int ref_transaction_commit(struct ref_transaction *transaction, + struct strbuf *err) +{ + struct ref_store *refs = transaction->ref_store; + int ret; + + switch (transaction->state) { + case REF_TRANSACTION_OPEN: + /* Need to prepare first. */ + ret = ref_transaction_prepare(transaction, err); + if (ret) + return ret; + break; + case REF_TRANSACTION_PREPARED: + /* Fall through to finish. */ + break; + case REF_TRANSACTION_CLOSED: + die("BUG: commit called on a closed reference transaction"); + break; + default: + die("BUG: unexpected reference transaction state"); + break; + } + + return refs->be->transaction_finish(refs, transaction, err); } int refs_verify_refname_available(struct ref_store *refs, diff --git a/refs.h b/refs.h index b62722fb81..4be14c4b3c 100644 --- a/refs.h +++ b/refs.h @@ -143,30 +143,71 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); /* - * A ref_transaction represents a collection of ref updates - * that should succeed or fail together. + * A ref_transaction represents a collection of reference updates that + * should succeed or fail together. * * Calling sequence * ---------------- + * * - Allocate and initialize a `struct ref_transaction` by calling * `ref_transaction_begin()`. * - * - List intended ref updates by calling functions like - * `ref_transaction_update()` and `ref_transaction_create()`. + * - Specify the intended ref updates by calling one or more of the + * following functions: + * - `ref_transaction_update()` + * - `ref_transaction_create()` + * - `ref_transaction_delete()` + * - `ref_transaction_verify()` * - * - Call `ref_transaction_commit()` to execute the transaction. - * If this succeeds, the ref updates will have taken place and - * the transaction cannot be rolled back. + * - Then either: * - * - Instead of `ref_transaction_commit`, use - * `initial_ref_transaction_commit()` if the ref database is known - * to be empty (e.g. during clone). This is likely to be much - * faster. + * - Optionally call `ref_transaction_prepare()` to prepare the + * transaction. This locks all references, checks preconditions, + * etc. but doesn't finalize anything. If this step fails, the + * transaction has been closed and can only be freed. If this step + * succeeds, then `ref_transaction_commit()` is almost certain to + * succeed. However, you can still call `ref_transaction_abort()` + * if you decide not to commit the transaction after all. * - * - At any time call `ref_transaction_free()` to discard the - * transaction and free associated resources. In particular, - * this rolls back the transaction if it has not been - * successfully committed. + * - Call `ref_transaction_commit()` to execute the transaction, + * make the changes permanent, and release all locks. If you + * haven't already called `ref_transaction_prepare()`, then + * `ref_transaction_commit()` calls it for you. + * + * Or + * + * - Call `initial_ref_transaction_commit()` if the ref database is + * known to be empty and have no other writers (e.g. during + * clone). This is likely to be much faster than + * `ref_transaction_commit()`. `ref_transaction_prepare()` should + * *not* be called before `initial_ref_transaction_commit()`. + * + * - Then finally, call `ref_transaction_free()` to free the + * `ref_transaction` data structure. + * + * At any time before calling `ref_transaction_commit()`, you can call + * `ref_transaction_abort()` to abort the transaction, rollback any + * locks, and free any associated resources (including the + * `ref_transaction` data structure). + * + * Putting it all together, a complete reference update looks like + * + * struct ref_transaction *transaction; + * struct strbuf err = STRBUF_INIT; + * int ret = 0; + * + * transaction = ref_store_transaction_begin(refs, &err); + * if (!transaction || + * ref_transaction_update(...) || + * ref_transaction_create(...) || + * ...etc... || + * ref_transaction_commit(transaction, &err)) { + * error("%s", err.buf); + * ret = -1; + * } + * ref_transaction_free(transaction); + * strbuf_release(&err); + * return ret; * * Error handling * -------------- @@ -183,8 +224,9 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); * ------- * * Note that no locks are taken, and no refs are read, until - * `ref_transaction_commit` is called. So `ref_transaction_verify` - * won't report a verification failure until the commit is attempted. + * `ref_transaction_prepare()` or `ref_transaction_commit()` is + * called. So, for example, `ref_transaction_verify()` won't report a + * verification failure until the commit is attempted. */ struct ref_transaction; @@ -523,19 +565,47 @@ int ref_transaction_verify(struct ref_transaction *transaction, unsigned int flags, struct strbuf *err); -/* - * Commit all of the changes that have been queued in transaction, as - * atomically as possible. - * - * Returns 0 for success, or one of the below error codes for errors. - */ /* Naming conflict (for example, the ref names A and A/B conflict). */ #define TRANSACTION_NAME_CONFLICT -1 /* All other errors. */ #define TRANSACTION_GENERIC_ERROR -2 + +/* + * Perform the preparatory stages of commiting `transaction`. Acquire + * any needed locks, check preconditions, etc.; basically, do as much + * as possible to ensure that the transaction will be able to go + * through, stopping just short of making any irrevocable or + * user-visible changes. The updates that this function prepares can + * be finished up by calling `ref_transaction_commit()` or rolled back + * by calling `ref_transaction_abort()`. + * + * On success, return 0 and leave the transaction in "prepared" state. + * On failure, abort the transaction, write an error message to `err`, + * and return one of the `TRANSACTION_*` constants. + * + * Callers who don't need such fine-grained control over commiting + * reference transactions should just call `ref_transaction_commit()`. + */ +int ref_transaction_prepare(struct ref_transaction *transaction, + struct strbuf *err); + +/* + * Commit all of the changes that have been queued in transaction, as + * atomically as possible. On success, return 0 and leave the + * transaction in "closed" state. On failure, roll back the + * transaction, write an error message to `err`, and return one of the + * `TRANSACTION_*` constants + */ int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err); +/* + * Abort `transaction`, which has been begun and possibly prepared, + * but not yet committed. + */ +int ref_transaction_abort(struct ref_transaction *transaction, + struct strbuf *err); + /* * Like ref_transaction_commit(), but optimized for creating * references when originally initializing a repository (e.g., by "git @@ -551,7 +621,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err); /* - * Free an existing transaction and all associated data. + * Free `*transaction` and all associated data. */ void ref_transaction_free(struct ref_transaction *transaction); diff --git a/refs/files-backend.c b/refs/files-backend.c index a4261d4683..19842d2e56 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2855,22 +2855,19 @@ static void files_transaction_cleanup(struct ref_transaction *transaction) transaction->state = REF_TRANSACTION_CLOSED; } -static int files_transaction_commit(struct ref_store *ref_store, - struct ref_transaction *transaction, - struct strbuf *err) +static int files_transaction_prepare(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, - "ref_transaction_commit"); + "ref_transaction_prepare"); size_t i; int ret = 0; - struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; - struct string_list_item *ref_to_delete; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; char *head_ref = NULL; int head_type; struct object_id head_oid; - struct strbuf sb = STRBUF_INIT; assert(err); @@ -2934,6 +2931,8 @@ static int files_transaction_commit(struct ref_store *ref_store, * that new values are valid, and write new values to the * lockfiles, ready to be activated. Only keep one lockfile * open at a time to avoid running out of file descriptors. + * Note that lock_ref_for_update() might append more updates + * to the transaction. */ for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; @@ -2941,7 +2940,38 @@ static int files_transaction_commit(struct ref_store *ref_store, ret = lock_ref_for_update(refs, update, transaction, head_ref, &affected_refnames, err); if (ret) - goto cleanup; + break; + } + +cleanup: + free(head_ref); + string_list_clear(&affected_refnames, 0); + + if (ret) + files_transaction_cleanup(transaction); + else + transaction->state = REF_TRANSACTION_PREPARED; + + return ret; +} + +static int files_transaction_finish(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + struct files_ref_store *refs = + files_downcast(ref_store, 0, "ref_transaction_finish"); + size_t i; + int ret = 0; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; + struct strbuf sb = STRBUF_INIT; + + assert(err); + + if (!transaction->nr) { + transaction->state = REF_TRANSACTION_CLOSED; + return 0; } /* Perform updates first so live commits remain referenced */ @@ -3022,7 +3052,6 @@ static int files_transaction_commit(struct ref_store *ref_store, cleanup: files_transaction_cleanup(transaction); - strbuf_release(&sb); for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; @@ -3039,13 +3068,19 @@ cleanup: } } + strbuf_release(&sb); string_list_clear(&refs_to_delete, 0); - free(head_ref); - string_list_clear(&affected_refnames, 0); - return ret; } +static int files_transaction_abort(struct ref_store *ref_store, + struct ref_transaction *transaction, + struct strbuf *err) +{ + files_transaction_cleanup(transaction); + return 0; +} + static int ref_present(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -3316,7 +3351,9 @@ struct ref_storage_be refs_be_files = { "files", files_ref_store_create, files_init_db, - files_transaction_commit, + files_transaction_prepare, + files_transaction_finish, + files_transaction_abort, files_initial_transaction_commit, files_pack_refs, diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 95edf6f234..4d3dd17f9f 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -185,17 +185,27 @@ struct ref_update *ref_transaction_add_update( /* * Transaction states. - * OPEN: The transaction is in a valid state and can accept new updates. - * An OPEN transaction can be committed. - * CLOSED: A closed transaction is no longer active and no other operations - * than free can be used on it in this state. - * A transaction can either become closed by successfully committing - * an active transaction or if there is a failure while building - * the transaction thus rendering it failed/inactive. + * + * OPEN: The transaction is initialized and new updates can still be + * added to it. An OPEN transaction can be prepared, + * committed, freed, or aborted (freeing and aborting an open + * transaction are equivalent). + * + * PREPARED: ref_transaction_prepare(), which locks all of the + * references involved in the update and checks that the + * update has no errors, has been called successfully for the + * transaction. A PREPARED transaction can be committed or + * aborted. + * + * CLOSED: The transaction is no longer active. A transaction becomes + * CLOSED if there is a failure while building the transaction + * or if a transaction is committed or aborted. A CLOSED + * transaction can only be freed. */ enum ref_transaction_state { - REF_TRANSACTION_OPEN = 0, - REF_TRANSACTION_CLOSED = 1 + REF_TRANSACTION_OPEN = 0, + REF_TRANSACTION_PREPARED = 1, + REF_TRANSACTION_CLOSED = 2 }; /* @@ -497,6 +507,18 @@ typedef struct ref_store *ref_store_init_fn(const char *gitdir, typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err); +typedef int ref_transaction_prepare_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_finish_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + +typedef int ref_transaction_abort_fn(struct ref_store *refs, + struct ref_transaction *transaction, + struct strbuf *err); + typedef int ref_transaction_commit_fn(struct ref_store *refs, struct ref_transaction *transaction, struct strbuf *err); @@ -600,7 +622,10 @@ struct ref_storage_be { const char *name; ref_store_init_fn *init; ref_init_db_fn *init_db; - ref_transaction_commit_fn *transaction_commit; + + ref_transaction_prepare_fn *transaction_prepare; + ref_transaction_finish_fn *transaction_finish; + ref_transaction_abort_fn *transaction_abort; ref_transaction_commit_fn *initial_transaction_commit; pack_refs_fn *pack_refs; From 2ced105cb1df064b9400aef5f4d35e20026ab267 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:45 +0200 Subject: [PATCH 292/566] ref_update_reject_duplicates(): expose function to whole refs module It will soon have some other users. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 17 +++++++++++++++++ refs/files-backend.c | 17 ----------------- refs/refs-internal.h | 8 ++++++++ 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/refs.c b/refs.c index b3860a9e33..beb49fb297 100644 --- a/refs.c +++ b/refs.c @@ -1702,6 +1702,23 @@ int create_symref(const char *ref_target, const char *refs_heads_master, refs_heads_master, logmsg); } +int ref_update_reject_duplicates(struct string_list *refnames, + struct strbuf *err) +{ + int i, n = refnames->nr; + + assert(err); + + for (i = 1; i < n; i++) + if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) { + strbuf_addf(err, + "multiple updates for ref '%s' not allowed.", + refnames->items[i].string); + return 1; + } + return 0; +} + int ref_transaction_prepare(struct ref_transaction *transaction, struct strbuf *err) { diff --git a/refs/files-backend.c b/refs/files-backend.c index 19842d2e56..8d0ce739a6 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2512,23 +2512,6 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st return ref_iterator; } -static int ref_update_reject_duplicates(struct string_list *refnames, - struct strbuf *err) -{ - int i, n = refnames->nr; - - assert(err); - - for (i = 1; i < n; i++) - if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) { - strbuf_addf(err, - "multiple updates for ref '%s' not allowed.", - refnames->items[i].string); - return 1; - } - return 0; -} - /* * If update is a direct update of head_ref (the reference pointed to * by HEAD), then add an extra REF_LOG_ONLY update for HEAD. diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 4d3dd17f9f..192f9f85c9 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -169,6 +169,14 @@ int refs_read_raw_ref(struct ref_store *ref_store, const char *refname, unsigned char *sha1, struct strbuf *referent, unsigned int *type); +/* + * Write an error to `err` and return a nonzero value iff the same + * refname appears multiple times in `refnames`. `refnames` must be + * sorted on entry to this function. + */ +int ref_update_reject_duplicates(struct string_list *refnames, + struct strbuf *err); + /* * Add a ref_update with the specified properties to transaction, and * return a pointer to the new object. This function does not verify From a552e50e5a82b9219c6b2911320931417a36af32 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:46 +0200 Subject: [PATCH 293/566] ref_update_reject_duplicates(): use `size_t` rather than `int` Eliminate a theoretical risk of integer overflow if the two types have different sizes. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refs.c b/refs.c index beb49fb297..143936a9c3 100644 --- a/refs.c +++ b/refs.c @@ -1705,7 +1705,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master, int ref_update_reject_duplicates(struct string_list *refnames, struct strbuf *err) { - int i, n = refnames->nr; + size_t i, n = refnames->nr; assert(err); From 8556f8d61330ec677dc48b0ef39e2017d6927708 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:47 +0200 Subject: [PATCH 294/566] ref_update_reject_duplicates(): add a sanity check It's pretty cheap to make sure that the caller didn't pass us an unsorted list by accident, so do so. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/refs.c b/refs.c index 143936a9c3..d1c781d94e 100644 --- a/refs.c +++ b/refs.c @@ -1709,13 +1709,19 @@ int ref_update_reject_duplicates(struct string_list *refnames, assert(err); - for (i = 1; i < n; i++) - if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) { + for (i = 1; i < n; i++) { + int cmp = strcmp(refnames->items[i - 1].string, + refnames->items[i].string); + + if (!cmp) { strbuf_addf(err, "multiple updates for ref '%s' not allowed.", refnames->items[i].string); return 1; + } else if (cmp > 0) { + die("BUG: ref_update_reject_duplicates() received unsorted list"); } + } return 0; } From 531cc4a56da1eff87cb57d90012197eb6d721edd Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:48 +0200 Subject: [PATCH 295/566] should_pack_ref(): new function, extracted from `files_pack_refs()` Extract a function for deciding whether a reference should be packed. It is a self-contained bit of logic, so splitting it out improves readability. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 8d0ce739a6..29514392b0 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1455,6 +1455,32 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r) } } +/* + * Return true if the specified reference should be packed. + */ +static int should_pack_ref(const char *refname, + const struct object_id *oid, unsigned int ref_flags, + unsigned int pack_flags) +{ + /* Do not pack per-worktree refs: */ + if (ref_type(refname) != REF_TYPE_NORMAL) + return 0; + + /* Do not pack non-tags unless PACK_REFS_ALL is set: */ + if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/")) + return 0; + + /* Do not pack symbolic refs: */ + if (ref_flags & REF_ISSYMREF) + return 0; + + /* Do not pack broken refs: */ + if (!ref_resolves_to_object(refname, oid, ref_flags)) + return 0; + + return 1; +} + static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) { struct files_ref_store *refs = @@ -1476,21 +1502,9 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) * pruned, also add it to refs_to_prune. */ struct ref_entry *packed_entry; - int is_tag_ref = starts_with(iter->refname, "refs/tags/"); - /* Do not pack per-worktree refs: */ - if (ref_type(iter->refname) != REF_TYPE_NORMAL) - continue; - - /* ALWAYS pack tags */ - if (!(flags & PACK_REFS_ALL) && !is_tag_ref) - continue; - - /* Do not pack symbolic or broken refs: */ - if (iter->flags & REF_ISSYMREF) - continue; - - if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags)) + if (!should_pack_ref(iter->refname, iter->oid, iter->flags, + flags)) continue; /* From 28ed9830b1dc65df37bdb3c9b8ef743f1f266262 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:49 +0200 Subject: [PATCH 296/566] get_packed_ref_cache(): assume "packed-refs" won't change while locked If we've got the "packed-refs" file locked, then it can't change; there's no need to keep calling `stat_validity_check()` on it. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 29514392b0..c4bc9550d3 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -342,13 +342,18 @@ static void files_ref_path(struct files_ref_store *refs, /* * Get the packed_ref_cache for the specified files_ref_store, - * creating it if necessary. + * creating and populating it if it hasn't been read before or if the + * file has been changed (according to its `validity` field) since it + * was last read. On the other hand, if we hold the lock, then assume + * that the file hasn't been changed out from under us, so skip the + * extra `stat()` call in `stat_validity_check()`. */ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs) { const char *packed_refs_file = files_packed_refs_path(refs); if (refs->packed && + !is_lock_file_locked(&refs->packed_refs_lock) && !stat_validity_check(&refs->packed->validity, packed_refs_file)) clear_packed_ref_cache(refs); @@ -1288,10 +1293,11 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags) flags, timeout_value) < 0) return -1; /* - * Get the current packed-refs while holding the lock. If the - * packed-refs file has been modified since we last read it, - * this will automatically invalidate the cache and re-read - * the packed-refs file. + * Get the current packed-refs while holding the lock. It is + * important that we call `get_packed_ref_cache()` before + * setting `packed_ref_cache->lock`, because otherwise the + * former will see that the file is locked and assume that the + * cache can't be stale. */ packed_ref_cache = get_packed_ref_cache(refs); /* Increment the reference count to prevent it from being freed: */ From 099a912a279415dd27716ee5de07ff347bfc49da Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:50 +0200 Subject: [PATCH 297/566] read_packed_refs(): do more of the work of reading packed refs Teach `read_packed_refs()` to also * Allocate and initialize the new `packed_ref_cache` * Open and close the `packed-refs` file * Update the `validity` field of the new object This decreases the coupling between `packed_refs_cache` and `files_ref_store` by a little bit. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 38 +++++++++++++++++++++++--------------- refs/ref-cache.h | 3 ++- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index c4bc9550d3..b4fa745cd7 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -209,7 +209,9 @@ static const char *parse_ref_line(struct strbuf *line, struct object_id *oid) } /* - * Read f, which is a packed-refs file, into dir. + * Read from `packed_refs_file` into a newly-allocated + * `packed_ref_cache` and return it. The return value will already + * have its reference count incremented. * * A comment line of the form "# pack-refs with: " may contain zero or * more traits. We interpret the traits as follows: @@ -235,12 +237,26 @@ static const char *parse_ref_line(struct strbuf *line, struct object_id *oid) * compatibility with older clients, but we do not require it * (i.e., "peeled" is a no-op if "fully-peeled" is set). */ -static void read_packed_refs(FILE *f, struct ref_dir *dir) +static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file) { + FILE *f; + struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs)); struct ref_entry *last = NULL; struct strbuf line = STRBUF_INIT; enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE; + struct ref_dir *dir; + acquire_packed_ref_cache(packed_refs); + packed_refs->cache = create_ref_cache(NULL, NULL); + packed_refs->cache->root->flag &= ~REF_INCOMPLETE; + + f = fopen(packed_refs_file, "r"); + if (!f) + return packed_refs; + + stat_validity_update(&packed_refs->validity, fileno(f)); + + dir = get_ref_dir(packed_refs->cache->root); while (strbuf_getwholeline(&line, f, '\n') != EOF) { struct object_id oid; const char *refname; @@ -287,7 +303,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) } } + fclose(f); strbuf_release(&line); + + return packed_refs; } static const char *files_packed_refs_path(struct files_ref_store *refs) @@ -357,20 +376,9 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref !stat_validity_check(&refs->packed->validity, packed_refs_file)) clear_packed_ref_cache(refs); - if (!refs->packed) { - FILE *f; + if (!refs->packed) + refs->packed = read_packed_refs(packed_refs_file); - refs->packed = xcalloc(1, sizeof(*refs->packed)); - acquire_packed_ref_cache(refs->packed); - refs->packed->cache = create_ref_cache(&refs->base, NULL); - refs->packed->cache->root->flag &= ~REF_INCOMPLETE; - f = fopen(packed_refs_file, "r"); - if (f) { - stat_validity_update(&refs->packed->validity, fileno(f)); - read_packed_refs(f, get_ref_dir(refs->packed->cache->root)); - fclose(f); - } - } return refs->packed; } diff --git a/refs/ref-cache.h b/refs/ref-cache.h index 1f65e2f9ed..fbfee7ce79 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -194,7 +194,8 @@ struct ref_entry *create_ref_entry(const char *refname, * function called to fill in incomplete directories in the * `ref_cache` when they are accessed. If it is NULL, then the whole * `ref_cache` must be filled (including clearing its directories' - * `REF_INCOMPLETE` bits) before it is used. + * `REF_INCOMPLETE` bits) before it is used, and `refs` can be NULL, + * too. */ struct ref_cache *create_ref_cache(struct ref_store *refs, fill_ref_dir_fn *fill_ref_dir); From 89c571da56a1e84fe12308f727fac0e82c1d5be6 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:51 +0200 Subject: [PATCH 298/566] read_packed_refs(): report unexpected fopen() failures The old code ignored any errors encountered when trying to fopen the "packed-refs" file, treating all such failures as if the file didn't exist. But it could be that there is some other error opening the file (e.g., permissions problems), and we don't want to silently ignore such problems. So report any failures that are not due to ENOENT. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index b4fa745cd7..dbfd03f989 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -251,8 +251,18 @@ static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file) packed_refs->cache->root->flag &= ~REF_INCOMPLETE; f = fopen(packed_refs_file, "r"); - if (!f) - return packed_refs; + if (!f) { + if (errno == ENOENT) { + /* + * This is OK; it just means that no + * "packed-refs" file has been written yet, + * which is equivalent to it being empty. + */ + return packed_refs; + } else { + die_errno("couldn't read %s", packed_refs_file); + } + } stat_validity_update(&packed_refs->validity, fileno(f)); From 0a0865b8f168b7195bd15440d15eb0e7817d6526 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:52 +0200 Subject: [PATCH 299/566] refs_ref_iterator_begin(): handle `GIT_REF_PARANOIA` Instead of handling `GIT_REF_PARANOIA` in `files_ref_iterator_begin()`, handle it in `refs_ref_iterator_begin()`, where it will cover all reference stores. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs.c | 5 +++++ refs/files-backend.c | 11 ++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/refs.c b/refs.c index d1c781d94e..f0685c9251 100644 --- a/refs.c +++ b/refs.c @@ -1259,6 +1259,11 @@ struct ref_iterator *refs_ref_iterator_begin( { struct ref_iterator *iter; + if (ref_paranoia < 0) + ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); + if (ref_paranoia) + flags |= DO_FOR_EACH_INCLUDE_BROKEN; + iter = refs->be->iterator_begin(refs, prefix, flags); /* diff --git a/refs/files-backend.c b/refs/files-backend.c index dbfd03f989..5de36fc335 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1074,15 +1074,12 @@ static struct ref_iterator *files_ref_iterator_begin( struct ref_iterator *loose_iter, *packed_iter; struct files_ref_iterator *iter; struct ref_iterator *ref_iterator; + unsigned int required_flags = REF_STORE_READ; - if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); - if (ref_paranoia) - flags |= DO_FOR_EACH_INCLUDE_BROKEN; + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) + required_flags |= REF_STORE_ODB; - refs = files_downcast(ref_store, - REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB), - "ref_iterator_begin"); + refs = files_downcast(ref_store, required_flags, "ref_iterator_begin"); iter = xcalloc(1, sizeof(*iter)); ref_iterator = &iter->base; From c1da06c6f1a77370341d93d80af027caa6a19a94 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:53 +0200 Subject: [PATCH 300/566] create_ref_entry(): remove `check_name` option Only one caller was using it, so move the check to that caller. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 12 ++++++++---- refs/ref-cache.c | 6 +----- refs/ref-cache.h | 3 +-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 5de36fc335..d8b3f73147 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -291,7 +291,7 @@ static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file) oidclr(&oid); flag |= REF_BAD_NAME | REF_ISBROKEN; } - last = create_ref_entry(refname, &oid, flag, 0); + last = create_ref_entry(refname, &oid, flag); if (peeled == PEELED_FULLY || (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/"))) last->flag |= REF_KNOWS_PEELED; @@ -415,8 +415,12 @@ static void add_packed_ref(struct files_ref_store *refs, if (!is_lock_file_locked(&refs->packed_refs_lock)) die("BUG: packed refs not locked"); + + if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) + die("Reference has invalid format: '%s'", refname); + add_ref_entry(get_packed_ref_dir(packed_ref_cache), - create_ref_entry(refname, oid, REF_ISPACKED, 1)); + create_ref_entry(refname, oid, REF_ISPACKED)); } /* @@ -493,7 +497,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, flag |= REF_BAD_NAME | REF_ISBROKEN; } add_entry_to_dir(dir, - create_ref_entry(refname.buf, &oid, flag, 0)); + create_ref_entry(refname.buf, &oid, flag)); } strbuf_setlen(&refname, dirnamelen); strbuf_setlen(&path, path_baselen); @@ -1541,7 +1545,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) oidcpy(&packed_entry->u.value.oid, iter->oid); } else { packed_entry = create_ref_entry(iter->refname, iter->oid, - REF_ISPACKED, 0); + REF_ISPACKED); add_ref_entry(packed_refs, packed_entry); } oidclr(&packed_entry->u.value.peeled); diff --git a/refs/ref-cache.c b/refs/ref-cache.c index 6b11d9cd12..ec97f3a38a 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -32,14 +32,10 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry) } struct ref_entry *create_ref_entry(const char *refname, - const struct object_id *oid, int flag, - int check_name) + const struct object_id *oid, int flag) { struct ref_entry *ref; - if (check_name && - check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) - die("Reference has invalid format: '%s'", refname); FLEX_ALLOC_STR(ref, name, refname); oidcpy(&ref->u.value.oid, oid); oidclr(&ref->u.value.peeled); diff --git a/refs/ref-cache.h b/refs/ref-cache.h index fbfee7ce79..794f000fd3 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -185,8 +185,7 @@ struct ref_entry *create_dir_entry(struct ref_cache *cache, int incomplete); struct ref_entry *create_ref_entry(const char *refname, - const struct object_id *oid, int flag, - int check_name); + const struct object_id *oid, int flag); /* * Return a pointer to a new `ref_cache`. Its top-level starts out From cfe004a5a9e5116d003332a64142b0f73f8f9cdf Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 22 May 2017 16:17:54 +0200 Subject: [PATCH 301/566] ref-filter: limit traversal to prefix When we are matching refnames against a pattern, then we know that the beginning of any refname that can match the pattern has to match the part of the pattern up to the first glob character. For example, if the pattern is `refs/heads/foo*bar`, then it can only match a reference that has the prefix `refs/heads/foo`. So pass that prefix to `for_each_fullref_in()`. This lets the ref code avoid passing us the full set of refs, and in some cases avoid reading them in the first place. Note that this applies only when the `match_as_path` flag is set (i.e., when `for-each-ref` is the caller), as the matching rules for git-branch and git-tag are subtly different. This could be generalized to the case of multiple patterns, but (a) it probably doesn't come up that often, and (b) it is more awkward to deal with multiple patterns (e.g., the patterns might not be disjoint). So, since this is just an optimization, punt on the case of multiple patterns. Signed-off-by: Jeff King Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- ref-filter.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/ref-filter.c b/ref-filter.c index 6cc93dcd9f..25ca56d62f 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1665,6 +1665,68 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname) return match_pattern(filter, refname); } +/* + * Find the longest prefix of pattern we can pass to + * `for_each_fullref_in()`, namely the part of pattern preceding the + * first glob character. (Note that `for_each_fullref_in()` is + * perfectly happy working with a prefix that doesn't end at a + * pathname component boundary.) + */ +static void find_longest_prefix(struct strbuf *out, const char *pattern) +{ + const char *p; + + for (p = pattern; *p && !is_glob_special(*p); p++) + ; + + strbuf_add(out, pattern, p - pattern); +} + +/* + * This is the same as for_each_fullref_in(), but it tries to iterate + * only over the patterns we'll care about. Note that it _doesn't_ do a full + * pattern match, so the callback still has to match each ref individually. + */ +static int for_each_fullref_in_pattern(struct ref_filter *filter, + each_ref_fn cb, + void *cb_data, + int broken) +{ + struct strbuf prefix = STRBUF_INIT; + int ret; + + if (!filter->match_as_path) { + /* + * in this case, the patterns are applied after + * prefixes like "refs/heads/" etc. are stripped off, + * so we have to look at everything: + */ + return for_each_fullref_in("", cb, cb_data, broken); + } + + if (!filter->name_patterns[0]) { + /* no patterns; we have to look at everything */ + return for_each_fullref_in("", cb, cb_data, broken); + } + + if (filter->name_patterns[1]) { + /* + * multiple patterns; in theory this could still work as long + * as the patterns are disjoint. We'd just make multiple calls + * to for_each_ref(). But if they're not disjoint, we'd end up + * reporting the same ref multiple times. So let's punt on that + * for now. + */ + return for_each_fullref_in("", cb, cb_data, broken); + } + + find_longest_prefix(&prefix, filter->name_patterns[0]); + + ret = for_each_fullref_in(prefix.buf, cb, cb_data, broken); + strbuf_release(&prefix); + return ret; +} + /* * Given a ref (sha1, refname), check if the ref belongs to the array * of sha1s. If the given ref is a tag, check if the given tag points @@ -1911,7 +1973,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int else if (filter->kind == FILTER_REFS_TAGS) ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken); else if (filter->kind & FILTER_REFS_ALL) - ret = for_each_fullref_in("", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken); if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) head_ref(ref_filter_handler, &ref_cbdata); } From fc7a5edb55fed000a85682f9791f645e5d768b63 Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Mon, 22 May 2017 22:59:58 +0200 Subject: [PATCH 302/566] Documentation: fix formatting typo in pretty-formats.txt A missing space messed up formatting of the `%(trailers)` format. Signed-off-by: Junio C Hamano --- Documentation/pretty-formats.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 47b286b33e..38040e95b5 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -199,7 +199,7 @@ endif::git-rev-list[] than given and there are spaces on its left, use those spaces - '%><()', '%><|()': similar to '% <()', '%<|()' respectively, but padding both sides (i.e. the text is centered) --%(trailers): display the trailers of the body as interpreted by +- %(trailers): display the trailers of the body as interpreted by linkgit:git-interpret-trailers[1] NOTE: Some placeholders may depend on other options given to the From e20b5b59099a960d59f20276ae37353870e714de Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 22 May 2017 20:58:32 +0200 Subject: [PATCH 303/566] mingw.h: permit arguments with side effects for is_dir_sep Taking git-compat-util.h's cue (which uses an inline function to back is_dir_sep()), let's use an inline function to back also the Windows version of is_dir_sep(). This avoids problems when calling the function with arguments that do more than just provide a single character, e.g. incrementing a pointer. Example: is_dir_sep(*p++) Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compat/mingw.h b/compat/mingw.h index 034fff9479..d2168c1e5e 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -395,7 +395,11 @@ HANDLE winansi_get_osfhandle(int fd); (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0) int mingw_skip_dos_drive_prefix(char **path); #define skip_dos_drive_prefix mingw_skip_dos_drive_prefix -#define is_dir_sep(c) ((c) == '/' || (c) == '\\') +static inline int mingw_is_dir_sep(int c) +{ + return c == '/' || c == '\\'; +} +#define is_dir_sep mingw_is_dir_sep static inline char *mingw_find_last_dir_sep(const char *path) { char *ret = NULL; From e0ca1ca20a0bde51c4eb9242dd9521ac1ed08304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 20 May 2017 21:35:37 +0200 Subject: [PATCH 304/566] mingw: simplify PATH handling On Windows the environment variable PATH contains a semicolon-separated list of directories to search for, in order, when looking for the location of a binary to run. get_path_split() parses it and returns an array of string copies, which is iterated by path_lookup(), which in turn passes each entry to lookup_prog(). Change lookup_prog() to take the directory name as a length-limited string instead of as a NUL-terminated one and parse PATH directly in path_lookup(). This avoids memory allocations, simplifying the code. Helped-by: Johannes Sixt Signed-off-by: Rene Scharfe Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/mingw.c | 91 +++++++++++++------------------------------------- 1 file changed, 23 insertions(+), 68 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 3fbfda5978..c6134f7c81 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -940,65 +940,15 @@ static const char *parse_interpreter(const char *cmd) return p+1; } -/* - * Splits the PATH into parts. - */ -static char **get_path_split(void) -{ - char *p, **path, *envpath = mingw_getenv("PATH"); - int i, n = 0; - - if (!envpath || !*envpath) - return NULL; - - envpath = xstrdup(envpath); - p = envpath; - while (p) { - char *dir = p; - p = strchr(p, ';'); - if (p) *p++ = '\0'; - if (*dir) { /* not earlier, catches series of ; */ - ++n; - } - } - if (!n) - return NULL; - - ALLOC_ARRAY(path, n + 1); - p = envpath; - i = 0; - do { - if (*p) - path[i++] = xstrdup(p); - p = p+strlen(p)+1; - } while (i < n); - path[i] = NULL; - - free(envpath); - - return path; -} - -static void free_path_split(char **path) -{ - char **p = path; - - if (!path) - return; - - while (*p) - free(*p++); - free(path); -} - /* * exe_only means that we only want to detect .exe files, but not scripts * (which do not have an extension) */ -static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_only) +static char *lookup_prog(const char *dir, int dirlen, const char *cmd, + int isexe, int exe_only) { char path[MAX_PATH]; - snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd); + snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd); if (!isexe && access(path, F_OK) == 0) return xstrdup(path); @@ -1013,17 +963,29 @@ static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_on * Determines the absolute path of cmd using the split path in path. * If cmd contains a slash or backslash, no lookup is performed. */ -static char *path_lookup(const char *cmd, char **path, int exe_only) +static char *path_lookup(const char *cmd, int exe_only) { + const char *path; char *prog = NULL; int len = strlen(cmd); int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe"); if (strchr(cmd, '/') || strchr(cmd, '\\')) - prog = xstrdup(cmd); + return xstrdup(cmd); - while (!prog && *path) - prog = lookup_prog(*path++, cmd, isexe, exe_only); + path = mingw_getenv("PATH"); + if (!path) + return NULL; + + while (!prog) { + const char *sep = strchrnul(path, ';'); + int dirlen = sep - path; + if (dirlen) + prog = lookup_prog(path, dirlen, cmd, isexe, exe_only); + if (!*sep) + break; + path = sep + 1; + } return prog; } @@ -1190,8 +1152,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv, int fhin, int fhout, int fherr) { pid_t pid; - char **path = get_path_split(); - char *prog = path_lookup(cmd, path, 0); + char *prog = path_lookup(cmd, 0); if (!prog) { errno = ENOENT; @@ -1202,7 +1163,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv, if (interpr) { const char *argv0 = argv[0]; - char *iprog = path_lookup(interpr, path, 1); + char *iprog = path_lookup(interpr, 1); argv[0] = prog; if (!iprog) { errno = ENOENT; @@ -1220,21 +1181,18 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv, fhin, fhout, fherr); free(prog); } - free_path_split(path); return pid; } static int try_shell_exec(const char *cmd, char *const *argv) { const char *interpr = parse_interpreter(cmd); - char **path; char *prog; int pid = 0; if (!interpr) return 0; - path = get_path_split(); - prog = path_lookup(interpr, path, 1); + prog = path_lookup(interpr, 1); if (prog) { int argc = 0; const char **argv2; @@ -1253,7 +1211,6 @@ static int try_shell_exec(const char *cmd, char *const *argv) free(prog); free(argv2); } - free_path_split(path); return pid; } @@ -1275,8 +1232,7 @@ int mingw_execv(const char *cmd, char *const *argv) int mingw_execvp(const char *cmd, char *const *argv) { - char **path = get_path_split(); - char *prog = path_lookup(cmd, path, 0); + char *prog = path_lookup(cmd, 0); if (prog) { mingw_execv(prog, argv); @@ -1284,7 +1240,6 @@ int mingw_execvp(const char *cmd, char *const *argv) } else errno = ENOENT; - free_path_split(path); return -1; } From 80f4cd804628f59e679b6107a63b05b898c7221e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 15 May 2017 12:23:31 +0000 Subject: [PATCH 305/566] tag: duplicate mention of --contains should mention --no-contains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a duplicate mention of --contains in the SYNOPSIS to mention --no-contains. This fixes an error introduced in my commit ac3f5a3468 ("ref-filter: add --no-contains option to tag/branch/for-each-ref", 2017-03-24). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index f8a0b787f4..1eb15afa1c 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git tag' [-a | -s | -u ] [-f] [-m | -F ] [ | ] 'git tag' -d ... -'git tag' [-n[]] -l [--contains ] [--contains ] +'git tag' [-n[]] -l [--contains ] [--no-contains ] [--points-at ] [--column[=] | --no-column] [--create-reflog] [--sort=] [--format=] [--[no-]merged []] [...] From 6b1db43109ab3d4c92e61874cd149779c66016db Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Tue, 23 May 2017 06:09:37 -0400 Subject: [PATCH 306/566] clean: teach clean -d to preserve ignored paths There is an implicit assumption that a directory containing only untracked and ignored paths should itself be considered untracked. This makes sense in use cases where we're asking if a directory should be added to the git database, but not when we're asking if a directory can be safely removed from the working tree; as a result, clean -d would assume that an "untracked" directory containing ignored paths could be deleted, even though doing so would also remove the ignored paths. To get around this, we teach clean -d to collect ignored paths and skip an untracked directory if it contained an ignored path, instead just removing the untracked contents thereof. To achieve this, cmd_clean() has to collect all untracked contents of untracked directories, in addition to all ignored paths, to determine which untracked dirs must be skipped (because they contain ignored paths) and which ones should *not* be skipped. For this purpose, correct_untracked_entries() is introduced to prune a given dir_struct of untracked entries containing ignored paths and those untracked entries encompassed by the untracked entries which are not pruned away. A memory leak is also fixed in cmd_clean(). This also fixes the known breakage in t7300, since clean -d now skips untracked directories containing ignored paths. Signed-off-by: Samuel Lijin Signed-off-by: Junio C Hamano --- builtin/clean.c | 42 ++++++++++++++++++++++++++++++++++++++++++ t/t7300-clean.sh | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/builtin/clean.c b/builtin/clean.c index d6bc3aaaea..7272033187 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -851,6 +851,38 @@ static void interactive_main_loop(void) } } +static void correct_untracked_entries(struct dir_struct *dir) +{ + int src, dst, ign; + + for (src = dst = ign = 0; src < dir->nr; src++) { + /* skip paths in ignored[] that cannot be inside entries[src] */ + while (ign < dir->ignored_nr && + 0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign])) + ign++; + + if (ign < dir->ignored_nr && + check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) { + /* entries[src] contains an ignored path, so we drop it */ + free(dir->entries[src]); + } else { + struct dir_entry *ent = dir->entries[src++]; + + /* entries[src] does not contain an ignored path, so we keep it */ + dir->entries[dst++] = ent; + + /* then discard paths in entries[] contained inside entries[src] */ + while (src < dir->nr && + check_dir_entry_contains(ent, dir->entries[src])) + free(dir->entries[src++]); + + /* compensate for the outer loop's loop control */ + src--; + } + } + dir->nr = dst; +} + int cmd_clean(int argc, const char **argv, const char *prefix) { int i, res; @@ -910,6 +942,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; + if (remove_directories) + dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS; + if (read_cache() < 0) die(_("index file corrupt")); @@ -925,6 +960,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) prefix, argv); fill_directory(&dir, &pathspec); + correct_untracked_entries(&dir); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; @@ -952,6 +988,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix) string_list_append(&del_list, rel); } + for (i = 0; i < dir.nr; i++) + free(dir.entries[i]); + + for (i = 0; i < dir.ignored_nr; i++) + free(dir.ignored[i]); + if (interactive && del_list.nr > 0) interactive_main_loop(); diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 3a2d709c29..7b36954d63 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -653,7 +653,7 @@ test_expect_success 'git clean -d respects pathspecs (pathspec is prefix of dir) test_path_is_dir foobar ' -test_expect_failure 'git clean -d skips untracked dirs containing ignored files' ' +test_expect_success 'git clean -d skips untracked dirs containing ignored files' ' echo /foo/bar >.gitignore && echo ignoreme >>.gitignore && rm -rf foo && From ed79b2cf034b81473dd1fa9648593b245c07daea Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 23 May 2017 15:51:32 -0400 Subject: [PATCH 307/566] handle_revision_arg: reset "dotdot" consistently When we are parsing a range like "a..b", we write a temporary NUL over the first ".", so that we can access the names "a" and "b" as C strings. But our restoration of the original "." is done at inconsistent times, which can lead to confusing results. For most calls, we restore the "." after we resolve the names, but before we call verify_non_filename(). This means that when we later call add_pending_object(), the name for the left-hand "a" has been re-expanded to "a..b". You can see this with: git log --source a...b where "b" will be correctly marked with "b", but "a" will be marked with "a...b". Likewise with "a..b" (though you need to use --boundary to even see "a" at all in that case). To top off the confusion, when the REVARG_CANNOT_BE_FILENAME flag is set, we skip the non-filename check, and leave the NUL in place. That means we do report the correct name for "a" in the pending array. But some code paths try to show the whole "a...b" name in error messages, and these erroneously show only "a" instead of "a...b". E.g.: $ git cherry-pick HEAD:foo...HEAD:foo error: object d95f3ad14dee633a758d2e331151e950dd13e4ed is a blob, not a commit error: object d95f3ad14dee633a758d2e331151e950dd13e4ed is a blob, not a commit fatal: Invalid symmetric difference expression HEAD:foo (That last message should be "HEAD:foo...HEAD:foo"; I used cherry-pick because it passes the CANNOT_BE_FILENAME flag). As an interesting side note, cherry-pick actually looks at and re-resolves the arguments from the pending->name fields. So it would have been visibly broken by the first bug, but the effect was canceled out by the second one. This patch makes the whole function consistent by re-writing the NUL immediately after calling verify_non_filename(), and then restoring the "." as appropriate in some error-printing and early-return code paths. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 3 +++ t/t4202-log.sh | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/revision.c b/revision.c index 7ff61ff5f7..2bcd60a2bb 100644 --- a/revision.c +++ b/revision.c @@ -1477,12 +1477,14 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi if (!cant_be_filename) { *dotdot = '.'; verify_non_filename(revs->prefix, arg); + *dotdot = '\0'; } a_obj = parse_object(from_sha1); b_obj = parse_object(sha1); if (!a_obj || !b_obj) { missing: + *dotdot = '.'; if (revs->ignore_missing) return 0; die(symmetric @@ -1525,6 +1527,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi REV_CMD_RIGHT, flags); add_pending_object(revs, a_obj, this); add_pending_object(revs, b_obj, next); + *dotdot = '.'; return 0; } *dotdot = '.'; diff --git a/t/t4202-log.sh b/t/t4202-log.sh index f577990716..6da1bbe912 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -1380,4 +1380,13 @@ test_expect_success 'log --source paints tag names' ' test_cmp expect actual ' +test_expect_success 'log --source paints symmetric ranges' ' + cat >expect <<-\EOF && + 09e12a9 source-b three + 8e393e1 source-a two + EOF + git log --oneline --source source-a...source-b >actual && + test_cmp expect actual +' + test_done From 1d6c93817bf22d6bf279bac302911cc93f63046c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:48:46 -0400 Subject: [PATCH 308/566] handle_revision_arg: simplify commit reference lookups The "dotdot" range parser avoids calling lookup_commit_reference() if we are directly fed two commits. But its casts are unnecessarily complex; that function will just return a commit we pass into it. Just calling the function all the time is much simpler, and doesn't do any significant extra work (the object is already parsed, and deref_tag() on a non-tag is a noop; we do incur one extra lookup_object() call, but that's fairly trivial). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/revision.c b/revision.c index 2bcd60a2bb..dc32812906 100644 --- a/revision.c +++ b/revision.c @@ -1500,12 +1500,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi struct commit *a, *b; struct commit_list *exclude; - a = (a_obj->type == OBJ_COMMIT - ? (struct commit *)a_obj - : lookup_commit_reference(a_obj->oid.hash)); - b = (b_obj->type == OBJ_COMMIT - ? (struct commit *)b_obj - : lookup_commit_reference(b_obj->oid.hash)); + a = lookup_commit_reference(a_obj->oid.hash); + b = lookup_commit_reference(b_obj->oid.hash); if (!a || !b) goto missing; exclude = get_merge_bases(a, b); From f632dedd8d29da20d546612d908a5c1b2ebf37aa Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:50:07 -0400 Subject: [PATCH 309/566] handle_revision_arg: stop using "dotdot" as a generic pointer The handle_revision_arg() function has a "dotdot" variable that it uses to find a ".." or "..." in the argument. If we don't find one, we look for other marks, like "^!". But we just keep re-using the "dotdot" variable, which is confusing. Let's introduce a separate "mark" variable that can be used for these other marks. They still reuse the same variable, but at least the name is no longer actively misleading. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/revision.c b/revision.c index dc32812906..c98804ac02 100644 --- a/revision.c +++ b/revision.c @@ -1433,6 +1433,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi { struct object_context oc; char *dotdot; + char *mark; struct object *object; unsigned char sha1[20]; int local_flags; @@ -1529,33 +1530,33 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi *dotdot = '.'; } - dotdot = strstr(arg, "^@"); - if (dotdot && !dotdot[2]) { - *dotdot = 0; + mark = strstr(arg, "^@"); + if (mark && !mark[2]) { + *mark = 0; if (add_parents_only(revs, arg, flags, 0)) return 0; - *dotdot = '^'; + *mark = '^'; } - dotdot = strstr(arg, "^!"); - if (dotdot && !dotdot[2]) { - *dotdot = 0; + mark = strstr(arg, "^!"); + if (mark && !mark[2]) { + *mark = 0; if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0)) - *dotdot = '^'; + *mark = '^'; } - dotdot = strstr(arg, "^-"); - if (dotdot) { + mark = strstr(arg, "^-"); + if (mark) { int exclude_parent = 1; - if (dotdot[2]) { + if (mark[2]) { char *end; - exclude_parent = strtoul(dotdot + 2, &end, 10); + exclude_parent = strtoul(mark + 2, &end, 10); if (*end != '\0' || !exclude_parent) return -1; } - *dotdot = 0; + *mark = 0; if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent)) - *dotdot = '^'; + *mark = '^'; } local_flags = 0; From d89797feff053bba939b62ee442f56e3fc98062b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:51:40 -0400 Subject: [PATCH 310/566] handle_revision_arg: hoist ".." check out of range parsing Since 003c84f6d (specifying ranges: we did not mean to make ".." an empty set, 2011-05-02), we treat the argument ".." specially. We detect it by noticing that both sides of the range are empty, and that this is a non-symmetric two-dot range. While correct, this makes the code overly complicated. We can just detect ".." up front before we try to do further parsing. This avoids having to de-munge the NUL from dotdot, and lets us eliminate an extra const array (which we needed only to do direct pointer comparisons). It also removes the one code path from the range-parsing conditional that requires us to return -1. That will make it simpler to pull the dotdot parsing out into its own function. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/revision.c b/revision.c index c98804ac02..2664f08879 100644 --- a/revision.c +++ b/revision.c @@ -1443,6 +1443,14 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM; + if (!cant_be_filename && !strcmp(arg, "..")) { + /* + * Just ".."? That is not a range but the + * pathspec for the parent directory. + */ + return -1; + } + dotdot = strstr(arg, ".."); if (dotdot) { unsigned char from_sha1[20]; @@ -1450,27 +1458,15 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi const char *this = arg; int symmetric = *next == '.'; unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); - static const char head_by_default[] = "HEAD"; unsigned int a_flags; *dotdot = 0; next += symmetric; if (!*next) - next = head_by_default; + next = "HEAD"; if (dotdot == arg) - this = head_by_default; - if (this == head_by_default && next == head_by_default && - !symmetric) { - /* - * Just ".."? That is not a range but the - * pathspec for the parent directory. - */ - if (!cant_be_filename) { - *dotdot = '.'; - return -1; - } - } + this = "HEAD"; if (!get_sha1_committish(this, from_sha1) && !get_sha1_committish(next, sha1)) { struct object *a_obj, *b_obj; From 62faad5aa5441942c333c15f3afa03a6e2dd994d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:52:00 -0400 Subject: [PATCH 311/566] handle_revision_arg: add handle_dotdot() helper The handle_revision_arg function is rather long, and a big chunk of it is handling the range operators. Let's pull that out to a separate helper. While we're doing so, we can clean up a few of the rough edges that made the flow hard to follow: - instead of manually restoring *dotdot (that we overwrote with a NUL), do the real work in a sub-helper, which makes it clear that the munge/restore lines are a matched pair - eliminate a goto which wasn't actually used for control flow, but only to avoid duplicating a few lines (instead, those lines are pushed into another helper function) - use early returns instead of deep nesting - consistently name all variables for the left-hand side of the range as "a" (rather than "this" or "from") and the right-hand side as "b" (rather than "next", or using the unadorned "sha1" or "flags" from the main function). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 177 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 75 deletions(-) diff --git a/revision.c b/revision.c index 2664f08879..49707695ab 100644 --- a/revision.c +++ b/revision.c @@ -1429,10 +1429,109 @@ static void prepare_show_merge(struct rev_info *revs) revs->limited = 1; } +static int dotdot_missing(const char *arg, char *dotdot, + struct rev_info *revs, int symmetric) +{ + if (revs->ignore_missing) + return 0; + /* de-munge so we report the full argument */ + *dotdot = '.'; + die(symmetric + ? "Invalid symmetric difference expression %s" + : "Invalid revision range %s", arg); +} + +static int handle_dotdot_1(const char *arg, char *dotdot, + struct rev_info *revs, int flags, + int cant_be_filename) +{ + const char *a_name, *b_name; + struct object_id a_oid, b_oid; + struct object *a_obj, *b_obj; + unsigned int a_flags, b_flags; + int symmetric = 0; + unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); + + a_name = arg; + if (!*a_name) + a_name = "HEAD"; + + b_name = dotdot + 2; + if (*b_name == '.') { + symmetric = 1; + b_name++; + } + if (!*b_name) + b_name = "HEAD"; + + if (get_sha1_committish(a_name, a_oid.hash) || + get_sha1_committish(b_name, b_oid.hash)) + return -1; + + if (!cant_be_filename) { + *dotdot = '.'; + verify_non_filename(revs->prefix, arg); + *dotdot = '\0'; + } + + a_obj = parse_object(a_oid.hash); + b_obj = parse_object(b_oid.hash); + if (!a_obj || !b_obj) + return dotdot_missing(arg, dotdot, revs, symmetric); + + if (!symmetric) { + /* just A..B */ + b_flags = flags; + a_flags = flags_exclude; + } else { + /* A...B -- find merge bases between the two */ + struct commit *a, *b; + struct commit_list *exclude; + + a = lookup_commit_reference(a_obj->oid.hash); + b = lookup_commit_reference(b_obj->oid.hash); + if (!a || !b) + return dotdot_missing(arg, dotdot, revs, symmetric); + + exclude = get_merge_bases(a, b); + add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, + flags_exclude); + add_pending_commit_list(revs, exclude, flags_exclude); + free_commit_list(exclude); + + b_flags = flags; + a_flags = flags | SYMMETRIC_LEFT; + } + + a_obj->flags |= a_flags; + b_obj->flags |= b_flags; + add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags); + add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags); + add_pending_object(revs, a_obj, a_name); + add_pending_object(revs, b_obj, b_name); + return 0; +} + +static int handle_dotdot(const char *arg, + struct rev_info *revs, int flags, + int cant_be_filename) +{ + char *dotdot = strstr(arg, ".."); + int ret; + + if (!dotdot) + return -1; + + *dotdot = '\0'; + ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename); + *dotdot = '.'; + + return ret; +} + int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt) { struct object_context oc; - char *dotdot; char *mark; struct object *object; unsigned char sha1[20]; @@ -1451,80 +1550,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi return -1; } - dotdot = strstr(arg, ".."); - if (dotdot) { - unsigned char from_sha1[20]; - const char *next = dotdot + 2; - const char *this = arg; - int symmetric = *next == '.'; - unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); - unsigned int a_flags; - - *dotdot = 0; - next += symmetric; - - if (!*next) - next = "HEAD"; - if (dotdot == arg) - this = "HEAD"; - if (!get_sha1_committish(this, from_sha1) && - !get_sha1_committish(next, sha1)) { - struct object *a_obj, *b_obj; - - if (!cant_be_filename) { - *dotdot = '.'; - verify_non_filename(revs->prefix, arg); - *dotdot = '\0'; - } - - a_obj = parse_object(from_sha1); - b_obj = parse_object(sha1); - if (!a_obj || !b_obj) { - missing: - *dotdot = '.'; - if (revs->ignore_missing) - return 0; - die(symmetric - ? "Invalid symmetric difference expression %s" - : "Invalid revision range %s", arg); - } - - if (!symmetric) { - /* just A..B */ - a_flags = flags_exclude; - } else { - /* A...B -- find merge bases between the two */ - struct commit *a, *b; - struct commit_list *exclude; - - a = lookup_commit_reference(a_obj->oid.hash); - b = lookup_commit_reference(b_obj->oid.hash); - if (!a || !b) - goto missing; - exclude = get_merge_bases(a, b); - add_rev_cmdline_list(revs, exclude, - REV_CMD_MERGE_BASE, - flags_exclude); - add_pending_commit_list(revs, exclude, - flags_exclude); - free_commit_list(exclude); - - a_flags = flags | SYMMETRIC_LEFT; - } - - a_obj->flags |= a_flags; - b_obj->flags |= flags; - add_rev_cmdline(revs, a_obj, this, - REV_CMD_LEFT, a_flags); - add_rev_cmdline(revs, b_obj, next, - REV_CMD_RIGHT, flags); - add_pending_object(revs, a_obj, this); - add_pending_object(revs, b_obj, next); - *dotdot = '.'; - return 0; - } - *dotdot = '.'; - } + if (!handle_dotdot(arg, revs, flags, revarg_opt)) + return 0; mark = strstr(arg, "^@"); if (mark && !mark[2]) { From c0a487eafb63301004af424bf02b1951abdc4da7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:52:06 -0400 Subject: [PATCH 312/566] sha1_name: consistently refer to object_context as "oc" An early version of the patch to add object_context used the name object_resolve_context. This was later shortened to just object_context, but the "orc" variable name stuck in a few places. Let's use "oc", which is used elsewhere in the code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 2 +- sha1_name.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index e1f0e182ad..6cae30f64c 100644 --- a/cache.h +++ b/cache.h @@ -1363,7 +1363,7 @@ extern int get_sha1_tree(const char *str, unsigned char *sha1); extern int get_sha1_treeish(const char *str, unsigned char *sha1); extern int get_sha1_blob(const char *str, unsigned char *sha1); extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix); -extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc); +extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc); extern int get_oid(const char *str, struct object_id *oid); diff --git a/sha1_name.c b/sha1_name.c index 8eec9f7c1b..179f214c9e 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1638,9 +1638,9 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix) get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc); } -int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc) +int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc) { if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE) die("BUG: incompatible flags for get_sha1_with_context"); - return get_sha1_with_context_1(str, flags, NULL, sha1, orc); + return get_sha1_with_context_1(str, flags, NULL, sha1, oc); } From d72cae12b9a7bba3a6626e0b5805955eafdefcc6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:52:25 -0400 Subject: [PATCH 313/566] get_sha1_with_context: always initialize oc->symlink_path The get_sha1_with_context() function zeroes out the oc->symlink_path strbuf, but doesn't use strbuf_init() to set up the usual invariants (like pointing to the slopbuf). We don't actually write to the oc->symlink_path strbuf unless we call get_tree_entry_follow_symlinks(), and that function does initialize it. However, readers may still look at the zero'd strbuf. In practice this isn't a triggerable bug. The only caller that looks at it only does so when the mode we found is 0. This doesn't happen for non-tree-entries (where we return S_IFINVALID). A broken tree entry could have a mode of 0, but canon_mode() quietly rewrites that into S_IFGITLINK. So the "0" mode should only come up when we did indeed find a symlink. This is mostly just an accident of how the code happens to work, though. Let's future-proof ourselves to make sure the strbuf is properly initialized for all calls (it's only a few struct member assignments, not a heap allocation). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- sha1_name.c | 1 + tree-walk.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_name.c b/sha1_name.c index 179f214c9e..e7b0393bfc 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1511,6 +1511,7 @@ static int get_sha1_with_context_1(const char *name, memset(oc, 0, sizeof(*oc)); oc->mode = S_IFINVALID; + strbuf_init(&oc->symlink_path, 0); ret = get_sha1_1(name, namelen, sha1, flags); if (!ret) return ret; diff --git a/tree-walk.c b/tree-walk.c index ff77605680..c7ecfc8560 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -589,7 +589,6 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s int i; init_tree_desc(&t, NULL, 0UL); - strbuf_init(result_path, 0); strbuf_addstr(&namebuf, name); hashcpy(current_tree_sha1, tree_sha1); From dc944b65f1d13e258edc88a7608e2fe957ec334e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:54:43 -0400 Subject: [PATCH 314/566] get_sha1_with_context: dynamically allocate oc->path When a sha1 lookup returns the tree path via "struct object_context", it just copies it into a fixed-size buffer. This means the result can be truncated, and it means our "struct object_context" consumes a lot of stack space. Instead, let's allocate a string on the heap. Because most callers don't care about this information, we'll avoid doing it by default (so they don't all have to start calling free() on the result). There are basically two options for the caller to signal to us that it's interested: 1. By setting a pointer to storage in the object_context. 2. By passing a flag in another parameter. Doing (1) would match the way that sha1_object_info_extended() works. But it would mean that every caller would have to initialize the object_context, which they don't currently have to do. This patch does (2), and adds a new bit to the function's flags field. All of the callers that look at the "path" field are updated to pass the new flag. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 4 +++- builtin/grep.c | 4 +++- builtin/log.c | 10 +++++++--- cache.h | 8 +++++++- sha1_name.c | 6 ++++-- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 1890d7a639..421709517c 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -61,7 +61,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (unknown_type) flags |= LOOKUP_UNKNOWN_OBJECT; - if (get_sha1_with_context(obj_name, 0, oid.hash, &obj_context)) + if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH, + oid.hash, &obj_context)) die("Not a valid object name %s", obj_name); if (!path) @@ -165,6 +166,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, die("git cat-file %s: bad file", obj_name); write_or_die(1, buf, size); + free(obj_context.path); return 0; } diff --git a/builtin/grep.c b/builtin/grep.c index 3ffb5b4e81..254c1c7849 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1190,7 +1190,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) break; } - if (get_sha1_with_context(arg, 0, oid.hash, &oc)) { + if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH, + oid.hash, &oc)) { if (seen_dashdash) die(_("unable to resolve revision: %s"), arg); break; @@ -1200,6 +1201,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); + free(oc.path); } /* diff --git a/builtin/log.c b/builtin/log.c index b3b10cc1ed..1db016bcd9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -483,16 +483,20 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV)) return stream_blob_to_fd(1, oid, NULL, 0); - if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context)) + if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH, + oidc.hash, &obj_context)) die(_("Not a valid object name %s"), obj_name); - if (!obj_context.path[0] || - !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) + if (!obj_context.path || + !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) { + free(obj_context.path); return stream_blob_to_fd(1, oid, NULL, 0); + } if (!buf) die(_("git show %s: bad file"), obj_name); write_or_die(1, buf, size); + free(obj_context.path); return 0; } diff --git a/cache.h b/cache.h index 6cae30f64c..52b91f5b64 100644 --- a/cache.h +++ b/cache.h @@ -1333,13 +1333,18 @@ static inline int hex2chr(const char *s) struct object_context { unsigned char tree[20]; - char path[PATH_MAX]; unsigned mode; /* * symlink_path is only used by get_tree_entry_follow_symlinks, * and only for symlinks that point outside the repository. */ struct strbuf symlink_path; + /* + * If GET_SHA1_RECORD_PATH is set, this will record path (if any) + * found when resolving the name. The caller is responsible for + * releasing the memory. + */ + char *path; }; #define GET_SHA1_QUIETLY 01 @@ -1349,6 +1354,7 @@ struct object_context { #define GET_SHA1_TREEISH 020 #define GET_SHA1_BLOB 040 #define GET_SHA1_FOLLOW_SYMLINKS 0100 +#define GET_SHA1_RECORD_PATH 0200 #define GET_SHA1_ONLY_TO_DIE 04000 #define GET_SHA1_DISAMBIGUATORS \ diff --git a/sha1_name.c b/sha1_name.c index e7b0393bfc..5e2ec37b65 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1550,7 +1550,8 @@ static int get_sha1_with_context_1(const char *name, namelen = strlen(cp); } - strlcpy(oc->path, cp, sizeof(oc->path)); + if (flags & GET_SHA1_RECORD_PATH) + oc->path = xstrdup(cp); if (!active_cache) read_cache(); @@ -1613,7 +1614,8 @@ static int get_sha1_with_context_1(const char *name, } } hashcpy(oc->tree, tree_sha1); - strlcpy(oc->path, filename, sizeof(oc->path)); + if (flags & GET_SHA1_RECORD_PATH) + oc->path = xstrdup(filename); free(new_filename); return ret; From 74e89110a38fb52be29615a1468e86fb75077433 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:54:56 -0400 Subject: [PATCH 315/566] t4063: add tests of direct blob diffs The git-diff command can directly compare two blobs (or a blob and a file), but we don't test this at all. Let's add some basic tests that reveal a few problems. There are basically four interesting inputs: 1. sha1 against sha1 (where diff has no information beyond the contents) 2. tree:path against tree:path (where it can get information via get_sha1_with_context) 3. Same as (2), but using the ".." range syntax 4. tree:path against a filename And beyond generating a sane diff, we care about a few little bits: which paths they show in the diff header, and whether they correctly pick up a mode change. They should all be able to show a mode except for (1), though note that case (3) is currently broken. For the headers, we would ideally show the path within the tree if we have it, making: git diff a:path b:path look the same as: git diff a b -- path We can't always do that (e.g., in the direct sha1/sha1 diff, we have no path to show), in which case we should fall back to the name that resolved to the blob (which is nonsense from the repository's perspective, but is the best we can do). Aside from the fallback case in (1), none of the cases get this right. Cases (2) and (3) always show the full tree:path, even though we should be able to know just the "path" portion. Case (4) picks up the filename path, but assigns it to _both_ sides of the diff. So this works for: git diff tree:path path but not for: git diff tree:other_path path The appropriate tests are marked to expect failure. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t4063-diff-blobs.sh | 91 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100755 t/t4063-diff-blobs.sh diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh new file mode 100755 index 0000000000..90c6f6b85b --- /dev/null +++ b/t/t4063-diff-blobs.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +test_description='test direct comparison of blobs via git-diff' +. ./test-lib.sh + +run_diff () { + # use full-index to make it easy to match the index line + git diff --full-index "$@" >diff +} + +check_index () { + grep "^index $1\\.\\.$2" diff +} + +check_mode () { + grep "^old mode $1" diff && + grep "^new mode $2" diff +} + +check_paths () { + grep "^diff --git a/$1 b/$2" diff +} + +test_expect_success 'create some blobs' ' + echo one >one && + echo two >two && + chmod +x two && + git add . && + + # cover systems where modes are ignored + git update-index --chmod=+x two && + + git commit -m base && + + sha1_one=$(git rev-parse HEAD:one) && + sha1_two=$(git rev-parse HEAD:two) +' + +test_expect_success 'diff by sha1' ' + run_diff $sha1_one $sha1_two +' +test_expect_success 'index of sha1 diff' ' + check_index $sha1_one $sha1_two +' +test_expect_success 'sha1 diff uses arguments as paths' ' + check_paths $sha1_one $sha1_two +' +test_expect_success 'sha1 diff has no mode change' ' + ! grep mode diff +' + +test_expect_success 'diff by tree:path (run)' ' + run_diff HEAD:one HEAD:two +' +test_expect_success 'index of tree:path diff' ' + check_index $sha1_one $sha1_two +' +test_expect_failure 'tree:path diff uses filenames as paths' ' + check_paths one two +' +test_expect_success 'tree:path diff shows mode change' ' + check_mode 100644 100755 +' + +test_expect_success 'diff by ranged tree:path' ' + run_diff HEAD:one..HEAD:two +' +test_expect_success 'index of ranged tree:path diff' ' + check_index $sha1_one $sha1_two +' +test_expect_failure 'ranged tree:path diff uses filenames as paths' ' + check_paths one two +' +test_expect_failure 'ranged tree:path diff shows mode change' ' + check_mode 100644 100755 +' + +test_expect_success 'diff blob against file' ' + run_diff HEAD:one two +' +test_expect_success 'index of blob-file diff' ' + check_index $sha1_one $sha1_two +' +test_expect_failure 'blob-file diff uses filename as paths' ' + check_paths one two +' +test_expect_success FILEMODE 'blob-file diff shows mode change' ' + check_mode 100644 100755 +' + +test_done From 101dd4de16eea300a59e432452fd4fc06e1ac7f2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:55:11 -0400 Subject: [PATCH 316/566] handle_revision_arg: record modes for "a..b" endpoints The "a..b" revision syntax was designed to handle commits, so it doesn't bother to record any mode we find while traversing a "tree:path" endpoint. These days "git diff" can diff blobs using either "a:path..b:path" (with dots) or "a:path b:path" (without), but the two behave inconsistently, as the with-dots version fails to notice the mode. Let's teach the dot-dot range parser to record modes; it doesn't cost us anything, and it makes this case work. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 10 ++++++---- t/t4063-diff-blobs.sh | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/revision.c b/revision.c index 49707695ab..5b56f0e921 100644 --- a/revision.c +++ b/revision.c @@ -1448,9 +1448,11 @@ static int handle_dotdot_1(const char *arg, char *dotdot, const char *a_name, *b_name; struct object_id a_oid, b_oid; struct object *a_obj, *b_obj; + struct object_context a_oc, b_oc; unsigned int a_flags, b_flags; int symmetric = 0; unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); + unsigned int oc_flags = GET_SHA1_COMMITTISH; a_name = arg; if (!*a_name) @@ -1464,8 +1466,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot, if (!*b_name) b_name = "HEAD"; - if (get_sha1_committish(a_name, a_oid.hash) || - get_sha1_committish(b_name, b_oid.hash)) + if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, &a_oc) || + get_sha1_with_context(b_name, oc_flags, b_oid.hash, &b_oc)) return -1; if (!cant_be_filename) { @@ -1507,8 +1509,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot, b_obj->flags |= b_flags; add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags); add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags); - add_pending_object(revs, a_obj, a_name); - add_pending_object(revs, b_obj, b_name); + add_pending_object_with_mode(revs, a_obj, a_name, a_oc.mode); + add_pending_object_with_mode(revs, b_obj, b_name, b_oc.mode); return 0; } diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh index 90c6f6b85b..df9c35b2dd 100755 --- a/t/t4063-diff-blobs.sh +++ b/t/t4063-diff-blobs.sh @@ -71,7 +71,7 @@ test_expect_success 'index of ranged tree:path diff' ' test_expect_failure 'ranged tree:path diff uses filenames as paths' ' check_paths one two ' -test_expect_failure 'ranged tree:path diff shows mode change' ' +test_expect_success 'ranged tree:path diff shows mode change' ' check_mode 100644 100755 ' From 18f1ad76392e60cfaf68c3f1e49a4c91bd415d0d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:55:26 -0400 Subject: [PATCH 317/566] handle_revision_arg: record paths for pending objects If the revision parser sees an argument like tree:path, we parse it down to the correct blob (or tree), but throw away the "path" portion. Let's ask get_sha1_with_context() to record it, and pass it along in the pending array. This will let programs like git-diff which rely on the revision-parser show more accurate paths. Note that the implementation is a little tricky; we have to make sure we free oc.path in all code paths. For handle_dotdot(), we can piggy-back on the existing cleanup-wrapper pattern. The real work happens in handle_dotdot_1(), but the handle_dotdot() wrapper makes sure that the path is freed no matter how we exit the function (and for that reason we make sure that the object_context struct is zero'd, so if we fail to even get to the get_sha1_with_context() call, we just end up calling free(NULL)). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/revision.c b/revision.c index 5b56f0e921..a927d263a5 100644 --- a/revision.c +++ b/revision.c @@ -1443,16 +1443,17 @@ static int dotdot_missing(const char *arg, char *dotdot, static int handle_dotdot_1(const char *arg, char *dotdot, struct rev_info *revs, int flags, - int cant_be_filename) + int cant_be_filename, + struct object_context *a_oc, + struct object_context *b_oc) { const char *a_name, *b_name; struct object_id a_oid, b_oid; struct object *a_obj, *b_obj; - struct object_context a_oc, b_oc; unsigned int a_flags, b_flags; int symmetric = 0; unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); - unsigned int oc_flags = GET_SHA1_COMMITTISH; + unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH; a_name = arg; if (!*a_name) @@ -1466,8 +1467,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot, if (!*b_name) b_name = "HEAD"; - if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, &a_oc) || - get_sha1_with_context(b_name, oc_flags, b_oid.hash, &b_oc)) + if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) || + get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc)) return -1; if (!cant_be_filename) { @@ -1509,8 +1510,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot, b_obj->flags |= b_flags; add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags); add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags); - add_pending_object_with_mode(revs, a_obj, a_name, a_oc.mode); - add_pending_object_with_mode(revs, b_obj, b_name, b_oc.mode); + add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path); + add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path); return 0; } @@ -1518,16 +1519,24 @@ static int handle_dotdot(const char *arg, struct rev_info *revs, int flags, int cant_be_filename) { + struct object_context a_oc, b_oc; char *dotdot = strstr(arg, ".."); int ret; if (!dotdot) return -1; + memset(&a_oc, 0, sizeof(a_oc)); + memset(&b_oc, 0, sizeof(b_oc)); + *dotdot = '\0'; - ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename); + ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename, + &a_oc, &b_oc); *dotdot = '.'; + free(a_oc.path); + free(b_oc.path); + return ret; } @@ -1540,7 +1549,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi int local_flags; const char *arg = arg_; int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME; - unsigned get_sha1_flags = 0; + unsigned get_sha1_flags = GET_SHA1_RECORD_PATH; flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM; @@ -1591,7 +1600,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi } if (revarg_opt & REVARG_COMMITTISH) - get_sha1_flags = GET_SHA1_COMMITTISH; + get_sha1_flags |= GET_SHA1_COMMITTISH; if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc)) return revs->ignore_missing ? 0 : -1; @@ -1599,7 +1608,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, sha1, flags ^ local_flags); add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags); - add_pending_object_with_mode(revs, object, arg, oc.mode); + add_pending_object_with_path(revs, object, arg, oc.mode, oc.path); + free(oc.path); return 0; } From 42f5ba5bb6648c16a3c90a0110fbdb430e590a1b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:57:30 -0400 Subject: [PATCH 318/566] diff: pass whole pending entry in blobinfo When diffing blobs directly, git-diff picks the blobs out of the rev_info's pending array and copies the relevant bits to a custom "struct blobinfo". But the pending array entry already has all of this information (and more, which we'll use in future patches). Let's just pass the original entry instead. In practice, these two blobs are probably adjacent in the revs->pending array, and we could just pass the whole array. But the current code is careful to pick each blob out separately and put it into another array, so we'll continue to do so and make our own array-of-pointers. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/diff.c | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/builtin/diff.c b/builtin/diff.c index d184aafab9..8b276ae575 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -20,12 +20,6 @@ #define DIFF_NO_INDEX_EXPLICIT 1 #define DIFF_NO_INDEX_IMPLICIT 2 -struct blobinfo { - struct object_id oid; - const char *name; - unsigned mode; -}; - static const char builtin_diff_usage[] = "git diff [] [ []] [--] [...]"; @@ -65,7 +59,7 @@ static void stuff_change(struct diff_options *opt, static int builtin_diff_b_f(struct rev_info *revs, int argc, const char **argv, - struct blobinfo *blob) + struct object_array_entry **blob) { /* Blob vs file in the working tree*/ struct stat st; @@ -84,12 +78,12 @@ static int builtin_diff_b_f(struct rev_info *revs, diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/"); - if (blob[0].mode == S_IFINVALID) - blob[0].mode = canon_mode(st.st_mode); + if (blob[0]->mode == S_IFINVALID) + blob[0]->mode = canon_mode(st.st_mode); stuff_change(&revs->diffopt, - blob[0].mode, canon_mode(st.st_mode), - &blob[0].oid, &null_oid, + blob[0]->mode, canon_mode(st.st_mode), + &blob[0]->item->oid, &null_oid, 1, 0, path, path); diffcore_std(&revs->diffopt); @@ -99,24 +93,24 @@ static int builtin_diff_b_f(struct rev_info *revs, static int builtin_diff_blobs(struct rev_info *revs, int argc, const char **argv, - struct blobinfo *blob) + struct object_array_entry **blob) { unsigned mode = canon_mode(S_IFREG | 0644); if (argc > 1) usage(builtin_diff_usage); - if (blob[0].mode == S_IFINVALID) - blob[0].mode = mode; + if (blob[0]->mode == S_IFINVALID) + blob[0]->mode = mode; - if (blob[1].mode == S_IFINVALID) - blob[1].mode = mode; + if (blob[1]->mode == S_IFINVALID) + blob[1]->mode = mode; stuff_change(&revs->diffopt, - blob[0].mode, blob[1].mode, - &blob[0].oid, &blob[1].oid, + blob[0]->mode, blob[1]->mode, + &blob[0]->item->oid, &blob[1]->item->oid, 1, 1, - blob[0].name, blob[1].name); + blob[0]->name, blob[1]->name); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; @@ -259,7 +253,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; - struct blobinfo blob[2]; + struct object_array_entry *blob[2]; int nongit = 0, no_index = 0; int result = 0; @@ -408,9 +402,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); - hashcpy(blob[blobs].oid.hash, obj->oid.hash); - blob[blobs].name = name; - blob[blobs].mode = entry->mode; + blob[blobs] = entry; blobs++; } else { From d04ec74b17138733463c0ca1024fdbb42be6096a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:58:05 -0400 Subject: [PATCH 319/566] diff: use the word "path" instead of "name" for blobs The stuff_change() function makes diff_filespecs out of blobs. The term we generally use for filespecs is "path", not "name", so let's be consistent here. That will make things less confusing when the next patch starts caring about the path/name distinction inside the pending object array. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/diff.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/diff.c b/builtin/diff.c index 8b276ae575..4c0811d6fc 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -29,8 +29,8 @@ static void stuff_change(struct diff_options *opt, const struct object_id *new_oid, int old_oid_valid, int new_oid_valid, - const char *old_name, - const char *new_name) + const char *old_path, + const char *new_path) { struct diff_filespec *one, *two; @@ -41,16 +41,16 @@ static void stuff_change(struct diff_options *opt, if (DIFF_OPT_TST(opt, REVERSE_DIFF)) { SWAP(old_mode, new_mode); SWAP(old_oid, new_oid); - SWAP(old_name, new_name); + SWAP(old_path, new_path); } if (opt->prefix && - (strncmp(old_name, opt->prefix, opt->prefix_length) || - strncmp(new_name, opt->prefix, opt->prefix_length))) + (strncmp(old_path, opt->prefix, opt->prefix_length) || + strncmp(new_path, opt->prefix, opt->prefix_length))) return; - one = alloc_filespec(old_name); - two = alloc_filespec(new_name); + one = alloc_filespec(old_path); + two = alloc_filespec(new_path); fill_filespec(one, old_oid->hash, old_oid_valid, old_mode); fill_filespec(two, new_oid->hash, new_oid_valid, new_mode); From 158b06caee5f9ea2987f444b8e49bd2d678b187d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:59:15 -0400 Subject: [PATCH 320/566] diff: use pending "path" if it is available There's a subtle distinction between "name" and "path" for a blob that we resolve: the name is what the user told us on the command line, and the path is what we traversed when finding the blob within a tree (if we did so). When we diff blobs directly, we use "name", but "path" is more likely to be useful to the user (it will find the correct .gitattributes, and give them a saner diff header). We still have to fall back to using the name for some cases (i.e., any blob reference that isn't of the form tree:path). That's the best we can do in such a case. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/diff.c | 7 ++++++- t/t4063-diff-blobs.sh | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin/diff.c b/builtin/diff.c index 4c0811d6fc..1a1149eed4 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -23,6 +23,11 @@ static const char builtin_diff_usage[] = "git diff [] [ []] [--] [...]"; +static const char *blob_path(struct object_array_entry *entry) +{ + return entry->path ? entry->path : entry->name; +} + static void stuff_change(struct diff_options *opt, unsigned old_mode, unsigned new_mode, const struct object_id *old_oid, @@ -110,7 +115,7 @@ static int builtin_diff_blobs(struct rev_info *revs, blob[0]->mode, blob[1]->mode, &blob[0]->item->oid, &blob[1]->item->oid, 1, 1, - blob[0]->name, blob[1]->name); + blob_path(blob[0]), blob_path(blob[1])); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh index df9c35b2dd..80ce033ab6 100755 --- a/t/t4063-diff-blobs.sh +++ b/t/t4063-diff-blobs.sh @@ -55,7 +55,7 @@ test_expect_success 'diff by tree:path (run)' ' test_expect_success 'index of tree:path diff' ' check_index $sha1_one $sha1_two ' -test_expect_failure 'tree:path diff uses filenames as paths' ' +test_expect_success 'tree:path diff uses filenames as paths' ' check_paths one two ' test_expect_success 'tree:path diff shows mode change' ' @@ -68,7 +68,7 @@ test_expect_success 'diff by ranged tree:path' ' test_expect_success 'index of ranged tree:path diff' ' check_index $sha1_one $sha1_two ' -test_expect_failure 'ranged tree:path diff uses filenames as paths' ' +test_expect_success 'ranged tree:path diff uses filenames as paths' ' check_paths one two ' test_expect_success 'ranged tree:path diff shows mode change' ' From 30d005c02014680403b5d35ef274047ab91fa5bd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 19 May 2017 08:59:34 -0400 Subject: [PATCH 321/566] diff: use blob path for blob/file diffs When we diff a blob against a working tree file like: git diff HEAD:Makefile Makefile we always use the working tree filename for both sides of the diff. In most cases that's fine, as the two would be the same anyway, as above. And until recently, we used the "name" for the blob, not the path, which would have the messy "HEAD:" on the beginning. But when they don't match, like: git diff HEAD:old_path new_path it makes sense to show both names. This patch uses the blob's path field if it's available, and otherwise falls back to using the filename (in preference to the blob's name, which is likely to be garbage like a raw sha1). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/diff.c | 3 ++- t/t4063-diff-blobs.sh | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/builtin/diff.c b/builtin/diff.c index 1a1149eed4..5e7c6428c9 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -90,7 +90,8 @@ static int builtin_diff_b_f(struct rev_info *revs, blob[0]->mode, canon_mode(st.st_mode), &blob[0]->item->oid, &null_oid, 1, 0, - path, path); + blob[0]->path ? blob[0]->path : path, + path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh index 80ce033ab6..bc69e26c52 100755 --- a/t/t4063-diff-blobs.sh +++ b/t/t4063-diff-blobs.sh @@ -81,11 +81,16 @@ test_expect_success 'diff blob against file' ' test_expect_success 'index of blob-file diff' ' check_index $sha1_one $sha1_two ' -test_expect_failure 'blob-file diff uses filename as paths' ' +test_expect_success 'blob-file diff uses filename as paths' ' check_paths one two ' test_expect_success FILEMODE 'blob-file diff shows mode change' ' check_mode 100644 100755 ' +test_expect_success 'blob-file diff prefers filename to sha1' ' + run_diff $sha1_one two && + check_paths two two +' + test_done From b84bc9c3677c9746bfd81a64ed30b0670c8ff0f9 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:09 -0500 Subject: [PATCH 322/566] blame: remove unneeded dependency on blob.h With commit 21666f1 ("convert object type handling from a string to a number", 2007-02-26), there was no longer a need for blame.c to include blob.h but it was not removed. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin/blame.c b/builtin/blame.c index f00eda1637..d39f6af1a9 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -8,7 +8,6 @@ #include "cache.h" #include "refs.h" #include "builtin.h" -#include "blob.h" #include "commit.h" #include "tag.h" #include "tree-walk.h" From 3a35cb2ea8ab5d44d6ea6290b7af8a7c8623e4c2 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:10 -0500 Subject: [PATCH 323/566] blame: move textconv_object with related functions textconv_object is used in places other than blame.c and should be moved to a more appropriate location. Other textconv related functions are located in diff.c so that seems as good a place as any. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin.h | 2 -- builtin/blame.c | 28 ---------------------------- builtin/cat-file.c | 1 + diff.c | 23 +++++++++++++++++++++++ diff.h | 7 +++++++ 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/builtin.h b/builtin.h index 9e4a89816d..498ac80d07 100644 --- a/builtin.h +++ b/builtin.h @@ -25,8 +25,6 @@ struct fmt_merge_msg_opts { extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out, struct fmt_merge_msg_opts *); -extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size); - extern int is_builtin(const char *s); extern int cmd_add(int argc, const char **argv, const char *prefix); diff --git a/builtin/blame.c b/builtin/blame.c index d39f6af1a9..fbd757e583 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -146,34 +146,6 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb); } -/* - * Prepare diff_filespec and convert it using diff textconv API - * if the textconv driver exists. - * Return 1 if the conversion succeeds, 0 otherwise. - */ -int textconv_object(const char *path, - unsigned mode, - const struct object_id *oid, - int oid_valid, - char **buf, - unsigned long *buf_size) -{ - struct diff_filespec *df; - struct userdiff_driver *textconv; - - df = alloc_filespec(path); - fill_filespec(df, oid->hash, oid_valid, mode); - textconv = get_textconv(df); - if (!textconv) { - free_filespec(df); - return 0; - } - - *buf_size = fill_textconv(textconv, df, buf); - free_filespec(df); - return 1; -} - /* * Given an origin, prepare mmfile_t structure to be used by the * diff machinery diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 1890d7a639..79a2c82c56 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -5,6 +5,7 @@ */ #include "cache.h" #include "builtin.h" +#include "diff.h" #include "parse-options.h" #include "userdiff.h" #include "streaming.h" diff --git a/diff.c b/diff.c index 74283d9001..040fb2567c 100644 --- a/diff.c +++ b/diff.c @@ -5270,6 +5270,29 @@ size_t fill_textconv(struct userdiff_driver *driver, return size; } +int textconv_object(const char *path, + unsigned mode, + const struct object_id *oid, + int oid_valid, + char **buf, + unsigned long *buf_size) +{ + struct diff_filespec *df; + struct userdiff_driver *textconv; + + df = alloc_filespec(path); + fill_filespec(df, oid->hash, oid_valid, mode); + textconv = get_textconv(df); + if (!textconv) { + free_filespec(df); + return 0; + } + + *buf_size = fill_textconv(textconv, df, buf); + free_filespec(df); + return 1; +} + void setup_diff_pager(struct diff_options *opt) { /* diff --git a/diff.h b/diff.h index 5be1ee77a7..52ebd54d9e 100644 --- a/diff.h +++ b/diff.h @@ -385,6 +385,13 @@ extern size_t fill_textconv(struct userdiff_driver *driver, */ extern struct userdiff_driver *get_textconv(struct diff_filespec *one); +/* + * Prepare diff_filespec and convert it using diff textconv API + * if the textconv driver exists. + * Return 1 if the conversion succeeds, 0 otherwise. + */ +extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size); + extern int parse_rename_score(const char **cp_p); extern long parse_algorithm_value(const char *value); From 8265921c3c70c757d68b76f2f013a4c6bd3c37d4 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:11 -0500 Subject: [PATCH 324/566] blame: remove unused parameters Clean up blame code before moving it into libgit Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index fbd757e583..3529f01c00 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -449,9 +449,7 @@ static struct origin *make_origin(struct commit *commit, const char *path) * Locate an existing origin or create a new one. * This moves the origin to front position in the commit util list. */ -static struct origin *get_origin(struct scoreboard *sb, - struct commit *commit, - const char *path) +static struct origin *get_origin(struct commit *commit, const char *path) { struct origin *o, *l; @@ -499,8 +497,7 @@ static int fill_blob_sha1_and_mode(struct origin *origin) * We have an origin -- check if the same path exists in the * parent and return an origin structure to represent it. */ -static struct origin *find_origin(struct scoreboard *sb, - struct commit *parent, +static struct origin *find_origin(struct commit *parent, struct origin *origin) { struct origin *porigin; @@ -543,7 +540,7 @@ static struct origin *find_origin(struct scoreboard *sb, if (!diff_queued_diff.nr) { /* The path is the same as parent */ - porigin = get_origin(sb, parent, origin->path); + porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &origin->blob_oid); porigin->mode = origin->mode; } else { @@ -569,7 +566,7 @@ static struct origin *find_origin(struct scoreboard *sb, die("internal error in blame::find_origin (%c)", p->status); case 'M': - porigin = get_origin(sb, parent, origin->path); + porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; @@ -588,8 +585,7 @@ static struct origin *find_origin(struct scoreboard *sb, * We have an origin -- find the path that corresponds to it in its * parent and return an origin structure to represent it. */ -static struct origin *find_rename(struct scoreboard *sb, - struct commit *parent, +static struct origin *find_rename(struct commit *parent, struct origin *origin) { struct origin *porigin = NULL; @@ -615,7 +611,7 @@ static struct origin *find_rename(struct scoreboard *sb, struct diff_filepair *p = diff_queued_diff.queue[i]; if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, origin->path)) { - porigin = get_origin(sb, parent, p->one->path); + porigin = get_origin(parent, p->one->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; @@ -1270,7 +1266,7 @@ static void find_copy_in_parent(struct scoreboard *sb, /* find_move already dealt with this path */ continue; - norigin = get_origin(sb, parent, p->one->path); + norigin = get_origin(parent, p->one->path); oidcpy(&norigin->blob_oid, &p->one->oid); norigin->mode = p->one->mode; fill_origin_blob(&sb->revs->diffopt, norigin, &file_p); @@ -1404,8 +1400,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * common cases, then we look for renames in the second pass. */ for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { - struct origin *(*find)(struct scoreboard *, - struct commit *, struct origin *); + struct origin *(*find)(struct commit *, struct origin *); find = pass ? find_rename : find_origin; for (i = 0, sg = first_scapegoat(revs, commit); @@ -1418,7 +1413,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) continue; if (parse_commit(p)) continue; - porigin = find(sb, p, origin); + porigin = find(p, origin); if (!porigin) continue; if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) { @@ -2806,7 +2801,7 @@ parse_done: sb.final_buf_size = o->file.size; } else { - o = get_origin(&sb, sb.final, path); + o = get_origin(sb.final, path); if (fill_blob_sha1_and_mode(o)) die(_("no such path %s in %s"), path, final_commit_name); From f84afb9c4ee09d640b2d6cbe38fe6f0d14ca70be Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:12 -0500 Subject: [PATCH 325/566] blame: rename origin structure to blame_origin The origin structure is core to the blame interface. Since origin will become more exposed, rename it to blame_origin to clarify what it is a part of. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 114 ++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 3529f01c00..3e8aaa4ca6 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -87,10 +87,10 @@ static unsigned blame_copy_score; /* * One blob in a commit that is being suspected */ -struct origin { +struct blame_origin { int refcnt; /* Record preceding blame record for this blob */ - struct origin *previous; + struct blame_origin *previous; /* origins are put in a list linked via `next' hanging off the * corresponding commit's util field in order to make finding * them fast. The presence in this chain does not count @@ -108,7 +108,7 @@ struct origin { * us get tripped up by this case, it certainly does not seem * worth optimizing for. */ - struct origin *next; + struct blame_origin *next; struct commit *commit; /* `suspects' contains blame entries that may be attributed to * this origin's commit or to parent commits. When a commit @@ -151,7 +151,7 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, * diff machinery */ static void fill_origin_blob(struct diff_options *opt, - struct origin *o, mmfile_t *file) + struct blame_origin *o, mmfile_t *file) { if (!o->file.ptr) { enum object_type type; @@ -180,17 +180,17 @@ static void fill_origin_blob(struct diff_options *opt, * Origin is refcounted and usually we keep the blob contents to be * reused. */ -static inline struct origin *origin_incref(struct origin *o) +static inline struct blame_origin *origin_incref(struct blame_origin *o) { if (o) o->refcnt++; return o; } -static void origin_decref(struct origin *o) +static void origin_decref(struct blame_origin *o) { if (o && --o->refcnt <= 0) { - struct origin *p, *l = NULL; + struct blame_origin *p, *l = NULL; if (o->previous) origin_decref(o->previous); free(o->file.ptr); @@ -209,7 +209,7 @@ static void origin_decref(struct origin *o) } } -static void drop_origin_blob(struct origin *o) +static void drop_origin_blob(struct blame_origin *o) { if (o->file.ptr) { free(o->file.ptr); @@ -238,7 +238,7 @@ struct blame_entry { int num_lines; /* the commit that introduced this group into the final image */ - struct origin *suspect; + struct blame_origin *suspect; /* the line number of the first line of this group in the * suspect's file; internally all line numbers are 0 based. @@ -410,13 +410,13 @@ static void coalesce(struct scoreboard *sb) * the commit priority queue of the score board. */ -static void queue_blames(struct scoreboard *sb, struct origin *porigin, +static void queue_blames(struct scoreboard *sb, struct blame_origin *porigin, struct blame_entry *sorted) { if (porigin->suspects) porigin->suspects = blame_merge(porigin->suspects, sorted); else { - struct origin *o; + struct blame_origin *o; for (o = porigin->commit->util; o; o = o->next) { if (o->suspects) { porigin->suspects = sorted; @@ -434,9 +434,9 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin, * get_origin() to obtain shared, refcounted copy instead of calling * this function directly. */ -static struct origin *make_origin(struct commit *commit, const char *path) +static struct blame_origin *make_origin(struct commit *commit, const char *path) { - struct origin *o; + struct blame_origin *o; FLEX_ALLOC_STR(o, path, path); o->commit = commit; o->refcnt = 1; @@ -449,9 +449,9 @@ static struct origin *make_origin(struct commit *commit, const char *path) * Locate an existing origin or create a new one. * This moves the origin to front position in the commit util list. */ -static struct origin *get_origin(struct commit *commit, const char *path) +static struct blame_origin *get_origin(struct commit *commit, const char *path) { - struct origin *o, *l; + struct blame_origin *o, *l; for (o = commit->util, l = NULL; o; l = o, o = o->next) { if (!strcmp(o->path, path)) { @@ -476,7 +476,7 @@ static struct origin *get_origin(struct commit *commit, const char *path) * * This also fills origin->mode for corresponding tree path. */ -static int fill_blob_sha1_and_mode(struct origin *origin) +static int fill_blob_sha1_and_mode(struct blame_origin *origin) { if (!is_null_oid(&origin->blob_oid)) return 0; @@ -497,10 +497,10 @@ static int fill_blob_sha1_and_mode(struct origin *origin) * We have an origin -- check if the same path exists in the * parent and return an origin structure to represent it. */ -static struct origin *find_origin(struct commit *parent, - struct origin *origin) +static struct blame_origin *find_origin(struct commit *parent, + struct blame_origin *origin) { - struct origin *porigin; + struct blame_origin *porigin; struct diff_options diff_opts; const char *paths[2]; @@ -585,10 +585,10 @@ static struct origin *find_origin(struct commit *parent, * We have an origin -- find the path that corresponds to it in its * parent and return an origin structure to represent it. */ -static struct origin *find_rename(struct commit *parent, - struct origin *origin) +static struct blame_origin *find_rename(struct commit *parent, + struct blame_origin *origin) { - struct origin *porigin = NULL; + struct blame_origin *porigin = NULL; struct diff_options diff_opts; int i; @@ -680,7 +680,7 @@ static const char *nth_line_cb(void *data, long lno) static void split_overlap(struct blame_entry *split, struct blame_entry *e, int tlno, int plno, int same, - struct origin *parent) + struct blame_origin *parent) { int chunk_end_lno; memset(split, 0, sizeof(struct blame_entry [3])); @@ -804,7 +804,7 @@ static struct blame_entry *reverse_blame(struct blame_entry *head, */ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, int tlno, int offset, int same, - struct origin *parent) + struct blame_origin *parent) { struct blame_entry *e = **srcq; struct blame_entry *samep = NULL, *diffp = NULL; @@ -895,7 +895,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, } struct blame_chunk_cb_data { - struct origin *parent; + struct blame_origin *parent; long offset; struct blame_entry **dstq; struct blame_entry **srcq; @@ -920,8 +920,8 @@ static int blame_chunk_cb(long start_a, long count_a, * which lines came from parent and pass blame for them. */ static void pass_blame_to_parent(struct scoreboard *sb, - struct origin *target, - struct origin *parent) + struct blame_origin *target, + struct blame_origin *parent) { mmfile_t file_p, file_o; struct blame_chunk_cb_data d; @@ -1023,7 +1023,7 @@ static void copy_split_if_better(struct scoreboard *sb, static void handle_split(struct scoreboard *sb, struct blame_entry *ent, int tlno, int plno, int same, - struct origin *parent, + struct blame_origin *parent, struct blame_entry *split) { if (ent->num_lines <= tlno) @@ -1041,7 +1041,7 @@ static void handle_split(struct scoreboard *sb, struct handle_split_cb_data { struct scoreboard *sb; struct blame_entry *ent; - struct origin *parent; + struct blame_origin *parent; struct blame_entry *split; long plno; long tlno; @@ -1065,7 +1065,7 @@ static int handle_split_cb(long start_a, long count_a, */ static void find_copy_in_blob(struct scoreboard *sb, struct blame_entry *ent, - struct origin *parent, + struct blame_origin *parent, struct blame_entry *split, mmfile_t *file_p) { @@ -1129,8 +1129,8 @@ static struct blame_entry **filter_small(struct scoreboard *sb, static void find_move_in_parent(struct scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, - struct origin *target, - struct origin *parent) + struct blame_origin *target, + struct blame_origin *parent) { struct blame_entry *e, split[3]; struct blame_entry *unblamed = target->suspects; @@ -1205,9 +1205,9 @@ static struct blame_list *setup_blame_list(struct blame_entry *unblamed, static void find_copy_in_parent(struct scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, - struct origin *target, + struct blame_origin *target, struct commit *parent, - struct origin *porigin, + struct blame_origin *porigin, int opt) { struct diff_options diff_opts; @@ -1254,7 +1254,7 @@ static void find_copy_in_parent(struct scoreboard *sb, for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; - struct origin *norigin; + struct blame_origin *norigin; mmfile_t file_p; struct blame_entry this[3]; @@ -1309,7 +1309,7 @@ static void find_copy_in_parent(struct scoreboard *sb, * origin is suspected for can be blamed on the parent. */ static void pass_whole_blame(struct scoreboard *sb, - struct origin *origin, struct origin *porigin) + struct blame_origin *origin, struct blame_origin *porigin) { struct blame_entry *e, *suspects; @@ -1361,7 +1361,7 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed) blamed = blame_sort(blamed, compare_blame_suspect); while (blamed) { - struct origin *porigin = blamed->suspect; + struct blame_origin *porigin = blamed->suspect; struct blame_entry *suspects = NULL; do { struct blame_entry *next = blamed->next; @@ -1376,14 +1376,14 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed) #define MAXSG 16 -static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) +static void pass_blame(struct scoreboard *sb, struct blame_origin *origin, int opt) { struct rev_info *revs = sb->revs; int i, pass, num_sg; struct commit *commit = origin->commit; struct commit_list *sg; - struct origin *sg_buf[MAXSG]; - struct origin *porigin, **sg_origin = sg_buf; + struct blame_origin *sg_buf[MAXSG]; + struct blame_origin *porigin, **sg_origin = sg_buf; struct blame_entry *toosmall = NULL; struct blame_entry *blames, **blametail = &blames; @@ -1400,7 +1400,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * common cases, then we look for renames in the second pass. */ for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { - struct origin *(*find)(struct commit *, struct origin *); + struct blame_origin *(*find)(struct commit *, struct blame_origin *); find = pass ? find_rename : find_origin; for (i = 0, sg = first_scapegoat(revs, commit); @@ -1438,7 +1438,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { - struct origin *porigin = sg_origin[i]; + struct blame_origin *porigin = sg_origin[i]; if (!porigin) continue; if (!origin->previous) { @@ -1459,7 +1459,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { - struct origin *porigin = sg_origin[i]; + struct blame_origin *porigin = sg_origin[i]; if (!porigin) continue; find_move_in_parent(sb, &blametail, &toosmall, origin, porigin); @@ -1486,7 +1486,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { - struct origin *porigin = sg_origin[i]; + struct blame_origin *porigin = sg_origin[i]; find_copy_in_parent(sb, &blametail, &toosmall, origin, sg->item, porigin, opt); if (!origin->suspects) @@ -1665,10 +1665,10 @@ static void get_commit_info(struct commit *commit, * To allow LF and other nonportable characters in pathnames, * they are c-style quoted as needed. */ -static void write_filename_info(struct origin *suspect) +static void write_filename_info(struct blame_origin *suspect) { if (suspect->previous) { - struct origin *prev = suspect->previous; + struct blame_origin *prev = suspect->previous; printf("previous %s ", oid_to_hex(&prev->commit->object.oid)); write_name_quoted(prev->path, stdout, '\n'); } @@ -1682,7 +1682,7 @@ static void write_filename_info(struct origin *suspect) * the first time each commit appears in the output (unless the * user has specifically asked for us to repeat). */ -static int emit_one_suspect_detail(struct origin *suspect, int repeat) +static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat) { struct commit_info ci; @@ -1716,7 +1716,7 @@ static void found_guilty_entry(struct blame_entry *ent, struct progress_info *pi) { if (incremental) { - struct origin *suspect = ent->suspect; + struct blame_origin *suspect = ent->suspect; printf("%s %d %d %d\n", oid_to_hex(&suspect->commit->object.oid), @@ -1745,7 +1745,7 @@ static void assign_blame(struct scoreboard *sb, int opt) while (commit) { struct blame_entry *ent; - struct origin *suspect = commit->util; + struct blame_origin *suspect = commit->util; /* find one suspect to break down */ while (suspect && !suspect->suspects) @@ -1842,7 +1842,7 @@ static const char *format_time(timestamp_t time, const char *tz_str, #define OUTPUT_SHOW_EMAIL 0400 #define OUTPUT_LINE_PORCELAIN 01000 -static void emit_porcelain_details(struct origin *suspect, int repeat) +static void emit_porcelain_details(struct blame_origin *suspect, int repeat) { if (emit_one_suspect_detail(suspect, repeat) || (suspect->commit->object.flags & MORE_THAN_ONE_PATH)) @@ -1855,7 +1855,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent, int repeat = opt & OUTPUT_LINE_PORCELAIN; int cnt; const char *cp; - struct origin *suspect = ent->suspect; + struct blame_origin *suspect = ent->suspect; char hex[GIT_MAX_HEXSZ + 1]; oid_to_hex_r(hex, &suspect->commit->object.oid); @@ -1892,7 +1892,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) { int cnt; const char *cp; - struct origin *suspect = ent->suspect; + struct blame_origin *suspect = ent->suspect; struct commit_info ci; char hex[GIT_MAX_HEXSZ + 1]; int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); @@ -1974,7 +1974,7 @@ static void output(struct scoreboard *sb, int option) if (option & OUTPUT_PORCELAIN) { for (ent = sb->ent; ent; ent = ent->next) { int count = 0; - struct origin *suspect; + struct blame_origin *suspect; struct commit *commit = ent->suspect->commit; if (commit->object.flags & MORE_THAN_ONE_PATH) continue; @@ -2052,7 +2052,7 @@ static int read_ancestry(const char *graft_file) return 0; } -static int update_auto_abbrev(int auto_abbrev, struct origin *suspect) +static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) { const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash, auto_abbrev); @@ -2076,7 +2076,7 @@ static void find_alignment(struct scoreboard *sb, int *option) int auto_abbrev = DEFAULT_ABBREV; for (e = sb->ent; e; e = e->next) { - struct origin *suspect = e->suspect; + struct blame_origin *suspect = e->suspect; int num; if (compute_auto_abbrev) @@ -2268,7 +2268,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, const char *contents_from) { struct commit *commit; - struct origin *origin; + struct blame_origin *origin; struct commit_list **parent_tail, *parent; struct object_id head_oid; struct strbuf buf = STRBUF_INIT; @@ -2523,7 +2523,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) struct rev_info revs; const char *path; struct scoreboard sb; - struct origin *o; + struct blame_origin *o; struct blame_entry *ent = NULL; long dashdash_pos, lno; char *final_commit_name = NULL; From 9807b3d65d3789ed7916ffc98dbdf7ed89634ca3 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:13 -0500 Subject: [PATCH 326/566] blame: rename scoreboard structure to blame_scoreboard The scoreboard structure is core to the blame interface. Since scoreboard will become more exposed, rename it to blame_scoreboard to clarify what it is a part of. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 3e8aaa4ca6..717868e7e3 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -353,7 +353,7 @@ static int compare_commits_by_reverse_commit_date(const void *a, /* * The current state of the blame assignment. */ -struct scoreboard { +struct blame_scoreboard { /* the final commit (i.e. where we started digging from) */ struct commit *final; /* Priority queue for commits with unassigned blame records */ @@ -377,14 +377,14 @@ struct scoreboard { int *lineno; }; -static void sanity_check_refcnt(struct scoreboard *); +static void sanity_check_refcnt(struct blame_scoreboard *); /* * If two blame entries that are next to each other came from * contiguous lines in the same origin (i.e. pair), * merge them together. */ -static void coalesce(struct scoreboard *sb) +static void coalesce(struct blame_scoreboard *sb) { struct blame_entry *ent, *next; @@ -410,7 +410,7 @@ static void coalesce(struct scoreboard *sb) * the commit priority queue of the score board. */ -static void queue_blames(struct scoreboard *sb, struct blame_origin *porigin, +static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin, struct blame_entry *sorted) { if (porigin->suspects) @@ -653,14 +653,14 @@ static void dup_entry(struct blame_entry ***queue, *queue = &dst->next; } -static const char *nth_line(struct scoreboard *sb, long lno) +static const char *nth_line(struct blame_scoreboard *sb, long lno) { return sb->final_buf + sb->lineno[lno]; } static const char *nth_line_cb(void *data, long lno) { - return nth_line((struct scoreboard *)data, lno); + return nth_line((struct blame_scoreboard *)data, lno); } /* @@ -919,7 +919,7 @@ static int blame_chunk_cb(long start_a, long count_a, * for the lines it is suspected to its parent. Run diff to find * which lines came from parent and pass blame for them. */ -static void pass_blame_to_parent(struct scoreboard *sb, +static void pass_blame_to_parent(struct blame_scoreboard *sb, struct blame_origin *target, struct blame_origin *parent) { @@ -959,7 +959,7 @@ static void pass_blame_to_parent(struct scoreboard *sb, * * Compute how trivial the lines in the blame_entry are. */ -static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e) +static unsigned ent_score(struct blame_scoreboard *sb, struct blame_entry *e) { unsigned score; const char *cp, *ep; @@ -986,7 +986,7 @@ static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e) * so far, by comparing this and best_so_far and copying this into * bst_so_far as needed. */ -static void copy_split_if_better(struct scoreboard *sb, +static void copy_split_if_better(struct blame_scoreboard *sb, struct blame_entry *best_so_far, struct blame_entry *this) { @@ -1020,7 +1020,7 @@ static void copy_split_if_better(struct scoreboard *sb, * * All line numbers are 0-based. */ -static void handle_split(struct scoreboard *sb, +static void handle_split(struct blame_scoreboard *sb, struct blame_entry *ent, int tlno, int plno, int same, struct blame_origin *parent, @@ -1039,7 +1039,7 @@ static void handle_split(struct scoreboard *sb, } struct handle_split_cb_data { - struct scoreboard *sb; + struct blame_scoreboard *sb; struct blame_entry *ent; struct blame_origin *parent; struct blame_entry *split; @@ -1063,7 +1063,7 @@ static int handle_split_cb(long start_a, long count_a, * we can pass blames to it. file_p has the blob contents for * the parent. */ -static void find_copy_in_blob(struct scoreboard *sb, +static void find_copy_in_blob(struct blame_scoreboard *sb, struct blame_entry *ent, struct blame_origin *parent, struct blame_entry *split, @@ -1099,7 +1099,7 @@ static void find_copy_in_blob(struct scoreboard *sb, * Returns a pointer to the link pointing to the old head of the small list. */ -static struct blame_entry **filter_small(struct scoreboard *sb, +static struct blame_entry **filter_small(struct blame_scoreboard *sb, struct blame_entry **small, struct blame_entry **source, unsigned score_min) @@ -1126,7 +1126,7 @@ static struct blame_entry **filter_small(struct scoreboard *sb, * See if lines currently target is suspected for can be attributed to * parent. */ -static void find_move_in_parent(struct scoreboard *sb, +static void find_move_in_parent(struct blame_scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, struct blame_origin *target, @@ -1202,7 +1202,7 @@ static struct blame_list *setup_blame_list(struct blame_entry *unblamed, * across file boundary from the parent commit. porigin is the path * in the parent we already tried. */ -static void find_copy_in_parent(struct scoreboard *sb, +static void find_copy_in_parent(struct blame_scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, struct blame_origin *target, @@ -1308,7 +1308,7 @@ static void find_copy_in_parent(struct scoreboard *sb, * The blobs of origin and porigin exactly match, so everything * origin is suspected for can be blamed on the parent. */ -static void pass_whole_blame(struct scoreboard *sb, +static void pass_whole_blame(struct blame_scoreboard *sb, struct blame_origin *origin, struct blame_origin *porigin) { struct blame_entry *e, *suspects; @@ -1356,7 +1356,7 @@ static int num_scapegoats(struct rev_info *revs, struct commit *commit) /* Distribute collected unsorted blames to the respected sorted lists * in the various origins. */ -static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed) +static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed) { blamed = blame_sort(blamed, compare_blame_suspect); while (blamed) @@ -1376,7 +1376,7 @@ static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed) #define MAXSG 16 -static void pass_blame(struct scoreboard *sb, struct blame_origin *origin, int opt) +static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt) { struct rev_info *revs = sb->revs; int i, pass, num_sg; @@ -1733,7 +1733,7 @@ static void found_guilty_entry(struct blame_entry *ent, * The main loop -- while we have blobs with lines whose true origin * is still unknown, pick one blob, and allow its lines to pass blames * to its parents. */ -static void assign_blame(struct scoreboard *sb, int opt) +static void assign_blame(struct blame_scoreboard *sb, int opt) { struct rev_info *revs = sb->revs; struct commit *commit = prio_queue_get(&sb->commits); @@ -1849,7 +1849,7 @@ static void emit_porcelain_details(struct blame_origin *suspect, int repeat) write_filename_info(suspect); } -static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent, +static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int repeat = opt & OUTPUT_LINE_PORCELAIN; @@ -1888,7 +1888,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent, putchar('\n'); } -static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) +static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int cnt; const char *cp; @@ -1967,7 +1967,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) commit_info_destroy(&ci); } -static void output(struct scoreboard *sb, int option) +static void output(struct blame_scoreboard *sb, int option) { struct blame_entry *ent; @@ -2006,7 +2006,7 @@ static const char *get_next_line(const char *start, const char *end) * To allow quick access to the contents of nth line in the * final image, prepare an index in the scoreboard. */ -static int prepare_lines(struct scoreboard *sb) +static int prepare_lines(struct blame_scoreboard *sb) { const char *buf = sb->final_buf; unsigned long len = sb->final_buf_size; @@ -2066,7 +2066,7 @@ static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) * How many columns do we need to show line numbers, authors, * and filenames? */ -static void find_alignment(struct scoreboard *sb, int *option) +static void find_alignment(struct blame_scoreboard *sb, int *option) { int longest_src_lines = 0; int longest_dst_lines = 0; @@ -2120,7 +2120,7 @@ static void find_alignment(struct scoreboard *sb, int *option) * For debugging -- origin is refcounted, and this asserts that * we do not underflow. */ -static void sanity_check_refcnt(struct scoreboard *sb) +static void sanity_check_refcnt(struct blame_scoreboard *sb) { int baa = 0; struct blame_entry *ent; @@ -2411,14 +2411,14 @@ static struct commit *find_single_final(struct rev_info *revs, return found; } -static char *prepare_final(struct scoreboard *sb) +static char *prepare_final(struct blame_scoreboard *sb) { const char *name; sb->final = find_single_final(sb->revs, &name); return xstrdup_or_null(name); } -static const char *dwim_reverse_initial(struct scoreboard *sb) +static const char *dwim_reverse_initial(struct blame_scoreboard *sb) { /* * DWIM "git blame --reverse ONE -- PATH" as @@ -2453,7 +2453,7 @@ static const char *dwim_reverse_initial(struct scoreboard *sb) return sb->revs->pending.objects[0].name; } -static char *prepare_initial(struct scoreboard *sb) +static char *prepare_initial(struct blame_scoreboard *sb) { int i; const char *final_commit_name = NULL; @@ -2522,7 +2522,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; const char *path; - struct scoreboard sb; + struct blame_scoreboard sb; struct blame_origin *o; struct blame_entry *ent = NULL; long dashdash_pos, lno; From 006a0744998804d43a02034bf44b97a163027bb5 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:14 -0500 Subject: [PATCH 327/566] blame: rename origin-related functions Functions related to blame_origin that will be publicly exposed should have names that better reflect what they are a part of. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 717868e7e3..7854770d85 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -180,19 +180,19 @@ static void fill_origin_blob(struct diff_options *opt, * Origin is refcounted and usually we keep the blob contents to be * reused. */ -static inline struct blame_origin *origin_incref(struct blame_origin *o) +static inline struct blame_origin *blame_origin_incref(struct blame_origin *o) { if (o) o->refcnt++; return o; } -static void origin_decref(struct blame_origin *o) +static void blame_origin_decref(struct blame_origin *o) { if (o && --o->refcnt <= 0) { struct blame_origin *p, *l = NULL; if (o->previous) - origin_decref(o->previous); + blame_origin_decref(o->previous); free(o->file.ptr); /* Should be present exactly once in commit chain */ for (p = o->commit->util; p; l = p, p = p->next) { @@ -205,7 +205,7 @@ static void origin_decref(struct blame_origin *o) return; } } - die("internal error in blame::origin_decref"); + die("internal error in blame_origin_decref"); } } @@ -393,7 +393,7 @@ static void coalesce(struct blame_scoreboard *sb) ent->s_lno + ent->num_lines == next->s_lno) { ent->num_lines += next->num_lines; ent->next = next->next; - origin_decref(next->suspect); + blame_origin_decref(next->suspect); free(next); ent->score = 0; next = ent; /* again */ @@ -461,7 +461,7 @@ static struct blame_origin *get_origin(struct commit *commit, const char *path) o->next = commit->util; commit->util = o; } - return origin_incref(o); + return blame_origin_incref(o); } } return make_origin(commit, path); @@ -511,7 +511,7 @@ static struct blame_origin *find_origin(struct commit *parent, * The same path between origin and its parent * without renaming -- the most common case. */ - return origin_incref (porigin); + return blame_origin_incref (porigin); } /* See if the origin->path is different between parent @@ -630,7 +630,7 @@ static void add_blame_entry(struct blame_entry ***queue, { struct blame_entry *e = xmalloc(sizeof(*e)); memcpy(e, src, sizeof(*e)); - origin_incref(e->suspect); + blame_origin_incref(e->suspect); e->next = **queue; **queue = e; @@ -645,8 +645,8 @@ static void add_blame_entry(struct blame_entry ***queue, static void dup_entry(struct blame_entry ***queue, struct blame_entry *dst, struct blame_entry *src) { - origin_incref(src->suspect); - origin_decref(dst->suspect); + blame_origin_incref(src->suspect); + blame_origin_decref(dst->suspect); memcpy(dst, src, sizeof(*src)); dst->next = **queue; **queue = dst; @@ -687,7 +687,7 @@ static void split_overlap(struct blame_entry *split, if (e->s_lno < tlno) { /* there is a pre-chunk part not blamed on parent */ - split[0].suspect = origin_incref(e->suspect); + split[0].suspect = blame_origin_incref(e->suspect); split[0].lno = e->lno; split[0].s_lno = e->s_lno; split[0].num_lines = tlno - e->s_lno; @@ -701,7 +701,7 @@ static void split_overlap(struct blame_entry *split, if (same < e->s_lno + e->num_lines) { /* there is a post-chunk part not blamed on parent */ - split[2].suspect = origin_incref(e->suspect); + split[2].suspect = blame_origin_incref(e->suspect); split[2].lno = e->lno + (same - e->s_lno); split[2].s_lno = e->s_lno + (same - e->s_lno); split[2].num_lines = e->s_lno + e->num_lines - same; @@ -717,7 +717,7 @@ static void split_overlap(struct blame_entry *split, */ if (split[1].num_lines < 1) return; - split[1].suspect = origin_incref(parent); + split[1].suspect = blame_origin_incref(parent); } /* @@ -767,7 +767,7 @@ static void decref_split(struct blame_entry *split) int i; for (i = 0; i < 3; i++) - origin_decref(split[i].suspect); + blame_origin_decref(split[i].suspect); } /* @@ -830,10 +830,10 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, n->next = diffp; diffp = n; } else - origin_decref(e->suspect); + blame_origin_decref(e->suspect); /* Pass blame for everything before the differing * chunk to the parent */ - e->suspect = origin_incref(parent); + e->suspect = blame_origin_incref(parent); e->s_lno += offset; e->next = samep; samep = e; @@ -874,7 +874,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, */ int len = same - e->s_lno; struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); - n->suspect = origin_incref(e->suspect); + n->suspect = blame_origin_incref(e->suspect); n->lno = e->lno + len; n->s_lno = e->s_lno + len; n->num_lines = e->num_lines - len; @@ -1000,7 +1000,7 @@ static void copy_split_if_better(struct blame_scoreboard *sb, } for (i = 0; i < 3; i++) - origin_incref(this[i].suspect); + blame_origin_incref(this[i].suspect); decref_split(best_so_far); memcpy(best_so_far, this, sizeof(struct blame_entry [3])); } @@ -1280,7 +1280,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb, this); decref_split(this); } - origin_decref(norigin); + blame_origin_decref(norigin); } for (j = 0; j < num_ents; j++) { @@ -1321,8 +1321,8 @@ static void pass_whole_blame(struct blame_scoreboard *sb, suspects = origin->suspects; origin->suspects = NULL; for (e = suspects; e; e = e->next) { - origin_incref(porigin); - origin_decref(e->suspect); + blame_origin_incref(porigin); + blame_origin_decref(e->suspect); e->suspect = porigin; } queue_blames(sb, porigin, suspects); @@ -1418,7 +1418,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, continue; if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) { pass_whole_blame(sb, origin, porigin); - origin_decref(porigin); + blame_origin_decref(porigin); goto finish; } for (j = same = 0; j < i; j++) @@ -1430,7 +1430,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, if (!same) sg_origin[i] = porigin; else - origin_decref(porigin); + blame_origin_decref(porigin); } } @@ -1442,7 +1442,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, if (!porigin) continue; if (!origin->previous) { - origin_incref(porigin); + blame_origin_incref(porigin); origin->previous = porigin; } pass_blame_to_parent(sb, origin, porigin); @@ -1513,7 +1513,7 @@ finish: for (i = 0; i < num_sg; i++) { if (sg_origin[i]) { drop_origin_blob(sg_origin[i]); - origin_decref(sg_origin[i]); + blame_origin_decref(sg_origin[i]); } } drop_origin_blob(origin); @@ -1762,7 +1762,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) * We will use this suspect later in the loop, * so hold onto it in the meantime. */ - origin_incref(suspect); + blame_origin_incref(suspect); parse_commit(commit); if (reverse || (!(commit->object.flags & UNINTERESTING) && @@ -1794,7 +1794,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) break; } } - origin_decref(suspect); + blame_origin_decref(suspect); if (DEBUG) /* sanity */ sanity_check_refcnt(sb); @@ -2857,13 +2857,13 @@ parse_done: ent->suspect = o; ent->s_lno = bottom; ent->next = next; - origin_incref(o); + blame_origin_incref(o); } o->suspects = ent; prio_queue_put(&sb.commits, o->commit); - origin_decref(o); + blame_origin_decref(o); range_set_release(&ranges); string_list_clear(&range_list, 0); From c697136229290d5f5d6e6606916d4e77c90877dc Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:15 -0500 Subject: [PATCH 328/566] blame: rename coalesce function Functions that will be publicly exposed should have names that better reflect what they are a part of. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 7854770d85..7c493d2b99 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -384,7 +384,7 @@ static void sanity_check_refcnt(struct blame_scoreboard *); * contiguous lines in the same origin (i.e. pair), * merge them together. */ -static void coalesce(struct blame_scoreboard *sb) +static void blame_coalesce(struct blame_scoreboard *sb) { struct blame_entry *ent, *next; @@ -2885,7 +2885,7 @@ parse_done: sb.ent = blame_sort(sb.ent, compare_blame_final); - coalesce(&sb); + blame_coalesce(&sb); if (!(output_option & OUTPUT_PORCELAIN)) find_alignment(&sb, &output_option); From 1a31a2d98a29f85194b59ceca40eaa172fceebc0 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:16 -0500 Subject: [PATCH 329/566] blame: rename ent_score function Functions that will be publicly exposed should have names that better reflect what they are a part of. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 7c493d2b99..129ef28878 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -959,7 +959,7 @@ static void pass_blame_to_parent(struct blame_scoreboard *sb, * * Compute how trivial the lines in the blame_entry are. */ -static unsigned ent_score(struct blame_scoreboard *sb, struct blame_entry *e) +static unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e) { unsigned score; const char *cp, *ep; @@ -995,7 +995,7 @@ static void copy_split_if_better(struct blame_scoreboard *sb, if (!this[1].suspect) return; if (best_so_far[1].suspect) { - if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1])) + if (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1])) return; } @@ -1107,7 +1107,7 @@ static struct blame_entry **filter_small(struct blame_scoreboard *sb, struct blame_entry *p = *source; struct blame_entry *oldsmall = *small; while (p) { - if (ent_score(sb, p) <= score_min) { + if (blame_entry_score(sb, p) <= score_min) { *small = p; small = &p->next; p = *small; @@ -1156,7 +1156,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb, next = e->next; find_copy_in_blob(sb, e, parent, split, &file_p); if (split[1].suspect && - blame_move_score < ent_score(sb, &split[1])) { + blame_move_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, e); } else { e->next = leftover; @@ -1286,7 +1286,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb, for (j = 0; j < num_ents; j++) { struct blame_entry *split = blame_list[j].split; if (split[1].suspect && - blame_copy_score < ent_score(sb, &split[1])) { + blame_copy_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, blame_list[j].ent); } else { @@ -2104,8 +2104,8 @@ static void find_alignment(struct blame_scoreboard *sb, int *option) num = e->lno + e->num_lines; if (longest_dst_lines < num) longest_dst_lines = num; - if (largest_score < ent_score(sb, e)) - largest_score = ent_score(sb, e); + if (largest_score < blame_entry_score(sb, e)) + largest_score = blame_entry_score(sb, e); } max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); From 935202bdf427f6dafa7ba013af92e4b6374a1c1b Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:17 -0500 Subject: [PATCH 330/566] blame: rename nth_line function Functions that will be publicly exposed should have names that better reflect what they are a part of. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 129ef28878..5082543a54 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -653,14 +653,14 @@ static void dup_entry(struct blame_entry ***queue, *queue = &dst->next; } -static const char *nth_line(struct blame_scoreboard *sb, long lno) +static const char *blame_nth_line(struct blame_scoreboard *sb, long lno) { return sb->final_buf + sb->lineno[lno]; } static const char *nth_line_cb(void *data, long lno) { - return nth_line((struct blame_scoreboard *)data, lno); + return blame_nth_line((struct blame_scoreboard *)data, lno); } /* @@ -968,8 +968,8 @@ static unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entr return e->score; score = 1; - cp = nth_line(sb, e->lno); - ep = nth_line(sb, e->lno + e->num_lines); + cp = blame_nth_line(sb, e->lno); + ep = blame_nth_line(sb, e->lno + e->num_lines); while (cp < ep) { unsigned ch = *((unsigned char *)cp); if (isalnum(ch)) @@ -1078,9 +1078,9 @@ static void find_copy_in_blob(struct blame_scoreboard *sb, /* * Prepare mmfile that contains only the lines in ent. */ - cp = nth_line(sb, ent->lno); + cp = blame_nth_line(sb, ent->lno); file_o.ptr = (char *) cp; - file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp; + file_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp; /* * file_o is a part of final image we are annotating. @@ -1866,7 +1866,7 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, ent->num_lines); emit_porcelain_details(suspect, repeat); - cp = nth_line(sb, ent->lno); + cp = blame_nth_line(sb, ent->lno); for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; if (cnt) { @@ -1900,7 +1900,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int get_commit_info(suspect->commit, &ci, 1); oid_to_hex_r(hex, &suspect->commit->object.oid); - cp = nth_line(sb, ent->lno); + cp = blame_nth_line(sb, ent->lno); for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; From 8449528deb6826fac5e6a9a312b45b4522ef7ebb Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:18 -0500 Subject: [PATCH 331/566] blame: move stat counters to scoreboard Statistic counters are used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 5082543a54..f0c9ab8737 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -61,11 +61,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP; #define DEBUG 0 #endif -/* stats */ -static int num_read_blob; -static int num_get_patch; -static int num_commits; - #define PICKAXE_BLAME_MOVE 01 #define PICKAXE_BLAME_COPY 02 #define PICKAXE_BLAME_COPY_HARDER 04 @@ -151,13 +146,13 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, * diff machinery */ static void fill_origin_blob(struct diff_options *opt, - struct blame_origin *o, mmfile_t *file) + struct blame_origin *o, mmfile_t *file, int *num_read_blob) { if (!o->file.ptr) { enum object_type type; unsigned long file_size; - num_read_blob++; + (*num_read_blob)++; if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size)) ; @@ -375,6 +370,11 @@ struct blame_scoreboard { /* look-up a line in the final buffer */ int num_lines; int *lineno; + + /* stats */ + int num_read_blob; + int num_get_patch; + int num_commits; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -934,9 +934,9 @@ static void pass_blame_to_parent(struct blame_scoreboard *sb, d.offset = 0; d.dstq = &newdest; d.srcq = &target->suspects; - fill_origin_blob(&sb->revs->diffopt, parent, &file_p); - fill_origin_blob(&sb->revs->diffopt, target, &file_o); - num_get_patch++; + fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); + fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob); + sb->num_get_patch++; if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d)) die("unable to generate diff (%s -> %s)", @@ -1140,7 +1140,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb, if (!unblamed) return; /* nothing remains for this target */ - fill_origin_blob(&sb->revs->diffopt, parent, &file_p); + fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); if (!file_p.ptr) return; @@ -1269,7 +1269,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb, norigin = get_origin(parent, p->one->path); oidcpy(&norigin->blob_oid, &p->one->oid); norigin->mode = p->one->mode; - fill_origin_blob(&sb->revs->diffopt, norigin, &file_p); + fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob); if (!file_p.ptr) continue; @@ -1434,7 +1434,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, } } - num_commits++; + sb->num_commits++; for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { @@ -2818,7 +2818,7 @@ parse_done: oid_to_hex(&o->blob_oid), path); } - num_read_blob++; + sb.num_read_blob++; lno = prepare_lines(&sb); if (lno && !range_list.nr) @@ -2899,9 +2899,9 @@ parse_done: } if (show_stats) { - printf("num read blob: %d\n", num_read_blob); - printf("num get patch: %d\n", num_get_patch); - printf("num commits: %d\n", num_commits); + printf("num read blob: %d\n", sb.num_read_blob); + printf("num get patch: %d\n", sb.num_get_patch); + printf("num commits: %d\n", sb.num_commits); } return 0; } From 18ec0d62ee9819adde8afdc0fb33d37766109b02 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:19 -0500 Subject: [PATCH 332/566] blame: move copy/move thresholds to scoreboard Copy and move score thresholds are used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index f0c9ab8737..643f847efe 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -66,10 +66,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP; #define PICKAXE_BLAME_COPY_HARDER 04 #define PICKAXE_BLAME_COPY_HARDEST 010 -/* - * blame for a blame_entry with score lower than these thresholds - * is not passed to the parent using move/copy logic. - */ static unsigned blame_move_score; static unsigned blame_copy_score; #define BLAME_DEFAULT_MOVE_SCORE 20 @@ -375,6 +371,13 @@ struct blame_scoreboard { int num_read_blob; int num_get_patch; int num_commits; + + /* + * blame for a blame_entry with score lower than these thresholds + * is not passed to the parent using move/copy logic. + */ + unsigned move_score; + unsigned copy_score; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -1156,7 +1159,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb, next = e->next; find_copy_in_blob(sb, e, parent, split, &file_p); if (split[1].suspect && - blame_move_score < blame_entry_score(sb, &split[1])) { + sb->move_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, e); } else { e->next = leftover; @@ -1165,7 +1168,7 @@ static void find_move_in_parent(struct blame_scoreboard *sb, decref_split(split); } *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score); + toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); } @@ -1286,7 +1289,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb, for (j = 0; j < num_ents; j++) { struct blame_entry *split = blame_list[j].split; if (split[1].suspect && - blame_copy_score < blame_entry_score(sb, &split[1])) { + sb->copy_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, blame_list[j].ent); } else { @@ -1297,7 +1300,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb, } free(blame_list); *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score); + toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); diff_flush(&diff_opts); @@ -1454,7 +1457,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, * Optionally find moves in parents' files. */ if (opt & PICKAXE_BLAME_MOVE) { - filter_small(sb, &toosmall, &origin->suspects, blame_move_score); + filter_small(sb, &toosmall, &origin->suspects, sb->move_score); if (origin->suspects) { for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; @@ -1473,12 +1476,12 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, * Optionally find copies from parents' files. */ if (opt & PICKAXE_BLAME_COPY) { - if (blame_copy_score > blame_move_score) - filter_small(sb, &toosmall, &origin->suspects, blame_copy_score); - else if (blame_copy_score < blame_move_score) { + if (sb->copy_score > sb->move_score) + filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); + else if (sb->copy_score < sb->move_score) { origin->suspects = blame_merge(origin->suspects, toosmall); toosmall = NULL; - filter_small(sb, &toosmall, &origin->suspects, blame_copy_score); + filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); } if (!origin->suspects) goto finish; @@ -2675,11 +2678,6 @@ parse_done: opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE | PICKAXE_BLAME_COPY_HARDER); - if (!blame_move_score) - blame_move_score = BLAME_DEFAULT_MOVE_SCORE; - if (!blame_copy_score) - blame_copy_score = BLAME_DEFAULT_COPY_SCORE; - /* * We have collected options unknown to us in argv[1..unk] * which are to be passed to revision machinery if we are @@ -2733,6 +2731,8 @@ parse_done: revs.disable_stdin = 1; setup_revisions(argc, argv, &revs, NULL); memset(&sb, 0, sizeof(sb)); + sb.move_score = BLAME_DEFAULT_MOVE_SCORE; + sb.copy_score = BLAME_DEFAULT_COPY_SCORE; sb.revs = &revs; if (!reverse) { @@ -2871,6 +2871,11 @@ parse_done: sb.ent = NULL; sb.path = path; + if (blame_move_score) + sb.move_score = blame_move_score; + if (blame_copy_score) + sb.copy_score = blame_copy_score; + read_mailmap(&mailmap, NULL); assign_blame(&sb, opt); From 84be875e610d7f10f2bfe9646d4ea1ace043c486 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:20 -0500 Subject: [PATCH 333/566] blame: move contents_from to scoreboard The argument from --contents is used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin/blame.c b/builtin/blame.c index 643f847efe..0955fc125c 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -378,6 +378,9 @@ struct blame_scoreboard { */ unsigned move_score; unsigned copy_score; + + /* use this file's contents as the final image */ + const char *contents_from; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -2735,6 +2738,7 @@ parse_done: sb.copy_score = BLAME_DEFAULT_COPY_SCORE; sb.revs = &revs; + sb.contents_from = contents_from; if (!reverse) { final_commit_name = prepare_final(&sb); sb.commits.compare = compare_commits_by_commit_date; From f81d70e94010bd0a137249fcf16e15bd4e3d722a Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:21 -0500 Subject: [PATCH 334/566] blame: move reverse flag to scoreboard The reverse flag is used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 0955fc125c..161d15c173 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -381,6 +381,9 @@ struct blame_scoreboard { /* use this file's contents as the final image */ const char *contents_from; + + /* flags */ + int reverse; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -1339,7 +1342,8 @@ static void pass_whole_blame(struct blame_scoreboard *sb, * "parent" (and "porigin"), but what we mean is to find scapegoat to * exonerate ourselves. */ -static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit) +static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit, + int reverse) { if (!reverse) { if (revs->first_parent_only && @@ -1353,9 +1357,9 @@ static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit return lookup_decoration(&revs->children, &commit->object); } -static int num_scapegoats(struct rev_info *revs, struct commit *commit) +static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse) { - struct commit_list *l = first_scapegoat(revs, commit); + struct commit_list *l = first_scapegoat(revs, commit, reverse); return commit_list_count(l); } @@ -1393,7 +1397,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, struct blame_entry *toosmall = NULL; struct blame_entry *blames, **blametail = &blames; - num_sg = num_scapegoats(revs, commit); + num_sg = num_scapegoats(revs, commit, sb->reverse); if (!num_sg) goto finish; else if (num_sg < ARRAY_SIZE(sg_buf)) @@ -1409,7 +1413,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, struct blame_origin *(*find)(struct commit *, struct blame_origin *); find = pass ? find_rename : find_origin; - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct commit *p = sg->item; @@ -1441,7 +1445,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, } sb->num_commits++; - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct blame_origin *porigin = sg_origin[i]; @@ -1462,7 +1466,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, if (opt & PICKAXE_BLAME_MOVE) { filter_small(sb, &toosmall, &origin->suspects, sb->move_score); if (origin->suspects) { - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct blame_origin *porigin = sg_origin[i]; @@ -1489,7 +1493,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, if (!origin->suspects) goto finish; - for (i = 0, sg = first_scapegoat(revs, commit); + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); i < num_sg && sg; sg = sg->next, i++) { struct blame_origin *porigin = sg_origin[i]; @@ -1770,7 +1774,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) */ blame_origin_incref(suspect); parse_commit(commit); - if (reverse || + if (sb->reverse || (!(commit->object.flags & UNINTERESTING) && !(revs->max_age != -1 && commit->date < revs->max_age))) pass_blame(sb, suspect, opt); @@ -2739,6 +2743,7 @@ parse_done: sb.revs = &revs; sb.contents_from = contents_from; + sb.reverse = reverse; if (!reverse) { final_commit_name = prepare_final(&sb); sb.commits.compare = compare_commits_by_commit_date; From 2cf833743294f27d8f167043c84146455b96bdb7 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:22 -0500 Subject: [PATCH 335/566] blame: move show_root flag to scoreboard The show_root flag is used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin/blame.c b/builtin/blame.c index 161d15c173..fdd41b41be 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -384,6 +384,7 @@ struct blame_scoreboard { /* flags */ int reverse; + int show_root; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -1784,7 +1785,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) mark_parents_uninteresting(commit); } /* treat root commit as boundary */ - if (!commit->parents && !show_root) + if (!commit->parents && !sb->show_root) commit->object.flags |= UNINTERESTING; /* Take responsibility for the remaining entries */ @@ -2885,6 +2886,8 @@ parse_done: if (blame_copy_score) sb.copy_score = blame_copy_score; + sb.show_root = show_root; + read_mailmap(&mailmap, NULL); assign_blame(&sb, opt); From 73e1c299e55df4d6e577c3950fc6d53a8ec2cbef Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:23 -0500 Subject: [PATCH 336/566] blame: move xdl_opts flags to scoreboard The xdl_opts flags are used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index fdd41b41be..8e676fb933 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -125,7 +125,7 @@ struct progress_info { }; static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, - xdl_emit_hunk_consume_func_t hunk_func, void *cb_data) + xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts) { xpparam_t xpp = {0}; xdemitconf_t xecfg = {0}; @@ -385,6 +385,7 @@ struct blame_scoreboard { /* flags */ int reverse; int show_root; + int xdl_opts; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -948,7 +949,7 @@ static void pass_blame_to_parent(struct blame_scoreboard *sb, fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob); sb->num_get_patch++; - if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d)) + if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts)) die("unable to generate diff (%s -> %s)", oid_to_hex(&parent->commit->object.oid), oid_to_hex(&target->commit->object.oid)); @@ -1097,7 +1098,7 @@ static void find_copy_in_blob(struct blame_scoreboard *sb, * file_p partially may match that image. */ memset(split, 0, sizeof(struct blame_entry [3])); - if (diff_hunks(file_p, &file_o, handle_split_cb, &d)) + if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts)) die("unable to generate diff (%s)", oid_to_hex(&parent->commit->object.oid)); /* remainder, if any, all match the preimage */ @@ -2887,6 +2888,7 @@ parse_done: sb.copy_score = blame_copy_score; sb.show_root = show_root; + sb.xdl_opts = xdl_opts; read_mailmap(&mailmap, NULL); From 1f44129b21235b92d5261964253d965e2249d582 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:24 -0500 Subject: [PATCH 337/566] blame: move no_whole_file_rename flag to scoreboard The no_whole_file_rename flag is used in parts of blame that are being moved to libgit, and should be accessible via the scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/blame.c b/builtin/blame.c index 8e676fb933..90c643c7da 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -386,6 +386,7 @@ struct blame_scoreboard { int reverse; int show_root; int xdl_opts; + int no_whole_file_rename; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -1411,7 +1412,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, * The first pass looks for unrenamed path to optimize for * common cases, then we look for renames in the second pass. */ - for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { + for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) { struct blame_origin *(*find)(struct commit *, struct blame_origin *); find = pass ? find_rename : find_origin; @@ -2889,6 +2890,7 @@ parse_done: sb.show_root = show_root; sb.xdl_opts = xdl_opts; + sb.no_whole_file_rename = no_whole_file_rename; read_mailmap(&mailmap, NULL); From 4149c1860b10512cf32410c1f71311a23dc97f07 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:25 -0500 Subject: [PATCH 338/566] blame: make sanity_check use a callback in scoreboard Allow the interface user to decide how to handle a failed sanity check, whether that be to output with the current state or to do nothing. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 90c643c7da..1b53325ff3 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -387,6 +387,10 @@ struct blame_scoreboard { int show_root; int xdl_opts; int no_whole_file_rename; + int debug; + + /* callbacks */ + void(*on_sanity_fail)(struct blame_scoreboard *, int); }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -412,7 +416,7 @@ static void blame_coalesce(struct blame_scoreboard *sb) } } - if (DEBUG) /* sanity */ + if (sb->debug) /* sanity */ sanity_check_refcnt(sb); } @@ -1809,7 +1813,7 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) } blame_origin_decref(suspect); - if (DEBUG) /* sanity */ + if (sb->debug) /* sanity */ sanity_check_refcnt(sb); } @@ -2148,12 +2152,16 @@ static void sanity_check_refcnt(struct blame_scoreboard *sb) baa = 1; } } - if (baa) { - int opt = 0160; - find_alignment(sb, &opt); - output(sb, opt); - die("Baa %d!", baa); - } + if (baa) + sb->on_sanity_fail(sb, baa); +} + +static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa) +{ + int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME; + find_alignment(sb, &opt); + output(sb, opt); + die("Baa %d!", baa); } static unsigned parse_score(const char *arg) @@ -2888,6 +2896,9 @@ parse_done: if (blame_copy_score) sb.copy_score = blame_copy_score; + sb.debug = DEBUG; + sb.on_sanity_fail = &sanity_check_on_fail; + sb.show_root = show_root; sb.xdl_opts = xdl_opts; sb.no_whole_file_rename = no_whole_file_rename; From f23092f19e73f5d8c3480ef02104af627a90361f Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 22 May 2017 16:17:55 +0200 Subject: [PATCH 339/566] cache_ref_iterator_begin(): avoid priming unneeded directories When iterating over references, reference priming is used to make sure that loose references are read into the ref-cache before packed references, to avoid races. It used to be that the prefix passed to reference iterators almost always ended in `/`, for example `refs/heads/`. In that case, the priming code would read all loose references under `find_containing_dir("refs/heads/")`, which is "refs/heads/". That's just what we want. But now that `ref-filter` knows how to pass refname prefixes to `for_each_fullref_in()`, the prefix might come from user input; for example, git for-each-ref refs/heads Since the argument doesn't include a trailing slash, the reference iteration code would prime all of the loose references under `find_containing_dir("refs/heads")`, which is "refs/". Thus we would unnecessarily read tags, remote-tracking references, etc., when the user is only interested in branches. It is a bit awkward to get around this problem. We can't just append a slash to the argument, because we don't know ab initio whether an argument like `refs/tags/release` corresponds to a single tag or to a directory containing tags. Moreover, until now a `prefix_ref_iterator` was used to make the final decision about which references fall within the prefix (the `cache_ref_iterator` only did a rough cut). This is also inefficient, because the `prefix_ref_iterator` can't know, for example, that while you are in a subdirectory that is completely within the prefix, you don't have to do the prefix check. So: * Move the responsibility for doing the prefix check directly to `cache_ref_iterator`. This means that `cache_ref_iterator_begin()` never has to wrap its return value in a `prefix_ref_iterator`. * Teach `cache_ref_iterator_begin()` (and `prime_ref_dir()`) to be stricter about what they iterate over and what directories they prime. * Teach `cache_ref_iterator` to keep track of whether the current `cache_ref_iterator_level` is fully within the prefix. If so, skip the prefix checks entirely. The main benefit of these optimizations is for loose references, since packed references are always read all at once. Note that after this change, `prefix_ref_iterator` is only ever used for its trimming feature and not for its "prefix" feature. But I'm not ripping out the latter yet, because it might be useful for another patch series that I'm working on. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/ref-cache.c | 95 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/refs/ref-cache.c b/refs/ref-cache.c index ec97f3a38a..af2fcb2c12 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -312,11 +312,42 @@ static void sort_ref_dir(struct ref_dir *dir) dir->sorted = dir->nr = i; } +enum prefix_state { + /* All refs within the directory would match prefix: */ + PREFIX_CONTAINS_DIR, + + /* Some, but not all, refs within the directory might match prefix: */ + PREFIX_WITHIN_DIR, + + /* No refs within the directory could possibly match prefix: */ + PREFIX_EXCLUDES_DIR +}; + /* - * Load all of the refs from `dir` (recursively) into our in-memory - * cache. + * Return a `prefix_state` constant describing the relationship + * between the directory with the specified `dirname` and `prefix`. */ -static void prime_ref_dir(struct ref_dir *dir) +static enum prefix_state overlaps_prefix(const char *dirname, + const char *prefix) +{ + while (*prefix && *dirname == *prefix) { + dirname++; + prefix++; + } + if (!*prefix) + return PREFIX_CONTAINS_DIR; + else if (!*dirname) + return PREFIX_WITHIN_DIR; + else + return PREFIX_EXCLUDES_DIR; +} + +/* + * Load all of the refs from `dir` (recursively) that could possibly + * contain references matching `prefix` into our in-memory cache. If + * `prefix` is NULL, prime unconditionally. + */ +static void prime_ref_dir(struct ref_dir *dir, const char *prefix) { /* * The hard work of loading loose refs is done by get_ref_dir(), so we @@ -327,8 +358,29 @@ static void prime_ref_dir(struct ref_dir *dir) int i; for (i = 0; i < dir->nr; i++) { struct ref_entry *entry = dir->entries[i]; - if (entry->flag & REF_DIR) - prime_ref_dir(get_ref_dir(entry)); + if (!(entry->flag & REF_DIR)) { + /* Not a directory; no need to recurse. */ + } else if (!prefix) { + /* Recurse in any case: */ + prime_ref_dir(get_ref_dir(entry), NULL); + } else { + switch (overlaps_prefix(entry->name, prefix)) { + case PREFIX_CONTAINS_DIR: + /* + * Recurse, and from here down we + * don't have to check the prefix + * anymore: + */ + prime_ref_dir(get_ref_dir(entry), NULL); + break; + case PREFIX_WITHIN_DIR: + prime_ref_dir(get_ref_dir(entry), prefix); + break; + case PREFIX_EXCLUDES_DIR: + /* No need to prime this directory. */ + break; + } + } } } @@ -343,6 +395,8 @@ struct cache_ref_iterator_level { */ struct ref_dir *dir; + enum prefix_state prefix_state; + /* * The index of the current entry within dir (which might * itself be a directory). If index == -1, then the iteration @@ -369,6 +423,13 @@ struct cache_ref_iterator { /* The number of levels that have been allocated on the stack */ size_t levels_alloc; + /* + * Only include references with this prefix in the iteration. + * The prefix is matched textually, without regard for path + * component boundaries. + */ + const char *prefix; + /* * A stack of levels. levels[0] is the uppermost level that is * being iterated over in this iteration. (This is not @@ -390,6 +451,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) &iter->levels[iter->levels_nr - 1]; struct ref_dir *dir = level->dir; struct ref_entry *entry; + enum prefix_state entry_prefix_state; if (level->index == -1) sort_ref_dir(dir); @@ -404,6 +466,14 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) entry = dir->entries[level->index]; + if (level->prefix_state == PREFIX_WITHIN_DIR) { + entry_prefix_state = overlaps_prefix(entry->name, iter->prefix); + if (entry_prefix_state == PREFIX_EXCLUDES_DIR) + continue; + } else { + entry_prefix_state = level->prefix_state; + } + if (entry->flag & REF_DIR) { /* push down a level */ ALLOC_GROW(iter->levels, iter->levels_nr + 1, @@ -411,6 +481,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) level = &iter->levels[iter->levels_nr++]; level->dir = get_ref_dir(entry); + level->prefix_state = entry_prefix_state; level->index = -1; } else { iter->base.refname = entry->name; @@ -471,6 +542,7 @@ static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) struct cache_ref_iterator *iter = (struct cache_ref_iterator *)ref_iterator; + free((char *)iter->prefix); free(iter->levels); base_ref_iterator_free(ref_iterator); return ITER_DONE; @@ -496,10 +568,10 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, dir = find_containing_dir(dir, prefix, 0); if (!dir) /* There's nothing to iterate over. */ - return empty_ref_iterator_begin(); + return empty_ref_iterator_begin(); if (prime_dir) - prime_ref_dir(dir); + prime_ref_dir(dir, prefix); iter = xcalloc(1, sizeof(*iter)); ref_iterator = &iter->base; @@ -511,9 +583,12 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, level->index = -1; level->dir = dir; - if (prefix && *prefix) - ref_iterator = prefix_ref_iterator_begin(ref_iterator, - prefix, 0); + if (prefix && *prefix) { + iter->prefix = xstrdup(prefix); + level->prefix_state = PREFIX_WITHIN_DIR; + } else { + level->prefix_state = PREFIX_CONTAINS_DIR; + } return ref_iterator; } From 8c59921dbf99cd19f1311a4eb1213312fdb41905 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:26 -0500 Subject: [PATCH 340/566] blame: move progress updates to a scoreboard callback Allow the interface user to decide how to handle a progress update. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 1b53325ff3..d05907b86c 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -391,6 +391,9 @@ struct blame_scoreboard { /* callbacks */ void(*on_sanity_fail)(struct blame_scoreboard *, int); + void(*found_guilty_entry)(struct blame_entry *, void *); + + void *found_guilty_entry_data; }; static void sanity_check_refcnt(struct blame_scoreboard *); @@ -1729,9 +1732,10 @@ static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat) * The blame_entry is found to be guilty for the range. * Show it in incremental output. */ -static void found_guilty_entry(struct blame_entry *ent, - struct progress_info *pi) +static void found_guilty_entry(struct blame_entry *ent, void *data) { + struct progress_info *pi = (struct progress_info *)data; + if (incremental) { struct blame_origin *suspect = ent->suspect; @@ -1754,11 +1758,6 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) { struct rev_info *revs = sb->revs; struct commit *commit = prio_queue_get(&sb->commits); - struct progress_info pi = { NULL, 0 }; - - if (show_progress) - pi.progress = start_progress_delay(_("Blaming lines"), - sb->num_lines, 50, 1); while (commit) { struct blame_entry *ent; @@ -1800,7 +1799,8 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) suspect->guilty = 1; for (;;) { struct blame_entry *next = ent->next; - found_guilty_entry(ent, &pi); + if (sb->found_guilty_entry) + sb->found_guilty_entry(ent, sb->found_guilty_entry_data); if (next) { ent = next; continue; @@ -1816,8 +1816,6 @@ static void assign_blame(struct blame_scoreboard *sb, int opt) if (sb->debug) /* sanity */ sanity_check_refcnt(sb); } - - stop_progress(&pi.progress); } static const char *format_time(timestamp_t time, const char *tz_str, @@ -2550,6 +2548,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) char *final_commit_name = NULL; enum object_type type; struct commit *final_commit = NULL; + struct progress_info pi = { NULL, 0 }; struct string_list range_list = STRING_LIST_INIT_NODUP; int output_option = 0, opt = 0; @@ -2905,8 +2904,16 @@ parse_done: read_mailmap(&mailmap, NULL); + sb.found_guilty_entry = &found_guilty_entry; + sb.found_guilty_entry_data = π + if (show_progress) + pi.progress = start_progress_delay(_("Blaming lines"), + sb.num_lines, 50, 1); + assign_blame(&sb, opt); + stop_progress(&pi.progress); + if (!incremental) setup_pager(); From 78b06e66be7806e7fa092c96a47be95984e7aee6 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:27 -0500 Subject: [PATCH 341/566] blame: wrap blame_sort and compare_blame_final The new method's interface is marginally cleaner than blame_sort, and will avoid the need to expose the compare_blame_final method. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index d05907b86c..61fd5b477a 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -328,12 +328,6 @@ static int compare_blame_suspect(const void *p1, const void *p2) return s1->s_lno > s2->s_lno ? 1 : -1; } -static struct blame_entry *blame_sort(struct blame_entry *head, - int (*compare_fn)(const void *, const void *)) -{ - return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn); -} - static int compare_commits_by_reverse_commit_date(const void *a, const void *b, void *c) @@ -396,6 +390,12 @@ struct blame_scoreboard { void *found_guilty_entry_data; }; +static void blame_sort_final(struct blame_scoreboard *sb) +{ + sb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame, + compare_blame_final); +} + static void sanity_check_refcnt(struct blame_scoreboard *); /* @@ -1378,7 +1378,8 @@ static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reve */ static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed) { - blamed = blame_sort(blamed, compare_blame_suspect); + blamed = llist_mergesort(blamed, get_next_blame, set_next_blame, + compare_blame_suspect); while (blamed) { struct blame_origin *porigin = blamed->suspect; @@ -2922,7 +2923,7 @@ parse_done: if (incremental) return 0; - sb.ent = blame_sort(sb.ent, compare_blame_final); + blame_sort_final(&sb); blame_coalesce(&sb); From 835c49f7d16c5f0307e71da19618fcfe500c2430 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:28 -0500 Subject: [PATCH 342/566] blame: rework methods that determine 'final' commit Either prepare_initial or prepare_final is used to determine which commit is marked as 'final'. Call the underlying methods directly to make this more clear. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 61fd5b477a..e3435201f4 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2431,14 +2431,8 @@ static struct commit *find_single_final(struct rev_info *revs, return found; } -static char *prepare_final(struct blame_scoreboard *sb) -{ - const char *name; - sb->final = find_single_final(sb->revs, &name); - return xstrdup_or_null(name); -} - -static const char *dwim_reverse_initial(struct blame_scoreboard *sb) +static struct commit *dwim_reverse_initial(struct rev_info *revs, + const char **name_p) { /* * DWIM "git blame --reverse ONE -- PATH" as @@ -2449,11 +2443,11 @@ static const char *dwim_reverse_initial(struct blame_scoreboard *sb) struct commit *head_commit; unsigned char head_sha1[20]; - if (sb->revs->pending.nr != 1) + if (revs->pending.nr != 1) return NULL; /* Is that sole rev a committish? */ - obj = sb->revs->pending.objects[0].item; + obj = revs->pending.objects[0].item; obj = deref_tag(obj, NULL, 0); if (obj->type != OBJ_COMMIT) return NULL; @@ -2467,17 +2461,19 @@ static const char *dwim_reverse_initial(struct blame_scoreboard *sb) /* Turn "ONE" into "ONE..HEAD" then */ obj->flags |= UNINTERESTING; - add_pending_object(sb->revs, &head_commit->object, "HEAD"); + add_pending_object(revs, &head_commit->object, "HEAD"); - sb->final = (struct commit *)obj; - return sb->revs->pending.objects[0].name; + if (name_p) + *name_p = revs->pending.objects[0].name; + return (struct commit *)obj; } -static char *prepare_initial(struct blame_scoreboard *sb) +static struct commit *find_single_initial(struct rev_info *revs, + const char **name_p) { int i; const char *final_commit_name = NULL; - struct rev_info *revs = sb->revs; + struct commit *found = NULL; /* * There must be one and only one negative commit, and it must be @@ -2490,19 +2486,22 @@ static char *prepare_initial(struct blame_scoreboard *sb) obj = deref_tag(obj, NULL, 0); if (obj->type != OBJ_COMMIT) die("Non commit %s?", revs->pending.objects[i].name); - if (sb->final) + if (found) die("More than one commit to dig up from, %s and %s?", revs->pending.objects[i].name, final_commit_name); - sb->final = (struct commit *) obj; + found = (struct commit *) obj; final_commit_name = revs->pending.objects[i].name; } if (!final_commit_name) - final_commit_name = dwim_reverse_initial(sb); + found = dwim_reverse_initial(revs, &final_commit_name); if (!final_commit_name) die("No commit to dig up from?"); - return xstrdup(final_commit_name); + + if (name_p) + *name_p = final_commit_name; + return found; } static int blame_copy_callback(const struct option *option, const char *arg, int unset) @@ -2546,7 +2545,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) struct blame_origin *o; struct blame_entry *ent = NULL; long dashdash_pos, lno; - char *final_commit_name = NULL; + const char *final_commit_name = NULL; enum object_type type; struct commit *final_commit = NULL; struct progress_info pi = { NULL, 0 }; @@ -2755,14 +2754,15 @@ parse_done: sb.revs = &revs; sb.contents_from = contents_from; sb.reverse = reverse; + if (!reverse) { - final_commit_name = prepare_final(&sb); + sb.final = find_single_final(&revs, &final_commit_name); sb.commits.compare = compare_commits_by_commit_date; } else if (contents_from) die(_("--contents and --reverse do not blend well.")); else { - final_commit_name = prepare_initial(&sb); + sb.final = find_single_initial(&revs, &final_commit_name); sb.commits.compare = compare_commits_by_reverse_commit_date; if (revs.first_parent_only) revs.children.name = NULL; @@ -2917,10 +2917,7 @@ parse_done: if (!incremental) setup_pager(); - - free(final_commit_name); - - if (incremental) + else return 0; blame_sort_final(&sb); From 6e4c9b5bcf2c4c9e29513949ad8b465d7afe88f1 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:29 -0500 Subject: [PATCH 343/566] blame: create scoreboard init function Create function that initializes blame_scoreboard to default values. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index e3435201f4..f839571097 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2537,6 +2537,13 @@ static int blame_move_callback(const struct option *option, const char *arg, int return 0; } +void init_scoreboard(struct blame_scoreboard *sb) +{ + memset(sb, 0, sizeof(struct blame_scoreboard)); + sb->move_score = BLAME_DEFAULT_MOVE_SCORE; + sb->copy_score = BLAME_DEFAULT_COPY_SCORE; +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -2747,10 +2754,8 @@ parse_done: revs.disable_stdin = 1; setup_revisions(argc, argv, &revs, NULL); - memset(&sb, 0, sizeof(sb)); - sb.move_score = BLAME_DEFAULT_MOVE_SCORE; - sb.copy_score = BLAME_DEFAULT_COPY_SCORE; + init_scoreboard(&sb); sb.revs = &revs; sb.contents_from = contents_from; sb.reverse = reverse; From d0d0ef1f67c1549aa1eab55c9a4ec1c2c34fccaa Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:30 -0500 Subject: [PATCH 344/566] blame: create scoreboard setup function Create function that completes setting up blame_scoreboard structure. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 190 +++++++++++++++++++++++++----------------------- 1 file changed, 101 insertions(+), 89 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index f839571097..fd415519bc 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2544,6 +2544,105 @@ void init_scoreboard(struct blame_scoreboard *sb) sb->copy_score = BLAME_DEFAULT_COPY_SCORE; } +void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig) +{ + const char *final_commit_name = NULL; + struct blame_origin *o; + struct commit *final_commit = NULL; + enum object_type type; + + if (sb->reverse && sb->contents_from) + die(_("--contents and --reverse do not blend well.")); + + if (!sb->reverse) { + sb->final = find_single_final(sb->revs, &final_commit_name); + sb->commits.compare = compare_commits_by_commit_date; + } else { + sb->final = find_single_initial(sb->revs, &final_commit_name); + sb->commits.compare = compare_commits_by_reverse_commit_date; + } + + if (sb->final && sb->contents_from) + die(_("cannot use --contents with final commit object name")); + + if (sb->reverse && sb->revs->first_parent_only) + sb->revs->children.name = NULL; + + if (!sb->final) { + /* + * "--not A B -- path" without anything positive; + * do not default to HEAD, but use the working tree + * or "--contents". + */ + setup_work_tree(); + sb->final = fake_working_tree_commit(&sb->revs->diffopt, + path, sb->contents_from); + add_pending_object(sb->revs, &(sb->final->object), ":"); + } + + if (sb->reverse && sb->revs->first_parent_only) { + final_commit = find_single_final(sb->revs, NULL); + if (!final_commit) + die(_("--reverse and --first-parent together require specified latest commit")); + } + + /* + * If we have bottom, this will mark the ancestors of the + * bottom commits we would reach while traversing as + * uninteresting. + */ + if (prepare_revision_walk(sb->revs)) + die(_("revision walk setup failed")); + + if (sb->reverse && sb->revs->first_parent_only) { + struct commit *c = final_commit; + + sb->revs->children.name = "children"; + while (c->parents && + oidcmp(&c->object.oid, &sb->final->object.oid)) { + struct commit_list *l = xcalloc(1, sizeof(*l)); + + l->item = c; + if (add_decoration(&sb->revs->children, + &c->parents->item->object, l)) + die("BUG: not unique item in first-parent chain"); + c = c->parents->item; + } + + if (oidcmp(&c->object.oid, &sb->final->object.oid)) + die(_("--reverse --first-parent together require range along first-parent chain")); + } + + if (is_null_oid(&sb->final->object.oid)) { + o = sb->final->util; + sb->final_buf = xmemdupz(o->file.ptr, o->file.size); + sb->final_buf_size = o->file.size; + } + else { + o = get_origin(sb->final, path); + if (fill_blob_sha1_and_mode(o)) + die(_("no such path %s in %s"), path, final_commit_name); + + if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) && + textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf, + &sb->final_buf_size)) + ; + else + sb->final_buf = read_sha1_file(o->blob_oid.hash, &type, + &sb->final_buf_size); + + if (!sb->final_buf) + die(_("cannot read blob %s for path %s"), + oid_to_hex(&o->blob_oid), + path); + } + sb->num_read_blob++; + prepare_lines(sb); + + if (orig) + *orig = o; +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -2552,9 +2651,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) struct blame_origin *o; struct blame_entry *ent = NULL; long dashdash_pos, lno; - const char *final_commit_name = NULL; - enum object_type type; - struct commit *final_commit = NULL; struct progress_info pi = { NULL, 0 }; struct string_list range_list = STRING_LIST_INIT_NODUP; @@ -2759,92 +2855,8 @@ parse_done: sb.revs = &revs; sb.contents_from = contents_from; sb.reverse = reverse; - - if (!reverse) { - sb.final = find_single_final(&revs, &final_commit_name); - sb.commits.compare = compare_commits_by_commit_date; - } - else if (contents_from) - die(_("--contents and --reverse do not blend well.")); - else { - sb.final = find_single_initial(&revs, &final_commit_name); - sb.commits.compare = compare_commits_by_reverse_commit_date; - if (revs.first_parent_only) - revs.children.name = NULL; - } - - if (!sb.final) { - /* - * "--not A B -- path" without anything positive; - * do not default to HEAD, but use the working tree - * or "--contents". - */ - setup_work_tree(); - sb.final = fake_working_tree_commit(&sb.revs->diffopt, - path, contents_from); - add_pending_object(&revs, &(sb.final->object), ":"); - } - else if (contents_from) - die(_("cannot use --contents with final commit object name")); - - if (reverse && revs.first_parent_only) { - final_commit = find_single_final(sb.revs, NULL); - if (!final_commit) - die(_("--reverse and --first-parent together require specified latest commit")); - } - - /* - * If we have bottom, this will mark the ancestors of the - * bottom commits we would reach while traversing as - * uninteresting. - */ - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - - if (reverse && revs.first_parent_only) { - struct commit *c = final_commit; - - sb.revs->children.name = "children"; - while (c->parents && - oidcmp(&c->object.oid, &sb.final->object.oid)) { - struct commit_list *l = xcalloc(1, sizeof(*l)); - - l->item = c; - if (add_decoration(&sb.revs->children, - &c->parents->item->object, l)) - die("BUG: not unique item in first-parent chain"); - c = c->parents->item; - } - - if (oidcmp(&c->object.oid, &sb.final->object.oid)) - die(_("--reverse --first-parent together require range along first-parent chain")); - } - - if (is_null_oid(&sb.final->object.oid)) { - o = sb.final->util; - sb.final_buf = xmemdupz(o->file.ptr, o->file.size); - sb.final_buf_size = o->file.size; - } - else { - o = get_origin(sb.final, path); - if (fill_blob_sha1_and_mode(o)) - die(_("no such path %s in %s"), path, final_commit_name); - - if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) && - textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf, - &sb.final_buf_size)) - ; - else - sb.final_buf = read_sha1_file(o->blob_oid.hash, &type, - &sb.final_buf_size); - - if (!sb.final_buf) - die(_("cannot read blob %s for path %s"), - oid_to_hex(&o->blob_oid), - path); - } - sb.num_read_blob++; - lno = prepare_lines(&sb); + setup_scoreboard(&sb, path, &o); + lno = sb.num_lines; if (lno && !range_list.nr) string_list_append(&range_list, "1"); From e94f77f0e28788972c3c828bdcbe5d68d034849e Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:31 -0500 Subject: [PATCH 345/566] blame: create entry prepend function Create function that populates a blame_entry and prepends it to a list. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- builtin/blame.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index fd415519bc..29771b7112 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2643,6 +2643,20 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam *orig = o; } +struct blame_entry *blame_entry_prepend(struct blame_entry *head, + long start, long end, + struct blame_origin *o) +{ + struct blame_entry *new_head = xcalloc(1, sizeof(struct blame_entry)); + new_head->lno = start; + new_head->num_lines = end - start; + new_head->suspect = o; + new_head->s_lno = start; + new_head->next = head; + blame_origin_incref(o); + return new_head; +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -2885,16 +2899,7 @@ parse_done: for (range_i = ranges.nr; range_i > 0; --range_i) { const struct range *r = &ranges.ranges[range_i - 1]; - long bottom = r->start; - long top = r->end; - struct blame_entry *next = ent; - ent = xcalloc(1, sizeof(*ent)); - ent->lno = bottom; - ent->num_lines = top - bottom; - ent->suspect = o; - ent->s_lno = bottom; - ent->next = next; - blame_origin_incref(o); + ent = blame_entry_prepend(ent, r->start, r->end, o); } o->suspects = ent; From dc076ae5d9fe8c2fbf238b265c1bc1f6f089fd16 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:32 -0500 Subject: [PATCH 346/566] blame: move core structures to header The origin, entry, and scoreboard structures are core to the blame interface and need to be exposed for blame functionality. Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- blame.h | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ builtin/blame.c | 134 +-------------------------------------------- 2 files changed, 144 insertions(+), 133 deletions(-) create mode 100644 blame.h diff --git a/blame.h b/blame.h new file mode 100644 index 0000000000..c064d929b7 --- /dev/null +++ b/blame.h @@ -0,0 +1,143 @@ +#ifndef BLAME_H +#define BLAME_H + +#include "cache.h" +#include "commit.h" +#include "xdiff-interface.h" +#include "revision.h" +#include "prio-queue.h" + +/* + * One blob in a commit that is being suspected + */ +struct blame_origin { + int refcnt; + /* Record preceding blame record for this blob */ + struct blame_origin *previous; + /* origins are put in a list linked via `next' hanging off the + * corresponding commit's util field in order to make finding + * them fast. The presence in this chain does not count + * towards the origin's reference count. It is tempting to + * let it count as long as the commit is pending examination, + * but even under circumstances where the commit will be + * present multiple times in the priority queue of unexamined + * commits, processing the first instance will not leave any + * work requiring the origin data for the second instance. An + * interspersed commit changing that would have to be + * preexisting with a different ancestry and with the same + * commit date in order to wedge itself between two instances + * of the same commit in the priority queue _and_ produce + * blame entries relevant for it. While we don't want to let + * us get tripped up by this case, it certainly does not seem + * worth optimizing for. + */ + struct blame_origin *next; + struct commit *commit; + /* `suspects' contains blame entries that may be attributed to + * this origin's commit or to parent commits. When a commit + * is being processed, all suspects will be moved, either by + * assigning them to an origin in a different commit, or by + * shipping them to the scoreboard's ent list because they + * cannot be attributed to a different commit. + */ + struct blame_entry *suspects; + mmfile_t file; + struct object_id blob_oid; + unsigned mode; + /* guilty gets set when shipping any suspects to the final + * blame list instead of other commits + */ + char guilty; + char path[FLEX_ARRAY]; +}; + +/* + * Each group of lines is described by a blame_entry; it can be split + * as we pass blame to the parents. They are arranged in linked lists + * kept as `suspects' of some unprocessed origin, or entered (when the + * blame origin has been finalized) into the scoreboard structure. + * While the scoreboard structure is only sorted at the end of + * processing (according to final image line number), the lists + * attached to an origin are sorted by the target line number. + */ +struct blame_entry { + struct blame_entry *next; + + /* the first line of this group in the final image; + * internally all line numbers are 0 based. + */ + int lno; + + /* how many lines this group has */ + int num_lines; + + /* the commit that introduced this group into the final image */ + struct blame_origin *suspect; + + /* the line number of the first line of this group in the + * suspect's file; internally all line numbers are 0 based. + */ + int s_lno; + + /* how significant this entry is -- cached to avoid + * scanning the lines over and over. + */ + unsigned score; +}; + +/* + * The current state of the blame assignment. + */ +struct blame_scoreboard { + /* the final commit (i.e. where we started digging from) */ + struct commit *final; + /* Priority queue for commits with unassigned blame records */ + struct prio_queue commits; + struct rev_info *revs; + const char *path; + + /* + * The contents in the final image. + * Used by many functions to obtain contents of the nth line, + * indexed with scoreboard.lineno[blame_entry.lno]. + */ + const char *final_buf; + unsigned long final_buf_size; + + /* linked list of blames */ + struct blame_entry *ent; + + /* look-up a line in the final buffer */ + int num_lines; + int *lineno; + + /* stats */ + int num_read_blob; + int num_get_patch; + int num_commits; + + /* + * blame for a blame_entry with score lower than these thresholds + * is not passed to the parent using move/copy logic. + */ + unsigned move_score; + unsigned copy_score; + + /* use this file's contents as the final image */ + const char *contents_from; + + /* flags */ + int reverse; + int show_root; + int xdl_opts; + int no_whole_file_rename; + int debug; + + /* callbacks */ + void(*on_sanity_fail)(struct blame_scoreboard *, int); + void(*found_guilty_entry)(struct blame_entry *, void *); + + void *found_guilty_entry_data; +}; + +#endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 29771b7112..07b1a76833 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -28,6 +28,7 @@ #include "line-log.h" #include "dir.h" #include "progress.h" +#include "blame.h" static char blame_usage[] = N_("git blame [] [] [] [--] "); @@ -75,50 +76,6 @@ static unsigned blame_copy_score; #define METAINFO_SHOWN (1u<<12) #define MORE_THAN_ONE_PATH (1u<<13) -/* - * One blob in a commit that is being suspected - */ -struct blame_origin { - int refcnt; - /* Record preceding blame record for this blob */ - struct blame_origin *previous; - /* origins are put in a list linked via `next' hanging off the - * corresponding commit's util field in order to make finding - * them fast. The presence in this chain does not count - * towards the origin's reference count. It is tempting to - * let it count as long as the commit is pending examination, - * but even under circumstances where the commit will be - * present multiple times in the priority queue of unexamined - * commits, processing the first instance will not leave any - * work requiring the origin data for the second instance. An - * interspersed commit changing that would have to be - * preexisting with a different ancestry and with the same - * commit date in order to wedge itself between two instances - * of the same commit in the priority queue _and_ produce - * blame entries relevant for it. While we don't want to let - * us get tripped up by this case, it certainly does not seem - * worth optimizing for. - */ - struct blame_origin *next; - struct commit *commit; - /* `suspects' contains blame entries that may be attributed to - * this origin's commit or to parent commits. When a commit - * is being processed, all suspects will be moved, either by - * assigning them to an origin in a different commit, or by - * shipping them to the scoreboard's ent list because they - * cannot be attributed to a different commit. - */ - struct blame_entry *suspects; - mmfile_t file; - struct object_id blob_oid; - unsigned mode; - /* guilty gets set when shipping any suspects to the final - * blame list instead of other commits - */ - char guilty; - char path[FLEX_ARRAY]; -}; - struct progress_info { struct progress *progress; int blamed_lines; @@ -208,40 +165,6 @@ static void drop_origin_blob(struct blame_origin *o) } } -/* - * Each group of lines is described by a blame_entry; it can be split - * as we pass blame to the parents. They are arranged in linked lists - * kept as `suspects' of some unprocessed origin, or entered (when the - * blame origin has been finalized) into the scoreboard structure. - * While the scoreboard structure is only sorted at the end of - * processing (according to final image line number), the lists - * attached to an origin are sorted by the target line number. - */ -struct blame_entry { - struct blame_entry *next; - - /* the first line of this group in the final image; - * internally all line numbers are 0 based. - */ - int lno; - - /* how many lines this group has */ - int num_lines; - - /* the commit that introduced this group into the final image */ - struct blame_origin *suspect; - - /* the line number of the first line of this group in the - * suspect's file; internally all line numbers are 0 based. - */ - int s_lno; - - /* how significant this entry is -- cached to avoid - * scanning the lines over and over. - */ - unsigned score; -}; - /* * Any merge of blames happens on lists of blames that arrived via * different parents in a single suspect. In this case, we want to @@ -335,61 +258,6 @@ static int compare_commits_by_reverse_commit_date(const void *a, return -compare_commits_by_commit_date(a, b, c); } -/* - * The current state of the blame assignment. - */ -struct blame_scoreboard { - /* the final commit (i.e. where we started digging from) */ - struct commit *final; - /* Priority queue for commits with unassigned blame records */ - struct prio_queue commits; - struct rev_info *revs; - const char *path; - - /* - * The contents in the final image. - * Used by many functions to obtain contents of the nth line, - * indexed with scoreboard.lineno[blame_entry.lno]. - */ - const char *final_buf; - unsigned long final_buf_size; - - /* linked list of blames */ - struct blame_entry *ent; - - /* look-up a line in the final buffer */ - int num_lines; - int *lineno; - - /* stats */ - int num_read_blob; - int num_get_patch; - int num_commits; - - /* - * blame for a blame_entry with score lower than these thresholds - * is not passed to the parent using move/copy logic. - */ - unsigned move_score; - unsigned copy_score; - - /* use this file's contents as the final image */ - const char *contents_from; - - /* flags */ - int reverse; - int show_root; - int xdl_opts; - int no_whole_file_rename; - int debug; - - /* callbacks */ - void(*on_sanity_fail)(struct blame_scoreboard *, int); - void(*found_guilty_entry)(struct blame_entry *, void *); - - void *found_guilty_entry_data; -}; - static void blame_sort_final(struct blame_scoreboard *sb) { sb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame, From f5dd754c36f7d9cc7dd4c3f1a22e0e4a557b2021 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:33 -0500 Subject: [PATCH 347/566] blame: move origin-related methods to libgit Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- Makefile | 1 + blame.c | 62 ++++++++++++++++++++++++++++++++++++++++++ blame.h | 15 +++++++++++ builtin/blame.c | 72 ------------------------------------------------- 4 files changed, 78 insertions(+), 72 deletions(-) create mode 100644 blame.c diff --git a/Makefile b/Makefile index e35542e631..2d795ed866 100644 --- a/Makefile +++ b/Makefile @@ -718,6 +718,7 @@ LIB_OBJS += argv-array.o LIB_OBJS += attr.o LIB_OBJS += base85.o LIB_OBJS += bisect.o +LIB_OBJS += blame.o LIB_OBJS += blob.o LIB_OBJS += branch.o LIB_OBJS += bulk-checkin.o diff --git a/blame.c b/blame.c new file mode 100644 index 0000000000..4855d6d6e9 --- /dev/null +++ b/blame.c @@ -0,0 +1,62 @@ +#include "blame.h" + +void blame_origin_decref(struct blame_origin *o) +{ + if (o && --o->refcnt <= 0) { + struct blame_origin *p, *l = NULL; + if (o->previous) + blame_origin_decref(o->previous); + free(o->file.ptr); + /* Should be present exactly once in commit chain */ + for (p = o->commit->util; p; l = p, p = p->next) { + if (p == o) { + if (l) + l->next = p->next; + else + o->commit->util = p->next; + free(o); + return; + } + } + die("internal error in blame_origin_decref"); + } +} + +/* + * Given a commit and a path in it, create a new origin structure. + * The callers that add blame to the scoreboard should use + * get_origin() to obtain shared, refcounted copy instead of calling + * this function directly. + */ +struct blame_origin *make_origin(struct commit *commit, const char *path) +{ + struct blame_origin *o; + FLEX_ALLOC_STR(o, path, path); + o->commit = commit; + o->refcnt = 1; + o->next = commit->util; + commit->util = o; + return o; +} + +/* + * Locate an existing origin or create a new one. + * This moves the origin to front position in the commit util list. + */ +struct blame_origin *get_origin(struct commit *commit, const char *path) +{ + struct blame_origin *o, *l; + + for (o = commit->util, l = NULL; o; l = o, o = o->next) { + if (!strcmp(o->path, path)) { + /* bump to front */ + if (l) { + l->next = o->next; + o->next = commit->util; + commit->util = o; + } + return blame_origin_incref(o); + } + } + return make_origin(commit, path); +} diff --git a/blame.h b/blame.h index c064d929b7..49b685e0fd 100644 --- a/blame.h +++ b/blame.h @@ -140,4 +140,19 @@ struct blame_scoreboard { void *found_guilty_entry_data; }; +/* + * Origin is refcounted and usually we keep the blob contents to be + * reused. + */ +static inline struct blame_origin *blame_origin_incref(struct blame_origin *o) +{ + if (o) + o->refcnt++; + return o; +} +extern void blame_origin_decref(struct blame_origin *o); + +extern struct blame_origin *make_origin(struct commit *commit, const char *path); +extern struct blame_origin *get_origin(struct commit *commit, const char *path); + #endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 07b1a76833..2d6d834a6d 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -124,39 +124,6 @@ static void fill_origin_blob(struct diff_options *opt, *file = o->file; } -/* - * Origin is refcounted and usually we keep the blob contents to be - * reused. - */ -static inline struct blame_origin *blame_origin_incref(struct blame_origin *o) -{ - if (o) - o->refcnt++; - return o; -} - -static void blame_origin_decref(struct blame_origin *o) -{ - if (o && --o->refcnt <= 0) { - struct blame_origin *p, *l = NULL; - if (o->previous) - blame_origin_decref(o->previous); - free(o->file.ptr); - /* Should be present exactly once in commit chain */ - for (p = o->commit->util; p; l = p, p = p->next) { - if (p == o) { - if (l) - l->next = p->next; - else - o->commit->util = p->next; - free(o); - return; - } - } - die("internal error in blame_origin_decref"); - } -} - static void drop_origin_blob(struct blame_origin *o) { if (o->file.ptr) { @@ -315,45 +282,6 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig } } -/* - * Given a commit and a path in it, create a new origin structure. - * The callers that add blame to the scoreboard should use - * get_origin() to obtain shared, refcounted copy instead of calling - * this function directly. - */ -static struct blame_origin *make_origin(struct commit *commit, const char *path) -{ - struct blame_origin *o; - FLEX_ALLOC_STR(o, path, path); - o->commit = commit; - o->refcnt = 1; - o->next = commit->util; - commit->util = o; - return o; -} - -/* - * Locate an existing origin or create a new one. - * This moves the origin to front position in the commit util list. - */ -static struct blame_origin *get_origin(struct commit *commit, const char *path) -{ - struct blame_origin *o, *l; - - for (o = commit->util, l = NULL; o; l = o, o = o->next) { - if (!strcmp(o->path, path)) { - /* bump to front */ - if (l) { - l->next = o->next; - o->next = commit->util; - commit->util = o; - } - return blame_origin_incref(o); - } - } - return make_origin(commit, path); -} - /* * Fill the blob_sha1 field of an origin if it hasn't, so that later * call to fill_origin_blob() can use it to locate the data. blob_sha1 From 072bf4321fb484a8eabb315c031f47883336fba2 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:34 -0500 Subject: [PATCH 348/566] blame: move fake-commit-related methods to libgit Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- blame.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++- blame.h | 4 +- builtin/blame.c | 197 ---------------------------------------------- 3 files changed, 205 insertions(+), 199 deletions(-) diff --git a/blame.c b/blame.c index 4855d6d6e9..8c025bc03d 100644 --- a/blame.c +++ b/blame.c @@ -1,3 +1,6 @@ +#include "cache.h" +#include "refs.h" +#include "cache-tree.h" #include "blame.h" void blame_origin_decref(struct blame_origin *o) @@ -28,7 +31,7 @@ void blame_origin_decref(struct blame_origin *o) * get_origin() to obtain shared, refcounted copy instead of calling * this function directly. */ -struct blame_origin *make_origin(struct commit *commit, const char *path) +static struct blame_origin *make_origin(struct commit *commit, const char *path) { struct blame_origin *o; FLEX_ALLOC_STR(o, path, path); @@ -60,3 +63,201 @@ struct blame_origin *get_origin(struct commit *commit, const char *path) } return make_origin(commit, path); } + + + +static void verify_working_tree_path(struct commit *work_tree, const char *path) +{ + struct commit_list *parents; + int pos; + + for (parents = work_tree->parents; parents; parents = parents->next) { + const struct object_id *commit_oid = &parents->item->object.oid; + struct object_id blob_oid; + unsigned mode; + + if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) && + sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB) + return; + } + + pos = cache_name_pos(path, strlen(path)); + if (pos >= 0) + ; /* path is in the index */ + else if (-1 - pos < active_nr && + !strcmp(active_cache[-1 - pos]->name, path)) + ; /* path is in the index, unmerged */ + else + die("no such path '%s' in HEAD", path); +} + +static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid) +{ + struct commit *parent; + + parent = lookup_commit_reference(oid->hash); + if (!parent) + die("no such commit %s", oid_to_hex(oid)); + return &commit_list_insert(parent, tail)->next; +} + +static void append_merge_parents(struct commit_list **tail) +{ + int merge_head; + struct strbuf line = STRBUF_INIT; + + merge_head = open(git_path_merge_head(), O_RDONLY); + if (merge_head < 0) { + if (errno == ENOENT) + return; + die("cannot open '%s' for reading", git_path_merge_head()); + } + + while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) { + struct object_id oid; + if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid)) + die("unknown line in '%s': %s", git_path_merge_head(), line.buf); + tail = append_parent(tail, &oid); + } + close(merge_head); + strbuf_release(&line); +} + +/* + * This isn't as simple as passing sb->buf and sb->len, because we + * want to transfer ownership of the buffer to the commit (so we + * must use detach). + */ +static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb) +{ + size_t len; + void *buf = strbuf_detach(sb, &len); + set_commit_buffer(c, buf, len); +} + +/* + * Prepare a dummy commit that represents the work tree (or staged) item. + * Note that annotating work tree item never works in the reverse. + */ +struct commit *fake_working_tree_commit(struct diff_options *opt, + const char *path, + const char *contents_from) +{ + struct commit *commit; + struct blame_origin *origin; + struct commit_list **parent_tail, *parent; + struct object_id head_oid; + struct strbuf buf = STRBUF_INIT; + const char *ident; + time_t now; + int size, len; + struct cache_entry *ce; + unsigned mode; + struct strbuf msg = STRBUF_INIT; + + read_cache(); + time(&now); + commit = alloc_commit_node(); + commit->object.parsed = 1; + commit->date = now; + parent_tail = &commit->parents; + + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) + die("no such ref: HEAD"); + + parent_tail = append_parent(parent_tail, &head_oid); + append_merge_parents(parent_tail); + verify_working_tree_path(commit, path); + + origin = make_origin(commit, path); + + ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0); + strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n"); + for (parent = commit->parents; parent; parent = parent->next) + strbuf_addf(&msg, "parent %s\n", + oid_to_hex(&parent->item->object.oid)); + strbuf_addf(&msg, + "author %s\n" + "committer %s\n\n" + "Version of %s from %s\n", + ident, ident, path, + (!contents_from ? path : + (!strcmp(contents_from, "-") ? "standard input" : contents_from))); + set_commit_buffer_from_strbuf(commit, &msg); + + if (!contents_from || strcmp("-", contents_from)) { + struct stat st; + const char *read_from; + char *buf_ptr; + unsigned long buf_len; + + if (contents_from) { + if (stat(contents_from, &st) < 0) + die_errno("Cannot stat '%s'", contents_from); + read_from = contents_from; + } + else { + if (lstat(path, &st) < 0) + die_errno("Cannot lstat '%s'", path); + read_from = path; + } + mode = canon_mode(st.st_mode); + + switch (st.st_mode & S_IFMT) { + case S_IFREG: + if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && + textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len)) + strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1); + else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) + die_errno("cannot open or read '%s'", read_from); + break; + case S_IFLNK: + if (strbuf_readlink(&buf, read_from, st.st_size) < 0) + die_errno("cannot readlink '%s'", read_from); + break; + default: + die("unsupported file type %s", read_from); + } + } + else { + /* Reading from stdin */ + mode = 0; + if (strbuf_read(&buf, 0, 0) < 0) + die_errno("failed to read from stdin"); + } + convert_to_git(path, buf.buf, buf.len, &buf, 0); + origin->file.ptr = buf.buf; + origin->file.size = buf.len; + pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash); + + /* + * Read the current index, replace the path entry with + * origin->blob_sha1 without mucking with its mode or type + * bits; we are not going to write this index out -- we just + * want to run "diff-index --cached". + */ + discard_cache(); + read_cache(); + + len = strlen(path); + if (!mode) { + int pos = cache_name_pos(path, len); + if (0 <= pos) + mode = active_cache[pos]->ce_mode; + else + /* Let's not bother reading from HEAD tree */ + mode = S_IFREG | 0644; + } + size = cache_entry_size(len); + ce = xcalloc(1, size); + oidcpy(&ce->oid, &origin->blob_oid); + memcpy(ce->name, path, len); + ce->ce_flags = create_ce_flags(0); + ce->ce_namelen = len; + ce->ce_mode = create_ce_mode(mode); + add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); + + cache_tree_invalidate_path(&the_index, path); + + return commit; +} diff --git a/blame.h b/blame.h index 49b685e0fd..0d10e17c96 100644 --- a/blame.h +++ b/blame.h @@ -6,6 +6,7 @@ #include "xdiff-interface.h" #include "revision.h" #include "prio-queue.h" +#include "diff.h" /* * One blob in a commit that is being suspected @@ -152,7 +153,8 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o) } extern void blame_origin_decref(struct blame_origin *o); -extern struct blame_origin *make_origin(struct commit *commit, const char *path); extern struct blame_origin *get_origin(struct commit *commit, const char *path); +extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from); + #endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 2d6d834a6d..3679214543 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -16,7 +16,6 @@ #include "revision.h" #include "quote.h" #include "xdiff-interface.h" -#include "cache-tree.h" #include "string-list.h" #include "mailmap.h" #include "mergesort.h" @@ -2006,202 +2005,6 @@ static int git_blame_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static void verify_working_tree_path(struct commit *work_tree, const char *path) -{ - struct commit_list *parents; - int pos; - - for (parents = work_tree->parents; parents; parents = parents->next) { - const struct object_id *commit_oid = &parents->item->object.oid; - struct object_id blob_oid; - unsigned mode; - - if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) && - sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB) - return; - } - - pos = cache_name_pos(path, strlen(path)); - if (pos >= 0) - ; /* path is in the index */ - else if (-1 - pos < active_nr && - !strcmp(active_cache[-1 - pos]->name, path)) - ; /* path is in the index, unmerged */ - else - die("no such path '%s' in HEAD", path); -} - -static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid) -{ - struct commit *parent; - - parent = lookup_commit_reference(oid->hash); - if (!parent) - die("no such commit %s", oid_to_hex(oid)); - return &commit_list_insert(parent, tail)->next; -} - -static void append_merge_parents(struct commit_list **tail) -{ - int merge_head; - struct strbuf line = STRBUF_INIT; - - merge_head = open(git_path_merge_head(), O_RDONLY); - if (merge_head < 0) { - if (errno == ENOENT) - return; - die("cannot open '%s' for reading", git_path_merge_head()); - } - - while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) { - struct object_id oid; - if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid)) - die("unknown line in '%s': %s", git_path_merge_head(), line.buf); - tail = append_parent(tail, &oid); - } - close(merge_head); - strbuf_release(&line); -} - -/* - * This isn't as simple as passing sb->buf and sb->len, because we - * want to transfer ownership of the buffer to the commit (so we - * must use detach). - */ -static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb) -{ - size_t len; - void *buf = strbuf_detach(sb, &len); - set_commit_buffer(c, buf, len); -} - -/* - * Prepare a dummy commit that represents the work tree (or staged) item. - * Note that annotating work tree item never works in the reverse. - */ -static struct commit *fake_working_tree_commit(struct diff_options *opt, - const char *path, - const char *contents_from) -{ - struct commit *commit; - struct blame_origin *origin; - struct commit_list **parent_tail, *parent; - struct object_id head_oid; - struct strbuf buf = STRBUF_INIT; - const char *ident; - time_t now; - int size, len; - struct cache_entry *ce; - unsigned mode; - struct strbuf msg = STRBUF_INIT; - - read_cache(); - time(&now); - commit = alloc_commit_node(); - commit->object.parsed = 1; - commit->date = now; - parent_tail = &commit->parents; - - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) - die("no such ref: HEAD"); - - parent_tail = append_parent(parent_tail, &head_oid); - append_merge_parents(parent_tail); - verify_working_tree_path(commit, path); - - origin = make_origin(commit, path); - - ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0); - strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n"); - for (parent = commit->parents; parent; parent = parent->next) - strbuf_addf(&msg, "parent %s\n", - oid_to_hex(&parent->item->object.oid)); - strbuf_addf(&msg, - "author %s\n" - "committer %s\n\n" - "Version of %s from %s\n", - ident, ident, path, - (!contents_from ? path : - (!strcmp(contents_from, "-") ? "standard input" : contents_from))); - set_commit_buffer_from_strbuf(commit, &msg); - - if (!contents_from || strcmp("-", contents_from)) { - struct stat st; - const char *read_from; - char *buf_ptr; - unsigned long buf_len; - - if (contents_from) { - if (stat(contents_from, &st) < 0) - die_errno("Cannot stat '%s'", contents_from); - read_from = contents_from; - } - else { - if (lstat(path, &st) < 0) - die_errno("Cannot lstat '%s'", path); - read_from = path; - } - mode = canon_mode(st.st_mode); - - switch (st.st_mode & S_IFMT) { - case S_IFREG: - if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && - textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len)) - strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1); - else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) - die_errno("cannot open or read '%s'", read_from); - break; - case S_IFLNK: - if (strbuf_readlink(&buf, read_from, st.st_size) < 0) - die_errno("cannot readlink '%s'", read_from); - break; - default: - die("unsupported file type %s", read_from); - } - } - else { - /* Reading from stdin */ - mode = 0; - if (strbuf_read(&buf, 0, 0) < 0) - die_errno("failed to read from stdin"); - } - convert_to_git(path, buf.buf, buf.len, &buf, 0); - origin->file.ptr = buf.buf; - origin->file.size = buf.len; - pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash); - - /* - * Read the current index, replace the path entry with - * origin->blob_sha1 without mucking with its mode or type - * bits; we are not going to write this index out -- we just - * want to run "diff-index --cached". - */ - discard_cache(); - read_cache(); - - len = strlen(path); - if (!mode) { - int pos = cache_name_pos(path, len); - if (0 <= pos) - mode = active_cache[pos]->ce_mode; - else - /* Let's not bother reading from HEAD tree */ - mode = S_IFREG | 0644; - } - size = cache_entry_size(len); - ce = xcalloc(1, size); - oidcpy(&ce->oid, &origin->blob_oid); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(0); - ce->ce_namelen = len; - ce->ce_mode = create_ce_mode(mode); - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); - - cache_tree_invalidate_path(&the_index, path); - - return commit; -} - static struct commit *find_single_final(struct rev_info *revs, const char **name_p) { From b543bb1cdfc9b61b262a848f936c326f4b060a6c Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:35 -0500 Subject: [PATCH 349/566] blame: move scoreboard-related methods to libgit Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- blame.c | 1313 ++++++++++++++++++++++++++++++++++++++++++++++ blame.h | 11 + builtin/blame.c | 1318 ----------------------------------------------- 3 files changed, 1324 insertions(+), 1318 deletions(-) diff --git a/blame.c b/blame.c index 8c025bc03d..798e61b8c7 100644 --- a/blame.c +++ b/blame.c @@ -1,6 +1,9 @@ #include "cache.h" #include "refs.h" #include "cache-tree.h" +#include "mergesort.h" +#include "diff.h" +#include "diffcore.h" #include "blame.h" void blame_origin_decref(struct blame_origin *o) @@ -261,3 +264,1313 @@ struct commit *fake_working_tree_commit(struct diff_options *opt, return commit; } + + + +static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, + xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts) +{ + xpparam_t xpp = {0}; + xdemitconf_t xecfg = {0}; + xdemitcb_t ecb = {NULL}; + + xpp.flags = xdl_opts; + xecfg.hunk_func = hunk_func; + ecb.priv = cb_data; + return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb); +} + +/* + * Given an origin, prepare mmfile_t structure to be used by the + * diff machinery + */ +static void fill_origin_blob(struct diff_options *opt, + struct blame_origin *o, mmfile_t *file, int *num_read_blob) +{ + if (!o->file.ptr) { + enum object_type type; + unsigned long file_size; + + (*num_read_blob)++; + if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && + textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size)) + ; + else + file->ptr = read_sha1_file(o->blob_oid.hash, &type, + &file_size); + file->size = file_size; + + if (!file->ptr) + die("Cannot read blob %s for path %s", + oid_to_hex(&o->blob_oid), + o->path); + o->file = *file; + } + else + *file = o->file; +} + +static void drop_origin_blob(struct blame_origin *o) +{ + if (o->file.ptr) { + free(o->file.ptr); + o->file.ptr = NULL; + } +} + +/* + * Any merge of blames happens on lists of blames that arrived via + * different parents in a single suspect. In this case, we want to + * sort according to the suspect line numbers as opposed to the final + * image line numbers. The function body is somewhat longish because + * it avoids unnecessary writes. + */ + +static struct blame_entry *blame_merge(struct blame_entry *list1, + struct blame_entry *list2) +{ + struct blame_entry *p1 = list1, *p2 = list2, + **tail = &list1; + + if (!p1) + return p2; + if (!p2) + return p1; + + if (p1->s_lno <= p2->s_lno) { + do { + tail = &p1->next; + if ((p1 = *tail) == NULL) { + *tail = p2; + return list1; + } + } while (p1->s_lno <= p2->s_lno); + } + for (;;) { + *tail = p2; + do { + tail = &p2->next; + if ((p2 = *tail) == NULL) { + *tail = p1; + return list1; + } + } while (p1->s_lno > p2->s_lno); + *tail = p1; + do { + tail = &p1->next; + if ((p1 = *tail) == NULL) { + *tail = p2; + return list1; + } + } while (p1->s_lno <= p2->s_lno); + } +} + +static void *get_next_blame(const void *p) +{ + return ((struct blame_entry *)p)->next; +} + +static void set_next_blame(void *p1, void *p2) +{ + ((struct blame_entry *)p1)->next = p2; +} + +/* + * Final image line numbers are all different, so we don't need a + * three-way comparison here. + */ + +static int compare_blame_final(const void *p1, const void *p2) +{ + return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno + ? 1 : -1; +} + +static int compare_blame_suspect(const void *p1, const void *p2) +{ + const struct blame_entry *s1 = p1, *s2 = p2; + /* + * to allow for collating suspects, we sort according to the + * respective pointer value as the primary sorting criterion. + * The actual relation is pretty unimportant as long as it + * establishes a total order. Comparing as integers gives us + * that. + */ + if (s1->suspect != s2->suspect) + return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1; + if (s1->s_lno == s2->s_lno) + return 0; + return s1->s_lno > s2->s_lno ? 1 : -1; +} + +void blame_sort_final(struct blame_scoreboard *sb) +{ + sb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame, + compare_blame_final); +} + +/* + * For debugging -- origin is refcounted, and this asserts that + * we do not underflow. + */ +static void sanity_check_refcnt(struct blame_scoreboard *sb) +{ + int baa = 0; + struct blame_entry *ent; + + for (ent = sb->ent; ent; ent = ent->next) { + /* Nobody should have zero or negative refcnt */ + if (ent->suspect->refcnt <= 0) { + fprintf(stderr, "%s in %s has negative refcnt %d\n", + ent->suspect->path, + oid_to_hex(&ent->suspect->commit->object.oid), + ent->suspect->refcnt); + baa = 1; + } + } + if (baa) + sb->on_sanity_fail(sb, baa); +} + +/* + * If two blame entries that are next to each other came from + * contiguous lines in the same origin (i.e. pair), + * merge them together. + */ +void blame_coalesce(struct blame_scoreboard *sb) +{ + struct blame_entry *ent, *next; + + for (ent = sb->ent; ent && (next = ent->next); ent = next) { + if (ent->suspect == next->suspect && + ent->s_lno + ent->num_lines == next->s_lno) { + ent->num_lines += next->num_lines; + ent->next = next->next; + blame_origin_decref(next->suspect); + free(next); + ent->score = 0; + next = ent; /* again */ + } + } + + if (sb->debug) /* sanity */ + sanity_check_refcnt(sb); +} + +/* + * Merge the given sorted list of blames into a preexisting origin. + * If there were no previous blames to that commit, it is entered into + * the commit priority queue of the score board. + */ + +static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin, + struct blame_entry *sorted) +{ + if (porigin->suspects) + porigin->suspects = blame_merge(porigin->suspects, sorted); + else { + struct blame_origin *o; + for (o = porigin->commit->util; o; o = o->next) { + if (o->suspects) { + porigin->suspects = sorted; + return; + } + } + porigin->suspects = sorted; + prio_queue_put(&sb->commits, porigin->commit); + } +} + +/* + * We have an origin -- check if the same path exists in the + * parent and return an origin structure to represent it. + */ +static struct blame_origin *find_origin(struct commit *parent, + struct blame_origin *origin) +{ + struct blame_origin *porigin; + struct diff_options diff_opts; + const char *paths[2]; + + /* First check any existing origins */ + for (porigin = parent->util; porigin; porigin = porigin->next) + if (!strcmp(porigin->path, origin->path)) { + /* + * The same path between origin and its parent + * without renaming -- the most common case. + */ + return blame_origin_incref (porigin); + } + + /* See if the origin->path is different between parent + * and origin first. Most of the time they are the + * same and diff-tree is fairly efficient about this. + */ + diff_setup(&diff_opts); + DIFF_OPT_SET(&diff_opts, RECURSIVE); + diff_opts.detect_rename = 0; + diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; + paths[0] = origin->path; + paths[1] = NULL; + + parse_pathspec(&diff_opts.pathspec, + PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, + PATHSPEC_LITERAL_PATH, "", paths); + diff_setup_done(&diff_opts); + + if (is_null_oid(&origin->commit->object.oid)) + do_diff_cache(parent->tree->object.oid.hash, &diff_opts); + else + diff_tree_sha1(parent->tree->object.oid.hash, + origin->commit->tree->object.oid.hash, + "", &diff_opts); + diffcore_std(&diff_opts); + + if (!diff_queued_diff.nr) { + /* The path is the same as parent */ + porigin = get_origin(parent, origin->path); + oidcpy(&porigin->blob_oid, &origin->blob_oid); + porigin->mode = origin->mode; + } else { + /* + * Since origin->path is a pathspec, if the parent + * commit had it as a directory, we will see a whole + * bunch of deletion of files in the directory that we + * do not care about. + */ + int i; + struct diff_filepair *p = NULL; + for (i = 0; i < diff_queued_diff.nr; i++) { + const char *name; + p = diff_queued_diff.queue[i]; + name = p->one->path ? p->one->path : p->two->path; + if (!strcmp(name, origin->path)) + break; + } + if (!p) + die("internal error in blame::find_origin"); + switch (p->status) { + default: + die("internal error in blame::find_origin (%c)", + p->status); + case 'M': + porigin = get_origin(parent, origin->path); + oidcpy(&porigin->blob_oid, &p->one->oid); + porigin->mode = p->one->mode; + break; + case 'A': + case 'T': + /* Did not exist in parent, or type changed */ + break; + } + } + diff_flush(&diff_opts); + clear_pathspec(&diff_opts.pathspec); + return porigin; +} + +/* + * We have an origin -- find the path that corresponds to it in its + * parent and return an origin structure to represent it. + */ +static struct blame_origin *find_rename(struct commit *parent, + struct blame_origin *origin) +{ + struct blame_origin *porigin = NULL; + struct diff_options diff_opts; + int i; + + diff_setup(&diff_opts); + DIFF_OPT_SET(&diff_opts, RECURSIVE); + diff_opts.detect_rename = DIFF_DETECT_RENAME; + diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_opts.single_follow = origin->path; + diff_setup_done(&diff_opts); + + if (is_null_oid(&origin->commit->object.oid)) + do_diff_cache(parent->tree->object.oid.hash, &diff_opts); + else + diff_tree_sha1(parent->tree->object.oid.hash, + origin->commit->tree->object.oid.hash, + "", &diff_opts); + diffcore_std(&diff_opts); + + for (i = 0; i < diff_queued_diff.nr; i++) { + struct diff_filepair *p = diff_queued_diff.queue[i]; + if ((p->status == 'R' || p->status == 'C') && + !strcmp(p->two->path, origin->path)) { + porigin = get_origin(parent, p->one->path); + oidcpy(&porigin->blob_oid, &p->one->oid); + porigin->mode = p->one->mode; + break; + } + } + diff_flush(&diff_opts); + clear_pathspec(&diff_opts.pathspec); + return porigin; +} + +/* + * Append a new blame entry to a given output queue. + */ +static void add_blame_entry(struct blame_entry ***queue, + const struct blame_entry *src) +{ + struct blame_entry *e = xmalloc(sizeof(*e)); + memcpy(e, src, sizeof(*e)); + blame_origin_incref(e->suspect); + + e->next = **queue; + **queue = e; + *queue = &e->next; +} + +/* + * src typically is on-stack; we want to copy the information in it to + * a malloced blame_entry that gets added to the given queue. The + * origin of dst loses a refcnt. + */ +static void dup_entry(struct blame_entry ***queue, + struct blame_entry *dst, struct blame_entry *src) +{ + blame_origin_incref(src->suspect); + blame_origin_decref(dst->suspect); + memcpy(dst, src, sizeof(*src)); + dst->next = **queue; + **queue = dst; + *queue = &dst->next; +} + +const char *blame_nth_line(struct blame_scoreboard *sb, long lno) +{ + return sb->final_buf + sb->lineno[lno]; +} + +/* + * It is known that lines between tlno to same came from parent, and e + * has an overlap with that range. it also is known that parent's + * line plno corresponds to e's line tlno. + * + * <---- e -----> + * <------> + * <------------> + * <------------> + * <------------------> + * + * Split e into potentially three parts; before this chunk, the chunk + * to be blamed for the parent, and after that portion. + */ +static void split_overlap(struct blame_entry *split, + struct blame_entry *e, + int tlno, int plno, int same, + struct blame_origin *parent) +{ + int chunk_end_lno; + memset(split, 0, sizeof(struct blame_entry [3])); + + if (e->s_lno < tlno) { + /* there is a pre-chunk part not blamed on parent */ + split[0].suspect = blame_origin_incref(e->suspect); + split[0].lno = e->lno; + split[0].s_lno = e->s_lno; + split[0].num_lines = tlno - e->s_lno; + split[1].lno = e->lno + tlno - e->s_lno; + split[1].s_lno = plno; + } + else { + split[1].lno = e->lno; + split[1].s_lno = plno + (e->s_lno - tlno); + } + + if (same < e->s_lno + e->num_lines) { + /* there is a post-chunk part not blamed on parent */ + split[2].suspect = blame_origin_incref(e->suspect); + split[2].lno = e->lno + (same - e->s_lno); + split[2].s_lno = e->s_lno + (same - e->s_lno); + split[2].num_lines = e->s_lno + e->num_lines - same; + chunk_end_lno = split[2].lno; + } + else + chunk_end_lno = e->lno + e->num_lines; + split[1].num_lines = chunk_end_lno - split[1].lno; + + /* + * if it turns out there is nothing to blame the parent for, + * forget about the splitting. !split[1].suspect signals this. + */ + if (split[1].num_lines < 1) + return; + split[1].suspect = blame_origin_incref(parent); +} + +/* + * split_overlap() divided an existing blame e into up to three parts + * in split. Any assigned blame is moved to queue to + * reflect the split. + */ +static void split_blame(struct blame_entry ***blamed, + struct blame_entry ***unblamed, + struct blame_entry *split, + struct blame_entry *e) +{ + if (split[0].suspect && split[2].suspect) { + /* The first part (reuse storage for the existing entry e) */ + dup_entry(unblamed, e, &split[0]); + + /* The last part -- me */ + add_blame_entry(unblamed, &split[2]); + + /* ... and the middle part -- parent */ + add_blame_entry(blamed, &split[1]); + } + else if (!split[0].suspect && !split[2].suspect) + /* + * The parent covers the entire area; reuse storage for + * e and replace it with the parent. + */ + dup_entry(blamed, e, &split[1]); + else if (split[0].suspect) { + /* me and then parent */ + dup_entry(unblamed, e, &split[0]); + add_blame_entry(blamed, &split[1]); + } + else { + /* parent and then me */ + dup_entry(blamed, e, &split[1]); + add_blame_entry(unblamed, &split[2]); + } +} + +/* + * After splitting the blame, the origins used by the + * on-stack blame_entry should lose one refcnt each. + */ +static void decref_split(struct blame_entry *split) +{ + int i; + + for (i = 0; i < 3; i++) + blame_origin_decref(split[i].suspect); +} + +/* + * reverse_blame reverses the list given in head, appending tail. + * That allows us to build lists in reverse order, then reverse them + * afterwards. This can be faster than building the list in proper + * order right away. The reason is that building in proper order + * requires writing a link in the _previous_ element, while building + * in reverse order just requires placing the list head into the + * _current_ element. + */ + +static struct blame_entry *reverse_blame(struct blame_entry *head, + struct blame_entry *tail) +{ + while (head) { + struct blame_entry *next = head->next; + head->next = tail; + tail = head; + head = next; + } + return tail; +} + +/* + * Process one hunk from the patch between the current suspect for + * blame_entry e and its parent. This first blames any unfinished + * entries before the chunk (which is where target and parent start + * differing) on the parent, and then splits blame entries at the + * start and at the end of the difference region. Since use of -M and + * -C options may lead to overlapping/duplicate source line number + * ranges, all we can rely on from sorting/merging is the order of the + * first suspect line number. + */ +static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, + int tlno, int offset, int same, + struct blame_origin *parent) +{ + struct blame_entry *e = **srcq; + struct blame_entry *samep = NULL, *diffp = NULL; + + while (e && e->s_lno < tlno) { + struct blame_entry *next = e->next; + /* + * current record starts before differing portion. If + * it reaches into it, we need to split it up and + * examine the second part separately. + */ + if (e->s_lno + e->num_lines > tlno) { + /* Move second half to a new record */ + int len = tlno - e->s_lno; + struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); + n->suspect = e->suspect; + n->lno = e->lno + len; + n->s_lno = e->s_lno + len; + n->num_lines = e->num_lines - len; + e->num_lines = len; + e->score = 0; + /* Push new record to diffp */ + n->next = diffp; + diffp = n; + } else + blame_origin_decref(e->suspect); + /* Pass blame for everything before the differing + * chunk to the parent */ + e->suspect = blame_origin_incref(parent); + e->s_lno += offset; + e->next = samep; + samep = e; + e = next; + } + /* + * As we don't know how much of a common stretch after this + * diff will occur, the currently blamed parts are all that we + * can assign to the parent for now. + */ + + if (samep) { + **dstq = reverse_blame(samep, **dstq); + *dstq = &samep->next; + } + /* + * Prepend the split off portions: everything after e starts + * after the blameable portion. + */ + e = reverse_blame(diffp, e); + + /* + * Now retain records on the target while parts are different + * from the parent. + */ + samep = NULL; + diffp = NULL; + while (e && e->s_lno < same) { + struct blame_entry *next = e->next; + + /* + * If current record extends into sameness, need to split. + */ + if (e->s_lno + e->num_lines > same) { + /* + * Move second half to a new record to be + * processed by later chunks + */ + int len = same - e->s_lno; + struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); + n->suspect = blame_origin_incref(e->suspect); + n->lno = e->lno + len; + n->s_lno = e->s_lno + len; + n->num_lines = e->num_lines - len; + e->num_lines = len; + e->score = 0; + /* Push new record to samep */ + n->next = samep; + samep = n; + } + e->next = diffp; + diffp = e; + e = next; + } + **srcq = reverse_blame(diffp, reverse_blame(samep, e)); + /* Move across elements that are in the unblamable portion */ + if (diffp) + *srcq = &diffp->next; +} + +struct blame_chunk_cb_data { + struct blame_origin *parent; + long offset; + struct blame_entry **dstq; + struct blame_entry **srcq; +}; + +/* diff chunks are from parent to target */ +static int blame_chunk_cb(long start_a, long count_a, + long start_b, long count_b, void *data) +{ + struct blame_chunk_cb_data *d = data; + if (start_a - start_b != d->offset) + die("internal error in blame::blame_chunk_cb"); + blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b, + start_b + count_b, d->parent); + d->offset = start_a + count_a - (start_b + count_b); + return 0; +} + +/* + * We are looking at the origin 'target' and aiming to pass blame + * for the lines it is suspected to its parent. Run diff to find + * which lines came from parent and pass blame for them. + */ +static void pass_blame_to_parent(struct blame_scoreboard *sb, + struct blame_origin *target, + struct blame_origin *parent) +{ + mmfile_t file_p, file_o; + struct blame_chunk_cb_data d; + struct blame_entry *newdest = NULL; + + if (!target->suspects) + return; /* nothing remains for this target */ + + d.parent = parent; + d.offset = 0; + d.dstq = &newdest; d.srcq = &target->suspects; + + fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); + fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob); + sb->num_get_patch++; + + if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts)) + die("unable to generate diff (%s -> %s)", + oid_to_hex(&parent->commit->object.oid), + oid_to_hex(&target->commit->object.oid)); + /* The rest are the same as the parent */ + blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent); + *d.dstq = NULL; + queue_blames(sb, parent, newdest); + + return; +} + +/* + * The lines in blame_entry after splitting blames many times can become + * very small and trivial, and at some point it becomes pointless to + * blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any + * ordinary C program, and it is not worth to say it was copied from + * totally unrelated file in the parent. + * + * Compute how trivial the lines in the blame_entry are. + */ +unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e) +{ + unsigned score; + const char *cp, *ep; + + if (e->score) + return e->score; + + score = 1; + cp = blame_nth_line(sb, e->lno); + ep = blame_nth_line(sb, e->lno + e->num_lines); + while (cp < ep) { + unsigned ch = *((unsigned char *)cp); + if (isalnum(ch)) + score++; + cp++; + } + e->score = score; + return score; +} + +/* + * best_so_far[] and this[] are both a split of an existing blame_entry + * that passes blame to the parent. Maintain best_so_far the best split + * so far, by comparing this and best_so_far and copying this into + * bst_so_far as needed. + */ +static void copy_split_if_better(struct blame_scoreboard *sb, + struct blame_entry *best_so_far, + struct blame_entry *this) +{ + int i; + + if (!this[1].suspect) + return; + if (best_so_far[1].suspect) { + if (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1])) + return; + } + + for (i = 0; i < 3; i++) + blame_origin_incref(this[i].suspect); + decref_split(best_so_far); + memcpy(best_so_far, this, sizeof(struct blame_entry [3])); +} + +/* + * We are looking at a part of the final image represented by + * ent (tlno and same are offset by ent->s_lno). + * tlno is where we are looking at in the final image. + * up to (but not including) same match preimage. + * plno is where we are looking at in the preimage. + * + * <-------------- final image ----------------------> + * <------ent------> + * ^tlno ^same + * <---------preimage-----> + * ^plno + * + * All line numbers are 0-based. + */ +static void handle_split(struct blame_scoreboard *sb, + struct blame_entry *ent, + int tlno, int plno, int same, + struct blame_origin *parent, + struct blame_entry *split) +{ + if (ent->num_lines <= tlno) + return; + if (tlno < same) { + struct blame_entry this[3]; + tlno += ent->s_lno; + same += ent->s_lno; + split_overlap(this, ent, tlno, plno, same, parent); + copy_split_if_better(sb, split, this); + decref_split(this); + } +} + +struct handle_split_cb_data { + struct blame_scoreboard *sb; + struct blame_entry *ent; + struct blame_origin *parent; + struct blame_entry *split; + long plno; + long tlno; +}; + +static int handle_split_cb(long start_a, long count_a, + long start_b, long count_b, void *data) +{ + struct handle_split_cb_data *d = data; + handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent, + d->split); + d->plno = start_a + count_a; + d->tlno = start_b + count_b; + return 0; +} + +/* + * Find the lines from parent that are the same as ent so that + * we can pass blames to it. file_p has the blob contents for + * the parent. + */ +static void find_copy_in_blob(struct blame_scoreboard *sb, + struct blame_entry *ent, + struct blame_origin *parent, + struct blame_entry *split, + mmfile_t *file_p) +{ + const char *cp; + mmfile_t file_o; + struct handle_split_cb_data d; + + memset(&d, 0, sizeof(d)); + d.sb = sb; d.ent = ent; d.parent = parent; d.split = split; + /* + * Prepare mmfile that contains only the lines in ent. + */ + cp = blame_nth_line(sb, ent->lno); + file_o.ptr = (char *) cp; + file_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp; + + /* + * file_o is a part of final image we are annotating. + * file_p partially may match that image. + */ + memset(split, 0, sizeof(struct blame_entry [3])); + if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts)) + die("unable to generate diff (%s)", + oid_to_hex(&parent->commit->object.oid)); + /* remainder, if any, all match the preimage */ + handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split); +} + +/* Move all blame entries from list *source that have a score smaller + * than score_min to the front of list *small. + * Returns a pointer to the link pointing to the old head of the small list. + */ + +static struct blame_entry **filter_small(struct blame_scoreboard *sb, + struct blame_entry **small, + struct blame_entry **source, + unsigned score_min) +{ + struct blame_entry *p = *source; + struct blame_entry *oldsmall = *small; + while (p) { + if (blame_entry_score(sb, p) <= score_min) { + *small = p; + small = &p->next; + p = *small; + } else { + *source = p; + source = &p->next; + p = *source; + } + } + *small = oldsmall; + *source = NULL; + return small; +} + +/* + * See if lines currently target is suspected for can be attributed to + * parent. + */ +static void find_move_in_parent(struct blame_scoreboard *sb, + struct blame_entry ***blamed, + struct blame_entry **toosmall, + struct blame_origin *target, + struct blame_origin *parent) +{ + struct blame_entry *e, split[3]; + struct blame_entry *unblamed = target->suspects; + struct blame_entry *leftover = NULL; + mmfile_t file_p; + + if (!unblamed) + return; /* nothing remains for this target */ + + fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); + if (!file_p.ptr) + return; + + /* At each iteration, unblamed has a NULL-terminated list of + * entries that have not yet been tested for blame. leftover + * contains the reversed list of entries that have been tested + * without being assignable to the parent. + */ + do { + struct blame_entry **unblamedtail = &unblamed; + struct blame_entry *next; + for (e = unblamed; e; e = next) { + next = e->next; + find_copy_in_blob(sb, e, parent, split, &file_p); + if (split[1].suspect && + sb->move_score < blame_entry_score(sb, &split[1])) { + split_blame(blamed, &unblamedtail, split, e); + } else { + e->next = leftover; + leftover = e; + } + decref_split(split); + } + *unblamedtail = NULL; + toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score); + } while (unblamed); + target->suspects = reverse_blame(leftover, NULL); +} + +struct blame_list { + struct blame_entry *ent; + struct blame_entry split[3]; +}; + +/* + * Count the number of entries the target is suspected for, + * and prepare a list of entry and the best split. + */ +static struct blame_list *setup_blame_list(struct blame_entry *unblamed, + int *num_ents_p) +{ + struct blame_entry *e; + int num_ents, i; + struct blame_list *blame_list = NULL; + + for (e = unblamed, num_ents = 0; e; e = e->next) + num_ents++; + if (num_ents) { + blame_list = xcalloc(num_ents, sizeof(struct blame_list)); + for (e = unblamed, i = 0; e; e = e->next) + blame_list[i++].ent = e; + } + *num_ents_p = num_ents; + return blame_list; +} + +/* + * For lines target is suspected for, see if we can find code movement + * across file boundary from the parent commit. porigin is the path + * in the parent we already tried. + */ +static void find_copy_in_parent(struct blame_scoreboard *sb, + struct blame_entry ***blamed, + struct blame_entry **toosmall, + struct blame_origin *target, + struct commit *parent, + struct blame_origin *porigin, + int opt) +{ + struct diff_options diff_opts; + int i, j; + struct blame_list *blame_list; + int num_ents; + struct blame_entry *unblamed = target->suspects; + struct blame_entry *leftover = NULL; + + if (!unblamed) + return; /* nothing remains for this target */ + + diff_setup(&diff_opts); + DIFF_OPT_SET(&diff_opts, RECURSIVE); + diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; + + diff_setup_done(&diff_opts); + + /* Try "find copies harder" on new path if requested; + * we do not want to use diffcore_rename() actually to + * match things up; find_copies_harder is set only to + * force diff_tree_sha1() to feed all filepairs to diff_queue, + * and this code needs to be after diff_setup_done(), which + * usually makes find-copies-harder imply copy detection. + */ + if ((opt & PICKAXE_BLAME_COPY_HARDEST) + || ((opt & PICKAXE_BLAME_COPY_HARDER) + && (!porigin || strcmp(target->path, porigin->path)))) + DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); + + if (is_null_oid(&target->commit->object.oid)) + do_diff_cache(parent->tree->object.oid.hash, &diff_opts); + else + diff_tree_sha1(parent->tree->object.oid.hash, + target->commit->tree->object.oid.hash, + "", &diff_opts); + + if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER)) + diffcore_std(&diff_opts); + + do { + struct blame_entry **unblamedtail = &unblamed; + blame_list = setup_blame_list(unblamed, &num_ents); + + for (i = 0; i < diff_queued_diff.nr; i++) { + struct diff_filepair *p = diff_queued_diff.queue[i]; + struct blame_origin *norigin; + mmfile_t file_p; + struct blame_entry this[3]; + + if (!DIFF_FILE_VALID(p->one)) + continue; /* does not exist in parent */ + if (S_ISGITLINK(p->one->mode)) + continue; /* ignore git links */ + if (porigin && !strcmp(p->one->path, porigin->path)) + /* find_move already dealt with this path */ + continue; + + norigin = get_origin(parent, p->one->path); + oidcpy(&norigin->blob_oid, &p->one->oid); + norigin->mode = p->one->mode; + fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob); + if (!file_p.ptr) + continue; + + for (j = 0; j < num_ents; j++) { + find_copy_in_blob(sb, blame_list[j].ent, + norigin, this, &file_p); + copy_split_if_better(sb, blame_list[j].split, + this); + decref_split(this); + } + blame_origin_decref(norigin); + } + + for (j = 0; j < num_ents; j++) { + struct blame_entry *split = blame_list[j].split; + if (split[1].suspect && + sb->copy_score < blame_entry_score(sb, &split[1])) { + split_blame(blamed, &unblamedtail, split, + blame_list[j].ent); + } else { + blame_list[j].ent->next = leftover; + leftover = blame_list[j].ent; + } + decref_split(split); + } + free(blame_list); + *unblamedtail = NULL; + toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score); + } while (unblamed); + target->suspects = reverse_blame(leftover, NULL); + diff_flush(&diff_opts); + clear_pathspec(&diff_opts.pathspec); +} + +/* + * The blobs of origin and porigin exactly match, so everything + * origin is suspected for can be blamed on the parent. + */ +static void pass_whole_blame(struct blame_scoreboard *sb, + struct blame_origin *origin, struct blame_origin *porigin) +{ + struct blame_entry *e, *suspects; + + if (!porigin->file.ptr && origin->file.ptr) { + /* Steal its file */ + porigin->file = origin->file; + origin->file.ptr = NULL; + } + suspects = origin->suspects; + origin->suspects = NULL; + for (e = suspects; e; e = e->next) { + blame_origin_incref(porigin); + blame_origin_decref(e->suspect); + e->suspect = porigin; + } + queue_blames(sb, porigin, suspects); +} + +/* + * We pass blame from the current commit to its parents. We keep saying + * "parent" (and "porigin"), but what we mean is to find scapegoat to + * exonerate ourselves. + */ +static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit, + int reverse) +{ + if (!reverse) { + if (revs->first_parent_only && + commit->parents && + commit->parents->next) { + free_commit_list(commit->parents->next); + commit->parents->next = NULL; + } + return commit->parents; + } + return lookup_decoration(&revs->children, &commit->object); +} + +static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse) +{ + struct commit_list *l = first_scapegoat(revs, commit, reverse); + return commit_list_count(l); +} + +/* Distribute collected unsorted blames to the respected sorted lists + * in the various origins. + */ +static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed) +{ + blamed = llist_mergesort(blamed, get_next_blame, set_next_blame, + compare_blame_suspect); + while (blamed) + { + struct blame_origin *porigin = blamed->suspect; + struct blame_entry *suspects = NULL; + do { + struct blame_entry *next = blamed->next; + blamed->next = suspects; + suspects = blamed; + blamed = next; + } while (blamed && blamed->suspect == porigin); + suspects = reverse_blame(suspects, NULL); + queue_blames(sb, porigin, suspects); + } +} + +#define MAXSG 16 + +static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt) +{ + struct rev_info *revs = sb->revs; + int i, pass, num_sg; + struct commit *commit = origin->commit; + struct commit_list *sg; + struct blame_origin *sg_buf[MAXSG]; + struct blame_origin *porigin, **sg_origin = sg_buf; + struct blame_entry *toosmall = NULL; + struct blame_entry *blames, **blametail = &blames; + + num_sg = num_scapegoats(revs, commit, sb->reverse); + if (!num_sg) + goto finish; + else if (num_sg < ARRAY_SIZE(sg_buf)) + memset(sg_buf, 0, sizeof(sg_buf)); + else + sg_origin = xcalloc(num_sg, sizeof(*sg_origin)); + + /* + * The first pass looks for unrenamed path to optimize for + * common cases, then we look for renames in the second pass. + */ + for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) { + struct blame_origin *(*find)(struct commit *, struct blame_origin *); + find = pass ? find_rename : find_origin; + + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); + i < num_sg && sg; + sg = sg->next, i++) { + struct commit *p = sg->item; + int j, same; + + if (sg_origin[i]) + continue; + if (parse_commit(p)) + continue; + porigin = find(p, origin); + if (!porigin) + continue; + if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) { + pass_whole_blame(sb, origin, porigin); + blame_origin_decref(porigin); + goto finish; + } + for (j = same = 0; j < i; j++) + if (sg_origin[j] && + !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) { + same = 1; + break; + } + if (!same) + sg_origin[i] = porigin; + else + blame_origin_decref(porigin); + } + } + + sb->num_commits++; + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); + i < num_sg && sg; + sg = sg->next, i++) { + struct blame_origin *porigin = sg_origin[i]; + if (!porigin) + continue; + if (!origin->previous) { + blame_origin_incref(porigin); + origin->previous = porigin; + } + pass_blame_to_parent(sb, origin, porigin); + if (!origin->suspects) + goto finish; + } + + /* + * Optionally find moves in parents' files. + */ + if (opt & PICKAXE_BLAME_MOVE) { + filter_small(sb, &toosmall, &origin->suspects, sb->move_score); + if (origin->suspects) { + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); + i < num_sg && sg; + sg = sg->next, i++) { + struct blame_origin *porigin = sg_origin[i]; + if (!porigin) + continue; + find_move_in_parent(sb, &blametail, &toosmall, origin, porigin); + if (!origin->suspects) + break; + } + } + } + + /* + * Optionally find copies from parents' files. + */ + if (opt & PICKAXE_BLAME_COPY) { + if (sb->copy_score > sb->move_score) + filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); + else if (sb->copy_score < sb->move_score) { + origin->suspects = blame_merge(origin->suspects, toosmall); + toosmall = NULL; + filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); + } + if (!origin->suspects) + goto finish; + + for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); + i < num_sg && sg; + sg = sg->next, i++) { + struct blame_origin *porigin = sg_origin[i]; + find_copy_in_parent(sb, &blametail, &toosmall, + origin, sg->item, porigin, opt); + if (!origin->suspects) + goto finish; + } + } + +finish: + *blametail = NULL; + distribute_blame(sb, blames); + /* + * prepend toosmall to origin->suspects + * + * There is no point in sorting: this ends up on a big + * unsorted list in the caller anyway. + */ + if (toosmall) { + struct blame_entry **tail = &toosmall; + while (*tail) + tail = &(*tail)->next; + *tail = origin->suspects; + origin->suspects = toosmall; + } + for (i = 0; i < num_sg; i++) { + if (sg_origin[i]) { + drop_origin_blob(sg_origin[i]); + blame_origin_decref(sg_origin[i]); + } + } + drop_origin_blob(origin); + if (sg_buf != sg_origin) + free(sg_origin); +} + +/* + * The main loop -- while we have blobs with lines whose true origin + * is still unknown, pick one blob, and allow its lines to pass blames + * to its parents. */ +void assign_blame(struct blame_scoreboard *sb, int opt) +{ + struct rev_info *revs = sb->revs; + struct commit *commit = prio_queue_get(&sb->commits); + + while (commit) { + struct blame_entry *ent; + struct blame_origin *suspect = commit->util; + + /* find one suspect to break down */ + while (suspect && !suspect->suspects) + suspect = suspect->next; + + if (!suspect) { + commit = prio_queue_get(&sb->commits); + continue; + } + + assert(commit == suspect->commit); + + /* + * We will use this suspect later in the loop, + * so hold onto it in the meantime. + */ + blame_origin_incref(suspect); + parse_commit(commit); + if (sb->reverse || + (!(commit->object.flags & UNINTERESTING) && + !(revs->max_age != -1 && commit->date < revs->max_age))) + pass_blame(sb, suspect, opt); + else { + commit->object.flags |= UNINTERESTING; + if (commit->object.parsed) + mark_parents_uninteresting(commit); + } + /* treat root commit as boundary */ + if (!commit->parents && !sb->show_root) + commit->object.flags |= UNINTERESTING; + + /* Take responsibility for the remaining entries */ + ent = suspect->suspects; + if (ent) { + suspect->guilty = 1; + for (;;) { + struct blame_entry *next = ent->next; + if (sb->found_guilty_entry) + sb->found_guilty_entry(ent, sb->found_guilty_entry_data); + if (next) { + ent = next; + continue; + } + ent->next = sb->ent; + sb->ent = suspect->suspects; + suspect->suspects = NULL; + break; + } + } + blame_origin_decref(suspect); + + if (sb->debug) /* sanity */ + sanity_check_refcnt(sb); + } +} diff --git a/blame.h b/blame.h index 0d10e17c96..df824b2267 100644 --- a/blame.h +++ b/blame.h @@ -8,6 +8,11 @@ #include "prio-queue.h" #include "diff.h" +#define PICKAXE_BLAME_MOVE 01 +#define PICKAXE_BLAME_COPY 02 +#define PICKAXE_BLAME_COPY_HARDER 04 +#define PICKAXE_BLAME_COPY_HARDEST 010 + /* * One blob in a commit that is being suspected */ @@ -157,4 +162,10 @@ extern struct blame_origin *get_origin(struct commit *commit, const char *path); extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from); +extern void blame_coalesce(struct blame_scoreboard *sb); +extern void blame_sort_final(struct blame_scoreboard *sb); +extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e); +extern void assign_blame(struct blame_scoreboard *sb, int opt); +extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno); + #endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 3679214543..461506c3cb 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -12,13 +12,10 @@ #include "tag.h" #include "tree-walk.h" #include "diff.h" -#include "diffcore.h" #include "revision.h" #include "quote.h" -#include "xdiff-interface.h" #include "string-list.h" #include "mailmap.h" -#include "mergesort.h" #include "parse-options.h" #include "prio-queue.h" #include "utf8.h" @@ -61,11 +58,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP; #define DEBUG 0 #endif -#define PICKAXE_BLAME_MOVE 01 -#define PICKAXE_BLAME_COPY 02 -#define PICKAXE_BLAME_COPY_HARDER 04 -#define PICKAXE_BLAME_COPY_HARDEST 010 - static unsigned blame_move_score; static unsigned blame_copy_score; #define BLAME_DEFAULT_MOVE_SCORE 20 @@ -80,143 +72,6 @@ struct progress_info { int blamed_lines; }; -static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, - xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts) -{ - xpparam_t xpp = {0}; - xdemitconf_t xecfg = {0}; - xdemitcb_t ecb = {NULL}; - - xpp.flags = xdl_opts; - xecfg.hunk_func = hunk_func; - ecb.priv = cb_data; - return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb); -} - -/* - * Given an origin, prepare mmfile_t structure to be used by the - * diff machinery - */ -static void fill_origin_blob(struct diff_options *opt, - struct blame_origin *o, mmfile_t *file, int *num_read_blob) -{ - if (!o->file.ptr) { - enum object_type type; - unsigned long file_size; - - (*num_read_blob)++; - if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && - textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size)) - ; - else - file->ptr = read_sha1_file(o->blob_oid.hash, &type, - &file_size); - file->size = file_size; - - if (!file->ptr) - die("Cannot read blob %s for path %s", - oid_to_hex(&o->blob_oid), - o->path); - o->file = *file; - } - else - *file = o->file; -} - -static void drop_origin_blob(struct blame_origin *o) -{ - if (o->file.ptr) { - free(o->file.ptr); - o->file.ptr = NULL; - } -} - -/* - * Any merge of blames happens on lists of blames that arrived via - * different parents in a single suspect. In this case, we want to - * sort according to the suspect line numbers as opposed to the final - * image line numbers. The function body is somewhat longish because - * it avoids unnecessary writes. - */ - -static struct blame_entry *blame_merge(struct blame_entry *list1, - struct blame_entry *list2) -{ - struct blame_entry *p1 = list1, *p2 = list2, - **tail = &list1; - - if (!p1) - return p2; - if (!p2) - return p1; - - if (p1->s_lno <= p2->s_lno) { - do { - tail = &p1->next; - if ((p1 = *tail) == NULL) { - *tail = p2; - return list1; - } - } while (p1->s_lno <= p2->s_lno); - } - for (;;) { - *tail = p2; - do { - tail = &p2->next; - if ((p2 = *tail) == NULL) { - *tail = p1; - return list1; - } - } while (p1->s_lno > p2->s_lno); - *tail = p1; - do { - tail = &p1->next; - if ((p1 = *tail) == NULL) { - *tail = p2; - return list1; - } - } while (p1->s_lno <= p2->s_lno); - } -} - -static void *get_next_blame(const void *p) -{ - return ((struct blame_entry *)p)->next; -} - -static void set_next_blame(void *p1, void *p2) -{ - ((struct blame_entry *)p1)->next = p2; -} - -/* - * Final image line numbers are all different, so we don't need a - * three-way comparison here. - */ - -static int compare_blame_final(const void *p1, const void *p2) -{ - return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno - ? 1 : -1; -} - -static int compare_blame_suspect(const void *p1, const void *p2) -{ - const struct blame_entry *s1 = p1, *s2 = p2; - /* - * to allow for collating suspects, we sort according to the - * respective pointer value as the primary sorting criterion. - * The actual relation is pretty unimportant as long as it - * establishes a total order. Comparing as integers gives us - * that. - */ - if (s1->suspect != s2->suspect) - return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1; - if (s1->s_lno == s2->s_lno) - return 0; - return s1->s_lno > s2->s_lno ? 1 : -1; -} - static int compare_commits_by_reverse_commit_date(const void *a, const void *b, void *c) @@ -224,63 +79,6 @@ static int compare_commits_by_reverse_commit_date(const void *a, return -compare_commits_by_commit_date(a, b, c); } -static void blame_sort_final(struct blame_scoreboard *sb) -{ - sb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame, - compare_blame_final); -} - -static void sanity_check_refcnt(struct blame_scoreboard *); - -/* - * If two blame entries that are next to each other came from - * contiguous lines in the same origin (i.e. pair), - * merge them together. - */ -static void blame_coalesce(struct blame_scoreboard *sb) -{ - struct blame_entry *ent, *next; - - for (ent = sb->ent; ent && (next = ent->next); ent = next) { - if (ent->suspect == next->suspect && - ent->s_lno + ent->num_lines == next->s_lno) { - ent->num_lines += next->num_lines; - ent->next = next->next; - blame_origin_decref(next->suspect); - free(next); - ent->score = 0; - next = ent; /* again */ - } - } - - if (sb->debug) /* sanity */ - sanity_check_refcnt(sb); -} - -/* - * Merge the given sorted list of blames into a preexisting origin. - * If there were no previous blames to that commit, it is entered into - * the commit priority queue of the score board. - */ - -static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin, - struct blame_entry *sorted) -{ - if (porigin->suspects) - porigin->suspects = blame_merge(porigin->suspects, sorted); - else { - struct blame_origin *o; - for (o = porigin->commit->util; o; o = o->next) { - if (o->suspects) { - porigin->suspects = sorted; - return; - } - } - porigin->suspects = sorted; - prio_queue_put(&sb->commits, porigin->commit); - } -} - /* * Fill the blob_sha1 field of an origin if it hasn't, so that later * call to fill_origin_blob() can use it to locate the data. blob_sha1 @@ -307,1036 +105,11 @@ static int fill_blob_sha1_and_mode(struct blame_origin *origin) return -1; } -/* - * We have an origin -- check if the same path exists in the - * parent and return an origin structure to represent it. - */ -static struct blame_origin *find_origin(struct commit *parent, - struct blame_origin *origin) -{ - struct blame_origin *porigin; - struct diff_options diff_opts; - const char *paths[2]; - - /* First check any existing origins */ - for (porigin = parent->util; porigin; porigin = porigin->next) - if (!strcmp(porigin->path, origin->path)) { - /* - * The same path between origin and its parent - * without renaming -- the most common case. - */ - return blame_origin_incref (porigin); - } - - /* See if the origin->path is different between parent - * and origin first. Most of the time they are the - * same and diff-tree is fairly efficient about this. - */ - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.detect_rename = 0; - diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - paths[0] = origin->path; - paths[1] = NULL; - - parse_pathspec(&diff_opts.pathspec, - PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, - PATHSPEC_LITERAL_PATH, "", paths); - diff_setup_done(&diff_opts); - - if (is_null_oid(&origin->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); - else - diff_tree_sha1(parent->tree->object.oid.hash, - origin->commit->tree->object.oid.hash, - "", &diff_opts); - diffcore_std(&diff_opts); - - if (!diff_queued_diff.nr) { - /* The path is the same as parent */ - porigin = get_origin(parent, origin->path); - oidcpy(&porigin->blob_oid, &origin->blob_oid); - porigin->mode = origin->mode; - } else { - /* - * Since origin->path is a pathspec, if the parent - * commit had it as a directory, we will see a whole - * bunch of deletion of files in the directory that we - * do not care about. - */ - int i; - struct diff_filepair *p = NULL; - for (i = 0; i < diff_queued_diff.nr; i++) { - const char *name; - p = diff_queued_diff.queue[i]; - name = p->one->path ? p->one->path : p->two->path; - if (!strcmp(name, origin->path)) - break; - } - if (!p) - die("internal error in blame::find_origin"); - switch (p->status) { - default: - die("internal error in blame::find_origin (%c)", - p->status); - case 'M': - porigin = get_origin(parent, origin->path); - oidcpy(&porigin->blob_oid, &p->one->oid); - porigin->mode = p->one->mode; - break; - case 'A': - case 'T': - /* Did not exist in parent, or type changed */ - break; - } - } - diff_flush(&diff_opts); - clear_pathspec(&diff_opts.pathspec); - return porigin; -} - -/* - * We have an origin -- find the path that corresponds to it in its - * parent and return an origin structure to represent it. - */ -static struct blame_origin *find_rename(struct commit *parent, - struct blame_origin *origin) -{ - struct blame_origin *porigin = NULL; - struct diff_options diff_opts; - int i; - - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.detect_rename = DIFF_DETECT_RENAME; - diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_opts.single_follow = origin->path; - diff_setup_done(&diff_opts); - - if (is_null_oid(&origin->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); - else - diff_tree_sha1(parent->tree->object.oid.hash, - origin->commit->tree->object.oid.hash, - "", &diff_opts); - diffcore_std(&diff_opts); - - for (i = 0; i < diff_queued_diff.nr; i++) { - struct diff_filepair *p = diff_queued_diff.queue[i]; - if ((p->status == 'R' || p->status == 'C') && - !strcmp(p->two->path, origin->path)) { - porigin = get_origin(parent, p->one->path); - oidcpy(&porigin->blob_oid, &p->one->oid); - porigin->mode = p->one->mode; - break; - } - } - diff_flush(&diff_opts); - clear_pathspec(&diff_opts.pathspec); - return porigin; -} - -/* - * Append a new blame entry to a given output queue. - */ -static void add_blame_entry(struct blame_entry ***queue, - const struct blame_entry *src) -{ - struct blame_entry *e = xmalloc(sizeof(*e)); - memcpy(e, src, sizeof(*e)); - blame_origin_incref(e->suspect); - - e->next = **queue; - **queue = e; - *queue = &e->next; -} - -/* - * src typically is on-stack; we want to copy the information in it to - * a malloced blame_entry that gets added to the given queue. The - * origin of dst loses a refcnt. - */ -static void dup_entry(struct blame_entry ***queue, - struct blame_entry *dst, struct blame_entry *src) -{ - blame_origin_incref(src->suspect); - blame_origin_decref(dst->suspect); - memcpy(dst, src, sizeof(*src)); - dst->next = **queue; - **queue = dst; - *queue = &dst->next; -} - -static const char *blame_nth_line(struct blame_scoreboard *sb, long lno) -{ - return sb->final_buf + sb->lineno[lno]; -} - static const char *nth_line_cb(void *data, long lno) { return blame_nth_line((struct blame_scoreboard *)data, lno); } -/* - * It is known that lines between tlno to same came from parent, and e - * has an overlap with that range. it also is known that parent's - * line plno corresponds to e's line tlno. - * - * <---- e -----> - * <------> - * <------------> - * <------------> - * <------------------> - * - * Split e into potentially three parts; before this chunk, the chunk - * to be blamed for the parent, and after that portion. - */ -static void split_overlap(struct blame_entry *split, - struct blame_entry *e, - int tlno, int plno, int same, - struct blame_origin *parent) -{ - int chunk_end_lno; - memset(split, 0, sizeof(struct blame_entry [3])); - - if (e->s_lno < tlno) { - /* there is a pre-chunk part not blamed on parent */ - split[0].suspect = blame_origin_incref(e->suspect); - split[0].lno = e->lno; - split[0].s_lno = e->s_lno; - split[0].num_lines = tlno - e->s_lno; - split[1].lno = e->lno + tlno - e->s_lno; - split[1].s_lno = plno; - } - else { - split[1].lno = e->lno; - split[1].s_lno = plno + (e->s_lno - tlno); - } - - if (same < e->s_lno + e->num_lines) { - /* there is a post-chunk part not blamed on parent */ - split[2].suspect = blame_origin_incref(e->suspect); - split[2].lno = e->lno + (same - e->s_lno); - split[2].s_lno = e->s_lno + (same - e->s_lno); - split[2].num_lines = e->s_lno + e->num_lines - same; - chunk_end_lno = split[2].lno; - } - else - chunk_end_lno = e->lno + e->num_lines; - split[1].num_lines = chunk_end_lno - split[1].lno; - - /* - * if it turns out there is nothing to blame the parent for, - * forget about the splitting. !split[1].suspect signals this. - */ - if (split[1].num_lines < 1) - return; - split[1].suspect = blame_origin_incref(parent); -} - -/* - * split_overlap() divided an existing blame e into up to three parts - * in split. Any assigned blame is moved to queue to - * reflect the split. - */ -static void split_blame(struct blame_entry ***blamed, - struct blame_entry ***unblamed, - struct blame_entry *split, - struct blame_entry *e) -{ - if (split[0].suspect && split[2].suspect) { - /* The first part (reuse storage for the existing entry e) */ - dup_entry(unblamed, e, &split[0]); - - /* The last part -- me */ - add_blame_entry(unblamed, &split[2]); - - /* ... and the middle part -- parent */ - add_blame_entry(blamed, &split[1]); - } - else if (!split[0].suspect && !split[2].suspect) - /* - * The parent covers the entire area; reuse storage for - * e and replace it with the parent. - */ - dup_entry(blamed, e, &split[1]); - else if (split[0].suspect) { - /* me and then parent */ - dup_entry(unblamed, e, &split[0]); - add_blame_entry(blamed, &split[1]); - } - else { - /* parent and then me */ - dup_entry(blamed, e, &split[1]); - add_blame_entry(unblamed, &split[2]); - } -} - -/* - * After splitting the blame, the origins used by the - * on-stack blame_entry should lose one refcnt each. - */ -static void decref_split(struct blame_entry *split) -{ - int i; - - for (i = 0; i < 3; i++) - blame_origin_decref(split[i].suspect); -} - -/* - * reverse_blame reverses the list given in head, appending tail. - * That allows us to build lists in reverse order, then reverse them - * afterwards. This can be faster than building the list in proper - * order right away. The reason is that building in proper order - * requires writing a link in the _previous_ element, while building - * in reverse order just requires placing the list head into the - * _current_ element. - */ - -static struct blame_entry *reverse_blame(struct blame_entry *head, - struct blame_entry *tail) -{ - while (head) { - struct blame_entry *next = head->next; - head->next = tail; - tail = head; - head = next; - } - return tail; -} - -/* - * Process one hunk from the patch between the current suspect for - * blame_entry e and its parent. This first blames any unfinished - * entries before the chunk (which is where target and parent start - * differing) on the parent, and then splits blame entries at the - * start and at the end of the difference region. Since use of -M and - * -C options may lead to overlapping/duplicate source line number - * ranges, all we can rely on from sorting/merging is the order of the - * first suspect line number. - */ -static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, - int tlno, int offset, int same, - struct blame_origin *parent) -{ - struct blame_entry *e = **srcq; - struct blame_entry *samep = NULL, *diffp = NULL; - - while (e && e->s_lno < tlno) { - struct blame_entry *next = e->next; - /* - * current record starts before differing portion. If - * it reaches into it, we need to split it up and - * examine the second part separately. - */ - if (e->s_lno + e->num_lines > tlno) { - /* Move second half to a new record */ - int len = tlno - e->s_lno; - struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); - n->suspect = e->suspect; - n->lno = e->lno + len; - n->s_lno = e->s_lno + len; - n->num_lines = e->num_lines - len; - e->num_lines = len; - e->score = 0; - /* Push new record to diffp */ - n->next = diffp; - diffp = n; - } else - blame_origin_decref(e->suspect); - /* Pass blame for everything before the differing - * chunk to the parent */ - e->suspect = blame_origin_incref(parent); - e->s_lno += offset; - e->next = samep; - samep = e; - e = next; - } - /* - * As we don't know how much of a common stretch after this - * diff will occur, the currently blamed parts are all that we - * can assign to the parent for now. - */ - - if (samep) { - **dstq = reverse_blame(samep, **dstq); - *dstq = &samep->next; - } - /* - * Prepend the split off portions: everything after e starts - * after the blameable portion. - */ - e = reverse_blame(diffp, e); - - /* - * Now retain records on the target while parts are different - * from the parent. - */ - samep = NULL; - diffp = NULL; - while (e && e->s_lno < same) { - struct blame_entry *next = e->next; - - /* - * If current record extends into sameness, need to split. - */ - if (e->s_lno + e->num_lines > same) { - /* - * Move second half to a new record to be - * processed by later chunks - */ - int len = same - e->s_lno; - struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); - n->suspect = blame_origin_incref(e->suspect); - n->lno = e->lno + len; - n->s_lno = e->s_lno + len; - n->num_lines = e->num_lines - len; - e->num_lines = len; - e->score = 0; - /* Push new record to samep */ - n->next = samep; - samep = n; - } - e->next = diffp; - diffp = e; - e = next; - } - **srcq = reverse_blame(diffp, reverse_blame(samep, e)); - /* Move across elements that are in the unblamable portion */ - if (diffp) - *srcq = &diffp->next; -} - -struct blame_chunk_cb_data { - struct blame_origin *parent; - long offset; - struct blame_entry **dstq; - struct blame_entry **srcq; -}; - -/* diff chunks are from parent to target */ -static int blame_chunk_cb(long start_a, long count_a, - long start_b, long count_b, void *data) -{ - struct blame_chunk_cb_data *d = data; - if (start_a - start_b != d->offset) - die("internal error in blame::blame_chunk_cb"); - blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b, - start_b + count_b, d->parent); - d->offset = start_a + count_a - (start_b + count_b); - return 0; -} - -/* - * We are looking at the origin 'target' and aiming to pass blame - * for the lines it is suspected to its parent. Run diff to find - * which lines came from parent and pass blame for them. - */ -static void pass_blame_to_parent(struct blame_scoreboard *sb, - struct blame_origin *target, - struct blame_origin *parent) -{ - mmfile_t file_p, file_o; - struct blame_chunk_cb_data d; - struct blame_entry *newdest = NULL; - - if (!target->suspects) - return; /* nothing remains for this target */ - - d.parent = parent; - d.offset = 0; - d.dstq = &newdest; d.srcq = &target->suspects; - - fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); - fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob); - sb->num_get_patch++; - - if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts)) - die("unable to generate diff (%s -> %s)", - oid_to_hex(&parent->commit->object.oid), - oid_to_hex(&target->commit->object.oid)); - /* The rest are the same as the parent */ - blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent); - *d.dstq = NULL; - queue_blames(sb, parent, newdest); - - return; -} - -/* - * The lines in blame_entry after splitting blames many times can become - * very small and trivial, and at some point it becomes pointless to - * blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any - * ordinary C program, and it is not worth to say it was copied from - * totally unrelated file in the parent. - * - * Compute how trivial the lines in the blame_entry are. - */ -static unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e) -{ - unsigned score; - const char *cp, *ep; - - if (e->score) - return e->score; - - score = 1; - cp = blame_nth_line(sb, e->lno); - ep = blame_nth_line(sb, e->lno + e->num_lines); - while (cp < ep) { - unsigned ch = *((unsigned char *)cp); - if (isalnum(ch)) - score++; - cp++; - } - e->score = score; - return score; -} - -/* - * best_so_far[] and this[] are both a split of an existing blame_entry - * that passes blame to the parent. Maintain best_so_far the best split - * so far, by comparing this and best_so_far and copying this into - * bst_so_far as needed. - */ -static void copy_split_if_better(struct blame_scoreboard *sb, - struct blame_entry *best_so_far, - struct blame_entry *this) -{ - int i; - - if (!this[1].suspect) - return; - if (best_so_far[1].suspect) { - if (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1])) - return; - } - - for (i = 0; i < 3; i++) - blame_origin_incref(this[i].suspect); - decref_split(best_so_far); - memcpy(best_so_far, this, sizeof(struct blame_entry [3])); -} - -/* - * We are looking at a part of the final image represented by - * ent (tlno and same are offset by ent->s_lno). - * tlno is where we are looking at in the final image. - * up to (but not including) same match preimage. - * plno is where we are looking at in the preimage. - * - * <-------------- final image ----------------------> - * <------ent------> - * ^tlno ^same - * <---------preimage-----> - * ^plno - * - * All line numbers are 0-based. - */ -static void handle_split(struct blame_scoreboard *sb, - struct blame_entry *ent, - int tlno, int plno, int same, - struct blame_origin *parent, - struct blame_entry *split) -{ - if (ent->num_lines <= tlno) - return; - if (tlno < same) { - struct blame_entry this[3]; - tlno += ent->s_lno; - same += ent->s_lno; - split_overlap(this, ent, tlno, plno, same, parent); - copy_split_if_better(sb, split, this); - decref_split(this); - } -} - -struct handle_split_cb_data { - struct blame_scoreboard *sb; - struct blame_entry *ent; - struct blame_origin *parent; - struct blame_entry *split; - long plno; - long tlno; -}; - -static int handle_split_cb(long start_a, long count_a, - long start_b, long count_b, void *data) -{ - struct handle_split_cb_data *d = data; - handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent, - d->split); - d->plno = start_a + count_a; - d->tlno = start_b + count_b; - return 0; -} - -/* - * Find the lines from parent that are the same as ent so that - * we can pass blames to it. file_p has the blob contents for - * the parent. - */ -static void find_copy_in_blob(struct blame_scoreboard *sb, - struct blame_entry *ent, - struct blame_origin *parent, - struct blame_entry *split, - mmfile_t *file_p) -{ - const char *cp; - mmfile_t file_o; - struct handle_split_cb_data d; - - memset(&d, 0, sizeof(d)); - d.sb = sb; d.ent = ent; d.parent = parent; d.split = split; - /* - * Prepare mmfile that contains only the lines in ent. - */ - cp = blame_nth_line(sb, ent->lno); - file_o.ptr = (char *) cp; - file_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp; - - /* - * file_o is a part of final image we are annotating. - * file_p partially may match that image. - */ - memset(split, 0, sizeof(struct blame_entry [3])); - if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts)) - die("unable to generate diff (%s)", - oid_to_hex(&parent->commit->object.oid)); - /* remainder, if any, all match the preimage */ - handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split); -} - -/* Move all blame entries from list *source that have a score smaller - * than score_min to the front of list *small. - * Returns a pointer to the link pointing to the old head of the small list. - */ - -static struct blame_entry **filter_small(struct blame_scoreboard *sb, - struct blame_entry **small, - struct blame_entry **source, - unsigned score_min) -{ - struct blame_entry *p = *source; - struct blame_entry *oldsmall = *small; - while (p) { - if (blame_entry_score(sb, p) <= score_min) { - *small = p; - small = &p->next; - p = *small; - } else { - *source = p; - source = &p->next; - p = *source; - } - } - *small = oldsmall; - *source = NULL; - return small; -} - -/* - * See if lines currently target is suspected for can be attributed to - * parent. - */ -static void find_move_in_parent(struct blame_scoreboard *sb, - struct blame_entry ***blamed, - struct blame_entry **toosmall, - struct blame_origin *target, - struct blame_origin *parent) -{ - struct blame_entry *e, split[3]; - struct blame_entry *unblamed = target->suspects; - struct blame_entry *leftover = NULL; - mmfile_t file_p; - - if (!unblamed) - return; /* nothing remains for this target */ - - fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob); - if (!file_p.ptr) - return; - - /* At each iteration, unblamed has a NULL-terminated list of - * entries that have not yet been tested for blame. leftover - * contains the reversed list of entries that have been tested - * without being assignable to the parent. - */ - do { - struct blame_entry **unblamedtail = &unblamed; - struct blame_entry *next; - for (e = unblamed; e; e = next) { - next = e->next; - find_copy_in_blob(sb, e, parent, split, &file_p); - if (split[1].suspect && - sb->move_score < blame_entry_score(sb, &split[1])) { - split_blame(blamed, &unblamedtail, split, e); - } else { - e->next = leftover; - leftover = e; - } - decref_split(split); - } - *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score); - } while (unblamed); - target->suspects = reverse_blame(leftover, NULL); -} - -struct blame_list { - struct blame_entry *ent; - struct blame_entry split[3]; -}; - -/* - * Count the number of entries the target is suspected for, - * and prepare a list of entry and the best split. - */ -static struct blame_list *setup_blame_list(struct blame_entry *unblamed, - int *num_ents_p) -{ - struct blame_entry *e; - int num_ents, i; - struct blame_list *blame_list = NULL; - - for (e = unblamed, num_ents = 0; e; e = e->next) - num_ents++; - if (num_ents) { - blame_list = xcalloc(num_ents, sizeof(struct blame_list)); - for (e = unblamed, i = 0; e; e = e->next) - blame_list[i++].ent = e; - } - *num_ents_p = num_ents; - return blame_list; -} - -/* - * For lines target is suspected for, see if we can find code movement - * across file boundary from the parent commit. porigin is the path - * in the parent we already tried. - */ -static void find_copy_in_parent(struct blame_scoreboard *sb, - struct blame_entry ***blamed, - struct blame_entry **toosmall, - struct blame_origin *target, - struct commit *parent, - struct blame_origin *porigin, - int opt) -{ - struct diff_options diff_opts; - int i, j; - struct blame_list *blame_list; - int num_ents; - struct blame_entry *unblamed = target->suspects; - struct blame_entry *leftover = NULL; - - if (!unblamed) - return; /* nothing remains for this target */ - - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - - diff_setup_done(&diff_opts); - - /* Try "find copies harder" on new path if requested; - * we do not want to use diffcore_rename() actually to - * match things up; find_copies_harder is set only to - * force diff_tree_sha1() to feed all filepairs to diff_queue, - * and this code needs to be after diff_setup_done(), which - * usually makes find-copies-harder imply copy detection. - */ - if ((opt & PICKAXE_BLAME_COPY_HARDEST) - || ((opt & PICKAXE_BLAME_COPY_HARDER) - && (!porigin || strcmp(target->path, porigin->path)))) - DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); - - if (is_null_oid(&target->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); - else - diff_tree_sha1(parent->tree->object.oid.hash, - target->commit->tree->object.oid.hash, - "", &diff_opts); - - if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER)) - diffcore_std(&diff_opts); - - do { - struct blame_entry **unblamedtail = &unblamed; - blame_list = setup_blame_list(unblamed, &num_ents); - - for (i = 0; i < diff_queued_diff.nr; i++) { - struct diff_filepair *p = diff_queued_diff.queue[i]; - struct blame_origin *norigin; - mmfile_t file_p; - struct blame_entry this[3]; - - if (!DIFF_FILE_VALID(p->one)) - continue; /* does not exist in parent */ - if (S_ISGITLINK(p->one->mode)) - continue; /* ignore git links */ - if (porigin && !strcmp(p->one->path, porigin->path)) - /* find_move already dealt with this path */ - continue; - - norigin = get_origin(parent, p->one->path); - oidcpy(&norigin->blob_oid, &p->one->oid); - norigin->mode = p->one->mode; - fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob); - if (!file_p.ptr) - continue; - - for (j = 0; j < num_ents; j++) { - find_copy_in_blob(sb, blame_list[j].ent, - norigin, this, &file_p); - copy_split_if_better(sb, blame_list[j].split, - this); - decref_split(this); - } - blame_origin_decref(norigin); - } - - for (j = 0; j < num_ents; j++) { - struct blame_entry *split = blame_list[j].split; - if (split[1].suspect && - sb->copy_score < blame_entry_score(sb, &split[1])) { - split_blame(blamed, &unblamedtail, split, - blame_list[j].ent); - } else { - blame_list[j].ent->next = leftover; - leftover = blame_list[j].ent; - } - decref_split(split); - } - free(blame_list); - *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score); - } while (unblamed); - target->suspects = reverse_blame(leftover, NULL); - diff_flush(&diff_opts); - clear_pathspec(&diff_opts.pathspec); -} - -/* - * The blobs of origin and porigin exactly match, so everything - * origin is suspected for can be blamed on the parent. - */ -static void pass_whole_blame(struct blame_scoreboard *sb, - struct blame_origin *origin, struct blame_origin *porigin) -{ - struct blame_entry *e, *suspects; - - if (!porigin->file.ptr && origin->file.ptr) { - /* Steal its file */ - porigin->file = origin->file; - origin->file.ptr = NULL; - } - suspects = origin->suspects; - origin->suspects = NULL; - for (e = suspects; e; e = e->next) { - blame_origin_incref(porigin); - blame_origin_decref(e->suspect); - e->suspect = porigin; - } - queue_blames(sb, porigin, suspects); -} - -/* - * We pass blame from the current commit to its parents. We keep saying - * "parent" (and "porigin"), but what we mean is to find scapegoat to - * exonerate ourselves. - */ -static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit, - int reverse) -{ - if (!reverse) { - if (revs->first_parent_only && - commit->parents && - commit->parents->next) { - free_commit_list(commit->parents->next); - commit->parents->next = NULL; - } - return commit->parents; - } - return lookup_decoration(&revs->children, &commit->object); -} - -static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse) -{ - struct commit_list *l = first_scapegoat(revs, commit, reverse); - return commit_list_count(l); -} - -/* Distribute collected unsorted blames to the respected sorted lists - * in the various origins. - */ -static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed) -{ - blamed = llist_mergesort(blamed, get_next_blame, set_next_blame, - compare_blame_suspect); - while (blamed) - { - struct blame_origin *porigin = blamed->suspect; - struct blame_entry *suspects = NULL; - do { - struct blame_entry *next = blamed->next; - blamed->next = suspects; - suspects = blamed; - blamed = next; - } while (blamed && blamed->suspect == porigin); - suspects = reverse_blame(suspects, NULL); - queue_blames(sb, porigin, suspects); - } -} - -#define MAXSG 16 - -static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt) -{ - struct rev_info *revs = sb->revs; - int i, pass, num_sg; - struct commit *commit = origin->commit; - struct commit_list *sg; - struct blame_origin *sg_buf[MAXSG]; - struct blame_origin *porigin, **sg_origin = sg_buf; - struct blame_entry *toosmall = NULL; - struct blame_entry *blames, **blametail = &blames; - - num_sg = num_scapegoats(revs, commit, sb->reverse); - if (!num_sg) - goto finish; - else if (num_sg < ARRAY_SIZE(sg_buf)) - memset(sg_buf, 0, sizeof(sg_buf)); - else - sg_origin = xcalloc(num_sg, sizeof(*sg_origin)); - - /* - * The first pass looks for unrenamed path to optimize for - * common cases, then we look for renames in the second pass. - */ - for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) { - struct blame_origin *(*find)(struct commit *, struct blame_origin *); - find = pass ? find_rename : find_origin; - - for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); - i < num_sg && sg; - sg = sg->next, i++) { - struct commit *p = sg->item; - int j, same; - - if (sg_origin[i]) - continue; - if (parse_commit(p)) - continue; - porigin = find(p, origin); - if (!porigin) - continue; - if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) { - pass_whole_blame(sb, origin, porigin); - blame_origin_decref(porigin); - goto finish; - } - for (j = same = 0; j < i; j++) - if (sg_origin[j] && - !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) { - same = 1; - break; - } - if (!same) - sg_origin[i] = porigin; - else - blame_origin_decref(porigin); - } - } - - sb->num_commits++; - for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); - i < num_sg && sg; - sg = sg->next, i++) { - struct blame_origin *porigin = sg_origin[i]; - if (!porigin) - continue; - if (!origin->previous) { - blame_origin_incref(porigin); - origin->previous = porigin; - } - pass_blame_to_parent(sb, origin, porigin); - if (!origin->suspects) - goto finish; - } - - /* - * Optionally find moves in parents' files. - */ - if (opt & PICKAXE_BLAME_MOVE) { - filter_small(sb, &toosmall, &origin->suspects, sb->move_score); - if (origin->suspects) { - for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); - i < num_sg && sg; - sg = sg->next, i++) { - struct blame_origin *porigin = sg_origin[i]; - if (!porigin) - continue; - find_move_in_parent(sb, &blametail, &toosmall, origin, porigin); - if (!origin->suspects) - break; - } - } - } - - /* - * Optionally find copies from parents' files. - */ - if (opt & PICKAXE_BLAME_COPY) { - if (sb->copy_score > sb->move_score) - filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); - else if (sb->copy_score < sb->move_score) { - origin->suspects = blame_merge(origin->suspects, toosmall); - toosmall = NULL; - filter_small(sb, &toosmall, &origin->suspects, sb->copy_score); - } - if (!origin->suspects) - goto finish; - - for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse); - i < num_sg && sg; - sg = sg->next, i++) { - struct blame_origin *porigin = sg_origin[i]; - find_copy_in_parent(sb, &blametail, &toosmall, - origin, sg->item, porigin, opt); - if (!origin->suspects) - goto finish; - } - } - -finish: - *blametail = NULL; - distribute_blame(sb, blames); - /* - * prepend toosmall to origin->suspects - * - * There is no point in sorting: this ends up on a big - * unsorted list in the caller anyway. - */ - if (toosmall) { - struct blame_entry **tail = &toosmall; - while (*tail) - tail = &(*tail)->next; - *tail = origin->suspects; - origin->suspects = toosmall; - } - for (i = 0; i < num_sg; i++) { - if (sg_origin[i]) { - drop_origin_blob(sg_origin[i]); - blame_origin_decref(sg_origin[i]); - } - } - drop_origin_blob(origin); - if (sg_buf != sg_origin) - free(sg_origin); -} - /* * Information on commits, used for output. */ @@ -1546,74 +319,6 @@ static void found_guilty_entry(struct blame_entry *ent, void *data) display_progress(pi->progress, pi->blamed_lines); } -/* - * The main loop -- while we have blobs with lines whose true origin - * is still unknown, pick one blob, and allow its lines to pass blames - * to its parents. */ -static void assign_blame(struct blame_scoreboard *sb, int opt) -{ - struct rev_info *revs = sb->revs; - struct commit *commit = prio_queue_get(&sb->commits); - - while (commit) { - struct blame_entry *ent; - struct blame_origin *suspect = commit->util; - - /* find one suspect to break down */ - while (suspect && !suspect->suspects) - suspect = suspect->next; - - if (!suspect) { - commit = prio_queue_get(&sb->commits); - continue; - } - - assert(commit == suspect->commit); - - /* - * We will use this suspect later in the loop, - * so hold onto it in the meantime. - */ - blame_origin_incref(suspect); - parse_commit(commit); - if (sb->reverse || - (!(commit->object.flags & UNINTERESTING) && - !(revs->max_age != -1 && commit->date < revs->max_age))) - pass_blame(sb, suspect, opt); - else { - commit->object.flags |= UNINTERESTING; - if (commit->object.parsed) - mark_parents_uninteresting(commit); - } - /* treat root commit as boundary */ - if (!commit->parents && !sb->show_root) - commit->object.flags |= UNINTERESTING; - - /* Take responsibility for the remaining entries */ - ent = suspect->suspects; - if (ent) { - suspect->guilty = 1; - for (;;) { - struct blame_entry *next = ent->next; - if (sb->found_guilty_entry) - sb->found_guilty_entry(ent, sb->found_guilty_entry_data); - if (next) { - ent = next; - continue; - } - ent->next = sb->ent; - sb->ent = suspect->suspects; - suspect->suspects = NULL; - break; - } - } - blame_origin_decref(suspect); - - if (sb->debug) /* sanity */ - sanity_check_refcnt(sb); - } -} - static const char *format_time(timestamp_t time, const char *tz_str, int show_raw_time) { @@ -1927,29 +632,6 @@ static void find_alignment(struct blame_scoreboard *sb, int *option) abbrev = auto_abbrev + 1; } -/* - * For debugging -- origin is refcounted, and this asserts that - * we do not underflow. - */ -static void sanity_check_refcnt(struct blame_scoreboard *sb) -{ - int baa = 0; - struct blame_entry *ent; - - for (ent = sb->ent; ent; ent = ent->next) { - /* Nobody should have zero or negative refcnt */ - if (ent->suspect->refcnt <= 0) { - fprintf(stderr, "%s in %s has negative refcnt %d\n", - ent->suspect->path, - oid_to_hex(&ent->suspect->commit->object.oid), - ent->suspect->refcnt); - baa = 1; - } - } - if (baa) - sb->on_sanity_fail(sb, baa); -} - static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa) { int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME; From 09002f1b31482dfbd0f471cd3614d542ea7a77a4 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:36 -0500 Subject: [PATCH 350/566] blame: move scoreboard setup to libgit Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- blame.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++- blame.h | 10 +- builtin/blame.c | 276 ----------------------------------------------- 3 files changed, 281 insertions(+), 284 deletions(-) diff --git a/blame.c b/blame.c index 798e61b8c7..f6c9cb7df9 100644 --- a/blame.c +++ b/blame.c @@ -4,6 +4,7 @@ #include "mergesort.h" #include "diff.h" #include "diffcore.h" +#include "tag.h" #include "blame.h" void blame_origin_decref(struct blame_origin *o) @@ -49,7 +50,7 @@ static struct blame_origin *make_origin(struct commit *commit, const char *path) * Locate an existing origin or create a new one. * This moves the origin to front position in the commit util list. */ -struct blame_origin *get_origin(struct commit *commit, const char *path) +static struct blame_origin *get_origin(struct commit *commit, const char *path) { struct blame_origin *o, *l; @@ -142,9 +143,9 @@ static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb) * Prepare a dummy commit that represents the work tree (or staged) item. * Note that annotating work tree item never works in the reverse. */ -struct commit *fake_working_tree_commit(struct diff_options *opt, - const char *path, - const char *contents_from) +static struct commit *fake_working_tree_commit(struct diff_options *opt, + const char *path, + const char *contents_from) { struct commit *commit; struct blame_origin *origin; @@ -410,6 +411,13 @@ void blame_sort_final(struct blame_scoreboard *sb) compare_blame_final); } +static int compare_commits_by_reverse_commit_date(const void *a, + const void *b, + void *c) +{ + return -compare_commits_by_commit_date(a, b, c); +} + /* * For debugging -- origin is refcounted, and this asserts that * we do not underflow. @@ -482,6 +490,32 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig } } +/* + * Fill the blob_sha1 field of an origin if it hasn't, so that later + * call to fill_origin_blob() can use it to locate the data. blob_sha1 + * for an origin is also used to pass the blame for the entire file to + * the parent to detect the case where a child's blob is identical to + * that of its parent's. + * + * This also fills origin->mode for corresponding tree path. + */ +static int fill_blob_sha1_and_mode(struct blame_origin *origin) +{ + if (!is_null_oid(&origin->blob_oid)) + return 0; + if (get_tree_entry(origin->commit->object.oid.hash, + origin->path, + origin->blob_oid.hash, &origin->mode)) + goto error_out; + if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB) + goto error_out; + return 0; + error_out: + oidclr(&origin->blob_oid); + origin->mode = S_IFINVALID; + return -1; +} + /* * We have an origin -- check if the same path exists in the * parent and return an origin structure to represent it. @@ -1574,3 +1608,240 @@ void assign_blame(struct blame_scoreboard *sb, int opt) sanity_check_refcnt(sb); } } + +static const char *get_next_line(const char *start, const char *end) +{ + const char *nl = memchr(start, '\n', end - start); + return nl ? nl + 1 : end; +} + +/* + * To allow quick access to the contents of nth line in the + * final image, prepare an index in the scoreboard. + */ +static int prepare_lines(struct blame_scoreboard *sb) +{ + const char *buf = sb->final_buf; + unsigned long len = sb->final_buf_size; + const char *end = buf + len; + const char *p; + int *lineno; + int num = 0; + + for (p = buf; p < end; p = get_next_line(p, end)) + num++; + + ALLOC_ARRAY(sb->lineno, num + 1); + lineno = sb->lineno; + + for (p = buf; p < end; p = get_next_line(p, end)) + *lineno++ = p - buf; + + *lineno = len; + + sb->num_lines = num; + return sb->num_lines; +} + +static struct commit *find_single_final(struct rev_info *revs, + const char **name_p) +{ + int i; + struct commit *found = NULL; + const char *name = NULL; + + for (i = 0; i < revs->pending.nr; i++) { + struct object *obj = revs->pending.objects[i].item; + if (obj->flags & UNINTERESTING) + continue; + obj = deref_tag(obj, NULL, 0); + if (obj->type != OBJ_COMMIT) + die("Non commit %s?", revs->pending.objects[i].name); + if (found) + die("More than one commit to dig from %s and %s?", + revs->pending.objects[i].name, name); + found = (struct commit *)obj; + name = revs->pending.objects[i].name; + } + if (name_p) + *name_p = name; + return found; +} + +static struct commit *dwim_reverse_initial(struct rev_info *revs, + const char **name_p) +{ + /* + * DWIM "git blame --reverse ONE -- PATH" as + * "git blame --reverse ONE..HEAD -- PATH" but only do so + * when it makes sense. + */ + struct object *obj; + struct commit *head_commit; + unsigned char head_sha1[20]; + + if (revs->pending.nr != 1) + return NULL; + + /* Is that sole rev a committish? */ + obj = revs->pending.objects[0].item; + obj = deref_tag(obj, NULL, 0); + if (obj->type != OBJ_COMMIT) + return NULL; + + /* Do we have HEAD? */ + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL)) + return NULL; + head_commit = lookup_commit_reference_gently(head_sha1, 1); + if (!head_commit) + return NULL; + + /* Turn "ONE" into "ONE..HEAD" then */ + obj->flags |= UNINTERESTING; + add_pending_object(revs, &head_commit->object, "HEAD"); + + if (name_p) + *name_p = revs->pending.objects[0].name; + return (struct commit *)obj; +} + +static struct commit *find_single_initial(struct rev_info *revs, + const char **name_p) +{ + int i; + struct commit *found = NULL; + const char *name = NULL; + + /* + * There must be one and only one negative commit, and it must be + * the boundary. + */ + for (i = 0; i < revs->pending.nr; i++) { + struct object *obj = revs->pending.objects[i].item; + if (!(obj->flags & UNINTERESTING)) + continue; + obj = deref_tag(obj, NULL, 0); + if (obj->type != OBJ_COMMIT) + die("Non commit %s?", revs->pending.objects[i].name); + if (found) + die("More than one commit to dig up from, %s and %s?", + revs->pending.objects[i].name, name); + found = (struct commit *) obj; + name = revs->pending.objects[i].name; + } + + if (!name) + found = dwim_reverse_initial(revs, &name); + if (!name) + die("No commit to dig up from?"); + + if (name_p) + *name_p = name; + return found; +} + +void init_scoreboard(struct blame_scoreboard *sb) +{ + memset(sb, 0, sizeof(struct blame_scoreboard)); + sb->move_score = BLAME_DEFAULT_MOVE_SCORE; + sb->copy_score = BLAME_DEFAULT_COPY_SCORE; +} + +void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig) +{ + const char *final_commit_name = NULL; + struct blame_origin *o; + struct commit *final_commit = NULL; + enum object_type type; + + if (sb->reverse && sb->contents_from) + die(_("--contents and --reverse do not blend well.")); + + if (!sb->reverse) { + sb->final = find_single_final(sb->revs, &final_commit_name); + sb->commits.compare = compare_commits_by_commit_date; + } else { + sb->final = find_single_initial(sb->revs, &final_commit_name); + sb->commits.compare = compare_commits_by_reverse_commit_date; + } + + if (sb->final && sb->contents_from) + die(_("cannot use --contents with final commit object name")); + + if (sb->reverse && sb->revs->first_parent_only) + sb->revs->children.name = NULL; + + if (!sb->final) { + /* + * "--not A B -- path" without anything positive; + * do not default to HEAD, but use the working tree + * or "--contents". + */ + setup_work_tree(); + sb->final = fake_working_tree_commit(&sb->revs->diffopt, + path, sb->contents_from); + add_pending_object(sb->revs, &(sb->final->object), ":"); + } + + if (sb->reverse && sb->revs->first_parent_only) { + final_commit = find_single_final(sb->revs, NULL); + if (!final_commit) + die(_("--reverse and --first-parent together require specified latest commit")); + } + + /* + * If we have bottom, this will mark the ancestors of the + * bottom commits we would reach while traversing as + * uninteresting. + */ + if (prepare_revision_walk(sb->revs)) + die(_("revision walk setup failed")); + + if (sb->reverse && sb->revs->first_parent_only) { + struct commit *c = final_commit; + + sb->revs->children.name = "children"; + while (c->parents && + oidcmp(&c->object.oid, &sb->final->object.oid)) { + struct commit_list *l = xcalloc(1, sizeof(*l)); + + l->item = c; + if (add_decoration(&sb->revs->children, + &c->parents->item->object, l)) + die("BUG: not unique item in first-parent chain"); + c = c->parents->item; + } + + if (oidcmp(&c->object.oid, &sb->final->object.oid)) + die(_("--reverse --first-parent together require range along first-parent chain")); + } + + if (is_null_oid(&sb->final->object.oid)) { + o = sb->final->util; + sb->final_buf = xmemdupz(o->file.ptr, o->file.size); + sb->final_buf_size = o->file.size; + } + else { + o = get_origin(sb->final, path); + if (fill_blob_sha1_and_mode(o)) + die(_("no such path %s in %s"), path, final_commit_name); + + if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) && + textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf, + &sb->final_buf_size)) + ; + else + sb->final_buf = read_sha1_file(o->blob_oid.hash, &type, + &sb->final_buf_size); + + if (!sb->final_buf) + die(_("cannot read blob %s for path %s"), + oid_to_hex(&o->blob_oid), + path); + } + sb->num_read_blob++; + prepare_lines(sb); + + if (orig) + *orig = o; +} diff --git a/blame.h b/blame.h index df824b2267..76fd8efe15 100644 --- a/blame.h +++ b/blame.h @@ -13,6 +13,9 @@ #define PICKAXE_BLAME_COPY_HARDER 04 #define PICKAXE_BLAME_COPY_HARDEST 010 +#define BLAME_DEFAULT_MOVE_SCORE 20 +#define BLAME_DEFAULT_COPY_SCORE 40 + /* * One blob in a commit that is being suspected */ @@ -158,14 +161,13 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o) } extern void blame_origin_decref(struct blame_origin *o); -extern struct blame_origin *get_origin(struct commit *commit, const char *path); - -extern struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from); - extern void blame_coalesce(struct blame_scoreboard *sb); extern void blame_sort_final(struct blame_scoreboard *sb); extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e); extern void assign_blame(struct blame_scoreboard *sb, int opt); extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno); +extern void init_scoreboard(struct blame_scoreboard *sb); +extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig); + #endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 461506c3cb..7d9e322d45 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -6,11 +6,8 @@ */ #include "cache.h" -#include "refs.h" #include "builtin.h" #include "commit.h" -#include "tag.h" -#include "tree-walk.h" #include "diff.h" #include "revision.h" #include "quote.h" @@ -60,8 +57,6 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP; static unsigned blame_move_score; static unsigned blame_copy_score; -#define BLAME_DEFAULT_MOVE_SCORE 20 -#define BLAME_DEFAULT_COPY_SCORE 40 /* Remember to update object flag allocation in object.h */ #define METAINFO_SHOWN (1u<<12) @@ -72,39 +67,6 @@ struct progress_info { int blamed_lines; }; -static int compare_commits_by_reverse_commit_date(const void *a, - const void *b, - void *c) -{ - return -compare_commits_by_commit_date(a, b, c); -} - -/* - * Fill the blob_sha1 field of an origin if it hasn't, so that later - * call to fill_origin_blob() can use it to locate the data. blob_sha1 - * for an origin is also used to pass the blame for the entire file to - * the parent to detect the case where a child's blob is identical to - * that of its parent's. - * - * This also fills origin->mode for corresponding tree path. - */ -static int fill_blob_sha1_and_mode(struct blame_origin *origin) -{ - if (!is_null_oid(&origin->blob_oid)) - return 0; - if (get_tree_entry(origin->commit->object.oid.hash, - origin->path, - origin->blob_oid.hash, &origin->mode)) - goto error_out; - if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB) - goto error_out; - return 0; - error_out: - oidclr(&origin->blob_oid); - origin->mode = S_IFINVALID; - return -1; -} - static const char *nth_line_cb(void *data, long lno) { return blame_nth_line((struct blame_scoreboard *)data, lno); @@ -512,40 +474,6 @@ static void output(struct blame_scoreboard *sb, int option) } } -static const char *get_next_line(const char *start, const char *end) -{ - const char *nl = memchr(start, '\n', end - start); - return nl ? nl + 1 : end; -} - -/* - * To allow quick access to the contents of nth line in the - * final image, prepare an index in the scoreboard. - */ -static int prepare_lines(struct blame_scoreboard *sb) -{ - const char *buf = sb->final_buf; - unsigned long len = sb->final_buf_size; - const char *end = buf + len; - const char *p; - int *lineno; - int num = 0; - - for (p = buf; p < end; p = get_next_line(p, end)) - num++; - - ALLOC_ARRAY(sb->lineno, num + 1); - lineno = sb->lineno; - - for (p = buf; p < end; p = get_next_line(p, end)) - *lineno++ = p - buf; - - *lineno = len; - - sb->num_lines = num; - return sb->num_lines; -} - /* * Add phony grafts for use with -S; this is primarily to * support git's cvsserver that wants to give a linear history @@ -687,104 +615,6 @@ static int git_blame_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static struct commit *find_single_final(struct rev_info *revs, - const char **name_p) -{ - int i; - struct commit *found = NULL; - const char *name = NULL; - - for (i = 0; i < revs->pending.nr; i++) { - struct object *obj = revs->pending.objects[i].item; - if (obj->flags & UNINTERESTING) - continue; - obj = deref_tag(obj, NULL, 0); - if (obj->type != OBJ_COMMIT) - die("Non commit %s?", revs->pending.objects[i].name); - if (found) - die("More than one commit to dig from %s and %s?", - revs->pending.objects[i].name, name); - found = (struct commit *)obj; - name = revs->pending.objects[i].name; - } - if (name_p) - *name_p = name; - return found; -} - -static struct commit *dwim_reverse_initial(struct rev_info *revs, - const char **name_p) -{ - /* - * DWIM "git blame --reverse ONE -- PATH" as - * "git blame --reverse ONE..HEAD -- PATH" but only do so - * when it makes sense. - */ - struct object *obj; - struct commit *head_commit; - unsigned char head_sha1[20]; - - if (revs->pending.nr != 1) - return NULL; - - /* Is that sole rev a committish? */ - obj = revs->pending.objects[0].item; - obj = deref_tag(obj, NULL, 0); - if (obj->type != OBJ_COMMIT) - return NULL; - - /* Do we have HEAD? */ - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL)) - return NULL; - head_commit = lookup_commit_reference_gently(head_sha1, 1); - if (!head_commit) - return NULL; - - /* Turn "ONE" into "ONE..HEAD" then */ - obj->flags |= UNINTERESTING; - add_pending_object(revs, &head_commit->object, "HEAD"); - - if (name_p) - *name_p = revs->pending.objects[0].name; - return (struct commit *)obj; -} - -static struct commit *find_single_initial(struct rev_info *revs, - const char **name_p) -{ - int i; - const char *final_commit_name = NULL; - struct commit *found = NULL; - - /* - * There must be one and only one negative commit, and it must be - * the boundary. - */ - for (i = 0; i < revs->pending.nr; i++) { - struct object *obj = revs->pending.objects[i].item; - if (!(obj->flags & UNINTERESTING)) - continue; - obj = deref_tag(obj, NULL, 0); - if (obj->type != OBJ_COMMIT) - die("Non commit %s?", revs->pending.objects[i].name); - if (found) - die("More than one commit to dig up from, %s and %s?", - revs->pending.objects[i].name, - final_commit_name); - found = (struct commit *) obj; - final_commit_name = revs->pending.objects[i].name; - } - - if (!final_commit_name) - found = dwim_reverse_initial(revs, &final_commit_name); - if (!final_commit_name) - die("No commit to dig up from?"); - - if (name_p) - *name_p = final_commit_name; - return found; -} - static int blame_copy_callback(const struct option *option, const char *arg, int unset) { int *opt = option->value; @@ -818,112 +648,6 @@ static int blame_move_callback(const struct option *option, const char *arg, int return 0; } -void init_scoreboard(struct blame_scoreboard *sb) -{ - memset(sb, 0, sizeof(struct blame_scoreboard)); - sb->move_score = BLAME_DEFAULT_MOVE_SCORE; - sb->copy_score = BLAME_DEFAULT_COPY_SCORE; -} - -void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig) -{ - const char *final_commit_name = NULL; - struct blame_origin *o; - struct commit *final_commit = NULL; - enum object_type type; - - if (sb->reverse && sb->contents_from) - die(_("--contents and --reverse do not blend well.")); - - if (!sb->reverse) { - sb->final = find_single_final(sb->revs, &final_commit_name); - sb->commits.compare = compare_commits_by_commit_date; - } else { - sb->final = find_single_initial(sb->revs, &final_commit_name); - sb->commits.compare = compare_commits_by_reverse_commit_date; - } - - if (sb->final && sb->contents_from) - die(_("cannot use --contents with final commit object name")); - - if (sb->reverse && sb->revs->first_parent_only) - sb->revs->children.name = NULL; - - if (!sb->final) { - /* - * "--not A B -- path" without anything positive; - * do not default to HEAD, but use the working tree - * or "--contents". - */ - setup_work_tree(); - sb->final = fake_working_tree_commit(&sb->revs->diffopt, - path, sb->contents_from); - add_pending_object(sb->revs, &(sb->final->object), ":"); - } - - if (sb->reverse && sb->revs->first_parent_only) { - final_commit = find_single_final(sb->revs, NULL); - if (!final_commit) - die(_("--reverse and --first-parent together require specified latest commit")); - } - - /* - * If we have bottom, this will mark the ancestors of the - * bottom commits we would reach while traversing as - * uninteresting. - */ - if (prepare_revision_walk(sb->revs)) - die(_("revision walk setup failed")); - - if (sb->reverse && sb->revs->first_parent_only) { - struct commit *c = final_commit; - - sb->revs->children.name = "children"; - while (c->parents && - oidcmp(&c->object.oid, &sb->final->object.oid)) { - struct commit_list *l = xcalloc(1, sizeof(*l)); - - l->item = c; - if (add_decoration(&sb->revs->children, - &c->parents->item->object, l)) - die("BUG: not unique item in first-parent chain"); - c = c->parents->item; - } - - if (oidcmp(&c->object.oid, &sb->final->object.oid)) - die(_("--reverse --first-parent together require range along first-parent chain")); - } - - if (is_null_oid(&sb->final->object.oid)) { - o = sb->final->util; - sb->final_buf = xmemdupz(o->file.ptr, o->file.size); - sb->final_buf_size = o->file.size; - } - else { - o = get_origin(sb->final, path); - if (fill_blob_sha1_and_mode(o)) - die(_("no such path %s in %s"), path, final_commit_name); - - if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) && - textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf, - &sb->final_buf_size)) - ; - else - sb->final_buf = read_sha1_file(o->blob_oid.hash, &type, - &sb->final_buf_size); - - if (!sb->final_buf) - die(_("cannot read blob %s for path %s"), - oid_to_hex(&o->blob_oid), - path); - } - sb->num_read_blob++; - prepare_lines(sb); - - if (orig) - *orig = o; -} - struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o) From bd481de713cb59a57f5f988b9bc46550c954dc45 Mon Sep 17 00:00:00 2001 From: Jeff Smith Date: Wed, 24 May 2017 00:15:37 -0500 Subject: [PATCH 351/566] blame: move entry prepend to libgit Signed-off-by: Jeff Smith Signed-off-by: Junio C Hamano --- blame.c | 16 ++++++++++++++++ blame.h | 2 ++ builtin/blame.c | 14 -------------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/blame.c b/blame.c index f6c9cb7df9..00404b9738 100644 --- a/blame.c +++ b/blame.c @@ -1845,3 +1845,19 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam if (orig) *orig = o; } + + + +struct blame_entry *blame_entry_prepend(struct blame_entry *head, + long start, long end, + struct blame_origin *o) +{ + struct blame_entry *new_head = xcalloc(1, sizeof(struct blame_entry)); + new_head->lno = start; + new_head->num_lines = end - start; + new_head->suspect = o; + new_head->s_lno = start; + new_head->next = head; + blame_origin_incref(o); + return new_head; +} diff --git a/blame.h b/blame.h index 76fd8efe15..a6c915c277 100644 --- a/blame.h +++ b/blame.h @@ -170,4 +170,6 @@ extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno); extern void init_scoreboard(struct blame_scoreboard *sb); extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig); +extern struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o); + #endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 7d9e322d45..08f35bd2de 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -648,20 +648,6 @@ static int blame_move_callback(const struct option *option, const char *arg, int return 0; } -struct blame_entry *blame_entry_prepend(struct blame_entry *head, - long start, long end, - struct blame_origin *o) -{ - struct blame_entry *new_head = xcalloc(1, sizeof(struct blame_entry)); - new_head->lno = start; - new_head->num_lines = end - start; - new_head->suspect = o; - new_head->s_lno = start; - new_head->next = head; - blame_origin_incref(o); - return new_head; -} - int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; From d9244ecf4f109030e61b8fd52a799789133e2199 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 25 May 2017 14:00:13 +0200 Subject: [PATCH 352/566] Windows: do not treat a path with backslashes as a remote's nick name On Windows, the remote repository name in, e.g., `git fetch foo\bar` is clearly not a nickname for a configured remote repository. However, the function valid_remote_nick() does not account for backslashes. Use is_dir_sep() to check for both slashes and backslashes on Windows. This was discovered while playing with Duy's patches that warn after fopen() failures. The functions that read the branches and remotes files are protected by a valid_remote_nick() check. Without this change, a Windows style absolute path is incorrectly regarded as nickname and is concatenated to a prefix and used with fopen(). This triggers warnings because a colon in a path name is not allowed: C:\Temp\gittest>git fetch C:\Temp\gittest warning: unable to access '.git/remotes/C:\Temp\gittest': Invalid argument warning: unable to access '.git/branches/C:\Temp\gittest': Invalid argument From C:\Temp\gittest * branch HEAD -> FETCH_HEAD Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- remote.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/remote.c b/remote.c index ad6c5424ed..1949882c10 100644 --- a/remote.c +++ b/remote.c @@ -645,7 +645,12 @@ static int valid_remote_nick(const char *name) { if (!name[0] || is_dot_or_dotdot(name)) return 0; - return !strchr(name, '/'); /* no slash */ + + /* remote nicknames cannot contain slashes */ + while (*name) + if (is_dir_sep(*name++)) + return 0; + return 1; } const char *remote_for_branch(struct branch *branch, int *explicit) From 449456ad47e4074d6d2ee1d1d8178ab29848a137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 26 May 2017 01:20:46 +0200 Subject: [PATCH 353/566] docs/config.txt: fix indefinite article in core.fileMode description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- Documentation/config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index a45b2cfe6c..3147f41f12 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -208,7 +208,7 @@ core.fileMode:: is to be honored. + Some filesystems lose the executable bit when a file that is -marked as executable is checked out, or checks out an +marked as executable is checked out, or checks out a non-executable file with executable bit on. linkgit:git-clone[1] or linkgit:git-init[1] probe the filesystem to see if it handles the executable bit correctly From b0a642ac4624740187212aecec62b35a2eab38cb Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Mon, 8 May 2017 21:45:56 +0100 Subject: [PATCH 354/566] git_fopen: fix a sparse 'not declared' warning If git is built with the FREAD_READS_DIRECTORIES build variable set, this would cause sparse to issue a 'not declared, should it be static?' warning on Linux. This is a result of the method employed by 'compat/fopen.c' to suppress the (possible) redefinition of the (system) fopen macro, which also removes the extern declaration of the git_fopen function. In order to suppress the warning, introduce a new macro to suppress the definition (or possibly the re-definition) of the fopen symbol as a macro override. This new macro (SUPPRESS_FOPEN_REDEFINITION) is only defined in 'compat/fopen.c', just prior to the inclusion of the 'git-compat-util.h' header file. Signed-off-by: Ramsay Jones Signed-off-by: Junio C Hamano --- compat/fopen.c | 4 ++-- git-compat-util.h | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compat/fopen.c b/compat/fopen.c index b5ca142fed..107b3e8182 100644 --- a/compat/fopen.c +++ b/compat/fopen.c @@ -1,14 +1,14 @@ /* * The order of the following two lines is important. * - * FREAD_READS_DIRECTORIES is undefined before including git-compat-util.h + * SUPPRESS_FOPEN_REDEFINITION is defined before including git-compat-util.h * to avoid the redefinition of fopen within git-compat-util.h. This is * necessary since fopen is a macro on some platforms which may be set * based on compiler options. For example, on AIX fopen is set to fopen64 * when _LARGE_FILES is defined. The previous technique of merely undefining * fopen after including git-compat-util.h is inadequate in this case. */ -#undef FREAD_READS_DIRECTORIES +#define SUPPRESS_FOPEN_REDEFINITION #include "../git-compat-util.h" FILE *git_fopen(const char *path, const char *mode) diff --git a/git-compat-util.h b/git-compat-util.h index bd04564a69..6be55cf8b3 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -689,10 +689,12 @@ char *gitstrdup(const char *s); #endif #ifdef FREAD_READS_DIRECTORIES -#ifdef fopen -#undef fopen -#endif -#define fopen(a,b) git_fopen(a,b) +# if !defined(SUPPRESS_FOPEN_REDEFINITION) +# ifdef fopen +# undef fopen +# endif +# define fopen(a,b) git_fopen(a,b) +# endif extern FILE *git_fopen(const char*, const char*); #endif From 23a9e0712d76a63c5af1caeb816943f466f0300e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:46 +0700 Subject: [PATCH 355/566] use xfopen() in more places MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xfopen() - provides error details - explains error on reading, or writing, or whatever operation - has l10n support - prints file name in the error Some of these are missing in the places that are replaced with xfopen(), which is a clear win. In some other places, it's just less code (not as clearly a win as the previous case but still is). The only slight regresssion is in remote-testsvn, where we don't report the file class (marks files) in the error messages anymore. But since this is a _test_ svn remote transport, I'm not too concerned. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- bisect.c | 5 +---- builtin/am.c | 8 ++------ builtin/commit.c | 5 +---- builtin/fast-export.c | 4 +--- builtin/fsck.c | 3 +-- builtin/merge.c | 4 +--- builtin/pull.c | 3 +-- diff.c | 8 ++------ fast-import.c | 4 +--- remote-testsvn.c | 8 ++------ 10 files changed, 13 insertions(+), 39 deletions(-) diff --git a/bisect.c b/bisect.c index 08c9fb7266..469a3e9061 100644 --- a/bisect.c +++ b/bisect.c @@ -438,10 +438,7 @@ static void read_bisect_paths(struct argv_array *array) { struct strbuf str = STRBUF_INIT; const char *filename = git_path_bisect_names(); - FILE *fp = fopen(filename, "r"); - - if (!fp) - die_errno(_("Could not open file '%s'"), filename); + FILE *fp = xfopen(filename, "r"); while (strbuf_getline_lf(&str, fp) != EOF) { strbuf_trim(&str); diff --git a/builtin/am.c b/builtin/am.c index a95dd8b4e6..f5dac7783e 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1275,12 +1275,8 @@ static int parse_mail(struct am_state *state, const char *mail) die("BUG: invalid value for state->scissors"); } - mi.input = fopen(mail, "r"); - if (!mi.input) - die("could not open input"); - mi.output = fopen(am_path(state, "info"), "w"); - if (!mi.output) - die("could not open output 'info'"); + mi.input = xfopen(mail, "r"); + mi.output = xfopen(am_path(state, "info"), "w"); if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch"))) die("could not parse patch"); diff --git a/builtin/commit.c b/builtin/commit.c index 1d805f5da8..eda0d32311 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1695,10 +1695,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = commit_list_append(current_head, pptr); - fp = fopen(git_path_merge_head(), "r"); - if (fp == NULL) - die_errno(_("could not open '%s' for reading"), - git_path_merge_head()); + fp = xfopen(git_path_merge_head(), "r"); while (strbuf_getline_lf(&m, fp) != EOF) { struct commit *parent; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e0220630d0..128b99e6da 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -905,9 +905,7 @@ static void export_marks(char *file) static void import_marks(char *input_file) { char line[512]; - FILE *f = fopen(input_file, "r"); - if (!f) - die_errno("cannot read '%s'", input_file); + FILE *f = xfopen(input_file, "r"); while (fgets(line, sizeof(line), f)) { uint32_t mark; diff --git a/builtin/fsck.c b/builtin/fsck.c index b5e13a4556..00beaaa4e6 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -280,8 +280,7 @@ static void check_unreachable_object(struct object *obj) free(filename); return; } - if (!(f = fopen(filename, "w"))) - die_errno("Could not open '%s'", filename); + f = xfopen(filename, "w"); if (obj->type == OBJ_BLOB) { if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1)) die_errno("Could not write '%s'", filename); diff --git a/builtin/merge.c b/builtin/merge.c index 703827f006..65a1501858 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -839,9 +839,7 @@ static int suggest_conflicts(void) struct strbuf msgbuf = STRBUF_INIT; filename = git_path_merge_msg(); - fp = fopen(filename, "a"); - if (!fp) - die_errno(_("Could not open '%s' for writing"), filename); + fp = xfopen(filename, "a"); append_conflicts_hint(&msgbuf); fputs(msgbuf.buf, fp); diff --git a/builtin/pull.c b/builtin/pull.c index dd1a4a94e4..589c25becf 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -337,8 +337,7 @@ static void get_merge_heads(struct oid_array *merge_heads) struct strbuf sb = STRBUF_INIT; struct object_id oid; - if (!(fp = fopen(filename, "r"))) - die_errno(_("could not open '%s' for reading"), filename); + fp = xfopen(filename, "r"); while (strbuf_getline_lf(&sb, fp) != EOF) { if (get_oid_hex(sb.buf, &oid)) continue; /* invalid line: does not start with SHA1 */ diff --git a/diff.c b/diff.c index 11eef1c85d..b6597ce568 100644 --- a/diff.c +++ b/diff.c @@ -4071,9 +4071,7 @@ int diff_opt_parse(struct diff_options *options, DIFF_OPT_CLR(options, FUNCCONTEXT); else if ((argcount = parse_long_opt("output", av, &optarg))) { char *path = prefix_filename(prefix, optarg); - options->file = fopen(path, "w"); - if (!options->file) - die_errno("Could not open '%s'", path); + options->file = xfopen(path, "w"); options->close_file = 1; if (options->use_color != GIT_COLOR_ALWAYS) options->use_color = GIT_COLOR_NEVER; @@ -4807,9 +4805,7 @@ void diff_flush(struct diff_options *options) */ if (options->close_file) fclose(options->file); - options->file = fopen("/dev/null", "w"); - if (!options->file) - die_errno("Could not open /dev/null"); + options->file = xfopen("/dev/null", "w"); options->close_file = 1; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; diff --git a/fast-import.c b/fast-import.c index cf58f875b8..420b3a00d3 100644 --- a/fast-import.c +++ b/fast-import.c @@ -3274,9 +3274,7 @@ static void option_export_pack_edges(const char *edges) { if (pack_edges) fclose(pack_edges); - pack_edges = fopen(edges, "a"); - if (!pack_edges) - die_errno("Cannot open '%s'", edges); + pack_edges = xfopen(edges, "a"); } static int parse_one_option(const char *option) diff --git a/remote-testsvn.c b/remote-testsvn.c index f87bf851ba..50404ef343 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -124,10 +124,8 @@ static int note2mark_cb(const unsigned char *object_sha1, static void regenerate_marks(void) { int ret; - FILE *marksfile = fopen(marksfilename, "w+"); + FILE *marksfile = xfopen(marksfilename, "w+"); - if (!marksfile) - die_errno("Couldn't create mark file %s.", marksfilename); ret = for_each_note(NULL, 0, note2mark_cb, marksfile); if (ret) die("Regeneration of marks failed, returned %d.", ret); @@ -148,9 +146,7 @@ static void check_or_regenerate_marks(int latestrev) marksfile = fopen(marksfilename, "r"); if (!marksfile) { regenerate_marks(); - marksfile = fopen(marksfilename, "r"); - if (!marksfile) - die_errno("cannot read marks file %s!", marksfilename); + marksfile = xfopen(marksfilename, "r"); fclose(marksfile); } else { strbuf_addf(&sb, ":%d ", latestrev); From 02912f477586befaf46e0db4f7c47b52081d5b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:47 +0700 Subject: [PATCH 356/566] clone: use xfopen() instead of fopen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit copy_alternates() called fopen() without handling errors. By switching to xfopen(), this bug is fixed. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/clone.c b/builtin/clone.c index de85b85254..dde4fe73af 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -357,7 +357,7 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst, * to turn entries with paths relative to the original * absolute, so that they can be used in the new repository. */ - FILE *in = fopen(src->buf, "r"); + FILE *in = xfopen(src->buf, "r"); struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in) != EOF) { From e2d90fd1c33ae57e4a6da5729ae53876107b3463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:48 +0700 Subject: [PATCH 357/566] config.mak.uname: set FREAD_READS_DIRECTORIES for Linux and FreeBSD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This variable is added [1] with the assumption that on a sane system, fopen(, "r") should return NULL. Linux and FreeBSD do not meet this expectation while at least Windows and AIX do. Let's make sure they behave the same way. I only tested one version on Linux (4.7.0 with glibc 2.22) and FreeBSD (11.0) but since GNU/kFreeBSD is fbsd kernel with gnu userspace, I'm pretty sure it shares the same problem. [1] cba22528fa (Add compat/fopen.c which returns NULL on attempt to open directory - 2008-02-08) Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- config.mak.uname | 3 +++ t/t1308-config-set.sh | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/config.mak.uname b/config.mak.uname index 399fe19271..a25ffddb3e 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -36,6 +36,7 @@ ifeq ($(uname_S),Linux) NEEDS_LIBRT = YesPlease HAVE_GETDELIM = YesPlease SANE_TEXT_GREP=-a + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease @@ -43,6 +44,7 @@ ifeq ($(uname_S),GNU/kFreeBSD) HAVE_PATHS_H = YesPlease DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),UnixWare) CC = cc @@ -201,6 +203,7 @@ ifeq ($(uname_S),FreeBSD) GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes HAVE_BSD_SYSCTL = YesPlease PAGER_ENV = LESS=FRX LV=-c MORE=FRX + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index ff50960cca..72d5f1f931 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -183,6 +183,14 @@ test_expect_success 'proper error on non-existent files' ' test_cmp expect actual ' +test_expect_success 'proper error on directory "files"' ' + echo "Error (-1) reading configuration file a-directory." >expect && + mkdir a-directory && + test_expect_code 2 test-config configset_get_value foo.bar a-directory 2>output && + grep "^Error" output >actual && + test_cmp expect actual +' + test_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' ' chmod -r .git/config && test_when_finished "chmod +r .git/config" && From 8e178ec4d072da4cd8f4449e17aef3aff5b57f6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 9 May 2017 21:44:33 -0700 Subject: [PATCH 358/566] config.mak.uname: set FREAD_READS_DIRECTORIES for Darwin, too Signed-off-by: Junio C Hamano --- config.mak.uname | 1 + 1 file changed, 1 insertion(+) diff --git a/config.mak.uname b/config.mak.uname index a25ffddb3e..1743890164 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -110,6 +110,7 @@ ifeq ($(uname_S),Darwin) BASIC_CFLAGS += -DPRECOMPOSE_UNICODE BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1 HAVE_BSD_SYSCTL = YesPlease + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease From 11dc1fcb3fa53f5a46486daa7cb38ed387153f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:49 +0700 Subject: [PATCH 359/566] wrapper.c: add and use warn_on_fopen_errors() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In many places, Git warns about an inaccessible file after a fopen() failed. To discern these cases from other cases where we want to warn about inaccessible files, introduce a new helper specifically to test whether fopen() failed because the current user lacks the permission to open file in question. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- config.c | 3 +++ dir.c | 6 +++--- git-compat-util.h | 2 ++ t/t1308-config-set.sh | 3 ++- wrapper.c | 10 ++++++++++ 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index b4a3205da3..2894fbb6d0 100644 --- a/config.c +++ b/config.c @@ -2640,6 +2640,9 @@ int git_config_rename_section_in_file(const char *config_filename, } if (!(config_file = fopen(config_filename, "rb"))) { + ret = warn_on_fopen_errors(config_filename); + if (ret) + goto out; /* no config file means nothing to rename, no error */ goto commit_and_out; } diff --git a/dir.c b/dir.c index f451bfa48c..be616e884e 100644 --- a/dir.c +++ b/dir.c @@ -745,9 +745,9 @@ static int add_excludes(const char *fname, const char *base, int baselen, fd = open(fname, O_RDONLY); if (fd < 0 || fstat(fd, &st) < 0) { - if (errno != ENOENT) - warn_on_inaccessible(fname); - if (0 <= fd) + if (fd < 0) + warn_on_fopen_errors(fname); + else close(fd); if (!check_index || (buf = read_skip_worktree_file_from_index(fname, &size, sha1_stat)) == NULL) diff --git a/git-compat-util.h b/git-compat-util.h index 6be55cf8b3..eb5c18c7fd 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1101,6 +1101,8 @@ int access_or_die(const char *path, int mode, unsigned flag); /* Warn on an inaccessible file that ought to be accessible */ void warn_on_inaccessible(const char *path); +/* Warn on an inaccessible file if errno indicates this is an error */ +int warn_on_fopen_errors(const char *path); #ifdef GMTIME_UNRELIABLE_ERRORS struct tm *git_gmtime(const time_t *); diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 72d5f1f931..df92fdcd40 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -195,7 +195,8 @@ test_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' ' chmod -r .git/config && test_when_finished "chmod +r .git/config" && echo "Error (-1) reading configuration file .git/config." >expect && - test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>actual && + test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>output && + grep "^Error" output >actual && test_cmp expect actual ' diff --git a/wrapper.c b/wrapper.c index d837417709..20c25e7e65 100644 --- a/wrapper.c +++ b/wrapper.c @@ -418,6 +418,16 @@ FILE *fopen_for_writing(const char *path) return ret; } +int warn_on_fopen_errors(const char *path) +{ + if (errno != ENOENT && errno != ENOTDIR) { + warn_on_inaccessible(path); + return -1; + } + + return 0; +} + int xmkstemp(char *template) { int fd; From e9d983f116c7de43f40a49aae60ebfe107f153ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:50 +0700 Subject: [PATCH 360/566] wrapper.c: add and use fopen_or_warn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When fopen() returns NULL, it could be because the given path does not exist, but it could also be some other errors and the caller has to check. Add a wrapper so we don't have to repeat the same error check everywhere. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- attr.c | 7 ++----- bisect.c | 2 +- builtin/blame.c | 2 +- commit.c | 2 +- config.c | 2 +- git-compat-util.h | 1 + ident.c | 8 +++----- remote.c | 4 ++-- rerere.c | 2 +- sequencer.c | 8 ++++---- server-info.c | 2 +- t/t1308-config-set.sh | 2 ++ t/t5512-ls-remote.sh | 13 ++++++++++--- wrapper.c | 11 +++++++++++ wt-status.c | 3 ++- 15 files changed, 43 insertions(+), 26 deletions(-) diff --git a/attr.c b/attr.c index 7e2134471c..821203e2a9 100644 --- a/attr.c +++ b/attr.c @@ -720,16 +720,13 @@ void git_attr_set_direction(enum git_attr_direction new_direction, static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { - FILE *fp = fopen(path, "r"); + FILE *fp = fopen_or_warn(path, "r"); struct attr_stack *res; char buf[2048]; int lineno = 0; - if (!fp) { - if (errno != ENOENT && errno != ENOTDIR) - warn_on_inaccessible(path); + if (!fp) return NULL; - } res = xcalloc(1, sizeof(*res)); while (fgets(buf, sizeof(buf), fp)) { char *bufp = buf; diff --git a/bisect.c b/bisect.c index 469a3e9061..bb28bf63b2 100644 --- a/bisect.c +++ b/bisect.c @@ -666,7 +666,7 @@ static int is_expected_rev(const struct object_id *oid) if (stat(filename, &st) || !S_ISREG(st.st_mode)) return 0; - fp = fopen(filename, "r"); + fp = fopen_or_warn(filename, "r"); if (!fp) return 0; diff --git a/builtin/blame.c b/builtin/blame.c index 07506a3e45..34445d7894 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2071,7 +2071,7 @@ static int prepare_lines(struct scoreboard *sb) */ static int read_ancestry(const char *graft_file) { - FILE *fp = fopen(graft_file, "r"); + FILE *fp = fopen_or_warn(graft_file, "r"); struct strbuf buf = STRBUF_INIT; if (!fp) return -1; diff --git a/commit.c b/commit.c index 73c78c2b80..3eeda081f9 100644 --- a/commit.c +++ b/commit.c @@ -167,7 +167,7 @@ bad_graft_data: static int read_graft_file(const char *graft_file) { - FILE *fp = fopen(graft_file, "r"); + FILE *fp = fopen_or_warn(graft_file, "r"); struct strbuf buf = STRBUF_INIT; if (!fp) return -1; diff --git a/config.c b/config.c index 2894fbb6d0..e54d99d519 100644 --- a/config.c +++ b/config.c @@ -1422,7 +1422,7 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data) int ret = -1; FILE *f; - f = fopen(filename, "r"); + f = fopen_or_warn(filename, "r"); if (f) { flockfile(f); ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data); diff --git a/git-compat-util.h b/git-compat-util.h index eb5c18c7fd..f74b401810 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -802,6 +802,7 @@ extern int xmkstemp(char *template); extern int xmkstemp_mode(char *template, int mode); extern char *xgetcwd(void); extern FILE *fopen_for_writing(const char *path); +extern FILE *fopen_or_warn(const char *path, const char *mode); #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc))) #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc))) diff --git a/ident.c b/ident.c index bea871c8e0..91c7609055 100644 --- a/ident.c +++ b/ident.c @@ -72,12 +72,10 @@ static int add_mailname_host(struct strbuf *buf) FILE *mailname; struct strbuf mailnamebuf = STRBUF_INIT; - mailname = fopen("/etc/mailname", "r"); - if (!mailname) { - if (errno != ENOENT) - warning_errno("cannot open /etc/mailname"); + mailname = fopen_or_warn("/etc/mailname", "r"); + if (!mailname) return -1; - } + if (strbuf_getline(&mailnamebuf, mailname) == EOF) { if (ferror(mailname)) warning_errno("cannot read /etc/mailname"); diff --git a/remote.c b/remote.c index 801137c72e..1f2453d0f6 100644 --- a/remote.c +++ b/remote.c @@ -251,7 +251,7 @@ static const char *skip_spaces(const char *s) static void read_remotes_file(struct remote *remote) { struct strbuf buf = STRBUF_INIT; - FILE *f = fopen(git_path("remotes/%s", remote->name), "r"); + FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r"); if (!f) return; @@ -277,7 +277,7 @@ static void read_branches_file(struct remote *remote) { char *frag; struct strbuf buf = STRBUF_INIT; - FILE *f = fopen(git_path("branches/%s", remote->name), "r"); + FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r"); if (!f) return; diff --git a/rerere.c b/rerere.c index 3bd55caf3b..971bfedfb2 100644 --- a/rerere.c +++ b/rerere.c @@ -200,7 +200,7 @@ static struct rerere_id *new_rerere_id(unsigned char *sha1) static void read_rr(struct string_list *rr) { struct strbuf buf = STRBUF_INIT; - FILE *in = fopen(git_path_merge_rr(), "r"); + FILE *in = fopen_or_warn(git_path_merge_rr(), "r"); if (!in) return; diff --git a/sequencer.c b/sequencer.c index 10c3b4ff81..11b5c7c114 100644 --- a/sequencer.c +++ b/sequencer.c @@ -897,8 +897,8 @@ static void flush_rewritten_pending(void) { FILE *out; if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 && - !get_sha1("HEAD", newsha1) && - (out = fopen(rebase_path_rewritten_list(), "a"))) { + !get_sha1("HEAD", newsha1) && + (out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) { char *bol = buf.buf, *eol; while (*bol) { @@ -917,7 +917,7 @@ static void flush_rewritten_pending(void) { static void record_in_rewritten(struct object_id *oid, enum todo_command next_command) { - FILE *out = fopen(rebase_path_rewritten_pending(), "a"); + FILE *out = fopen_or_warn(rebase_path_rewritten_pending(), "a"); if (!out) return; @@ -1378,7 +1378,7 @@ static int read_populate_todo(struct todo_list *todo_list, if (is_rebase_i(opts)) { struct todo_list done = TODO_LIST_INIT; - FILE *f = fopen(rebase_path_msgtotal(), "w"); + FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w"); if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 && !parse_insn_buffer(done.buf.buf, &done)) diff --git a/server-info.c b/server-info.c index f6c1a3dfb0..e01ac154a8 100644 --- a/server-info.c +++ b/server-info.c @@ -133,7 +133,7 @@ static int read_pack_info_file(const char *infofile) char line[1000]; int old_cnt = 0; - fp = fopen(infofile, "r"); + fp = fopen_or_warn(infofile, "r"); if (!fp) return 1; /* nonexistent is not an error. */ diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index df92fdcd40..e495a61616 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -187,6 +187,7 @@ test_expect_success 'proper error on directory "files"' ' echo "Error (-1) reading configuration file a-directory." >expect && mkdir a-directory && test_expect_code 2 test-config configset_get_value foo.bar a-directory 2>output && + grep "^warning:" output && grep "^Error" output >actual && test_cmp expect actual ' @@ -196,6 +197,7 @@ test_expect_success POSIXPERM,SANITY 'proper error on non-accessible files' ' test_when_finished "chmod +r .git/config" && echo "Error (-1) reading configuration file .git/config." >expect && test_expect_code 2 test-config configset_get_value foo.bar .git/config 2>output && + grep "^warning:" output && grep "^Error" output >actual && test_cmp expect actual ' diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 94fc9be9ce..02106c9226 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -85,8 +85,15 @@ test_expect_success 'use branch..remote if possible' ' ' test_expect_success 'confuses pattern as remote when no remote specified' ' - cat >exp <<-\EOF && - fatal: '\''refs*master'\'' does not appear to be a git repository + if test_have_prereq MINGW + then + # Windows does not like asterisks in pathname + does_not_exist=master + else + does_not_exist="refs*master" + fi && + cat >exp <<-EOF && + fatal: '\''$does_not_exist'\'' does not appear to be a git repository fatal: Could not read from remote repository. Please make sure you have the correct access rights @@ -98,7 +105,7 @@ test_expect_success 'confuses pattern as remote when no remote specified' ' # fetch . # We could just as easily have used "master"; the "*" emphasizes its # role as a pattern. - test_must_fail git ls-remote refs*master >actual 2>&1 && + test_must_fail git ls-remote "$does_not_exist" >actual 2>&1 && test_i18ncmp exp actual ' diff --git a/wrapper.c b/wrapper.c index 20c25e7e65..6e513c904a 100644 --- a/wrapper.c +++ b/wrapper.c @@ -428,6 +428,17 @@ int warn_on_fopen_errors(const char *path) return 0; } +FILE *fopen_or_warn(const char *path, const char *mode) +{ + FILE *fp = fopen(path, mode); + + if (fp) + return fp; + + warn_on_fopen_errors(path); + return NULL; +} + int xmkstemp(char *template) { int fd; diff --git a/wt-status.c b/wt-status.c index 0375484962..cdf9f5eed2 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1065,7 +1065,8 @@ static void show_am_in_progress(struct wt_status *s, static char *read_line_from_git_path(const char *filename) { struct strbuf buf = STRBUF_INIT; - FILE *fp = fopen(git_path("%s", filename), "r"); + FILE *fp = fopen_or_warn(git_path("%s", filename), "r"); + if (!fp) { strbuf_release(&buf); return NULL; From 382fb07f7bbb1f94fa3e1293d5df151725a886a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 8 May 2017 17:40:37 +0700 Subject: [PATCH 361/566] wrapper.c: make warn_on_inaccessible() static After the last patch, this function is not used outside anymore. Keep it static. Noticed-by: Ramsay Jones Signed-off-by: Junio C Hamano --- git-compat-util.h | 2 -- wrapper.c | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/git-compat-util.h b/git-compat-util.h index f74b401810..87f47828a5 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1100,8 +1100,6 @@ int remove_or_warn(unsigned int mode, const char *path); int access_or_warn(const char *path, int mode, unsigned flag); int access_or_die(const char *path, int mode, unsigned flag); -/* Warn on an inaccessible file that ought to be accessible */ -void warn_on_inaccessible(const char *path); /* Warn on an inaccessible file if errno indicates this is an error */ int warn_on_fopen_errors(const char *path); diff --git a/wrapper.c b/wrapper.c index 6e513c904a..b117eb14a4 100644 --- a/wrapper.c +++ b/wrapper.c @@ -418,6 +418,11 @@ FILE *fopen_for_writing(const char *path) return ret; } +static void warn_on_inaccessible(const char *path) +{ + warning_errno(_("unable to access '%s'"), path); +} + int warn_on_fopen_errors(const char *path) { if (errno != ENOENT && errno != ENOTDIR) { @@ -597,11 +602,6 @@ int remove_or_warn(unsigned int mode, const char *file) return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file); } -void warn_on_inaccessible(const char *path) -{ - warning_errno(_("unable to access '%s'"), path); -} - static int access_error_is_ok(int err, unsigned flag) { return err == ENOENT || err == ENOTDIR || From 5118d7f4e6e00b7eac3ce08a16392a732edc0b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:55 +0700 Subject: [PATCH 362/566] print errno when reporting a system call error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/log.c | 3 ++- rerere.c | 4 ++-- xdiff-interface.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index b3b10cc1ed..26d6a3cf14 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -858,7 +858,8 @@ static int open_next_file(struct commit *commit, const char *subject, printf("%s\n", filename.buf + outdir_offset); if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) - return error(_("Cannot open patch file %s"), filename.buf); + return error_errno(_("Cannot open patch file %s"), + filename.buf); strbuf_release(&filename); return 0; diff --git a/rerere.c b/rerere.c index 971bfedfb2..1351b0c3fb 100644 --- a/rerere.c +++ b/rerere.c @@ -484,13 +484,13 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output io.input = fopen(path, "r"); io.io.wrerror = 0; if (!io.input) - return error("Could not open %s", path); + return error_errno("Could not open %s", path); if (output) { io.io.output = fopen(output, "w"); if (!io.io.output) { fclose(io.input); - return error("Could not write %s", output); + return error_errno("Could not write %s", output); } } diff --git a/xdiff-interface.c b/xdiff-interface.c index 060038c2d6..d3f78ca2a7 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -164,9 +164,9 @@ int read_mmfile(mmfile_t *ptr, const char *filename) size_t sz; if (stat(filename, &st)) - return error("Could not stat %s", filename); + return error_errno("Could not stat %s", filename); if ((f = fopen(filename, "rb")) == NULL) - return error("Could not open %s", filename); + return error_errno("Could not open %s", filename); sz = xsize_t(st.st_size); ptr->ptr = xmalloc(sz ? sz : 1); if (sz && fread(ptr->ptr, sz, 1, f) != 1) { From f7566f073fccafdd9e0ace514b25897dd55d217a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 9 May 2017 17:11:33 +0700 Subject: [PATCH 363/566] rerere.c: move error_errno() closer to the source system call We are supposed to report errno from fopen(). fclose() between fopen() and the report function could either change errno or reset it. Signed-off-by: Junio C Hamano --- rerere.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rerere.c b/rerere.c index 1351b0c3fb..c26c29f87a 100644 --- a/rerere.c +++ b/rerere.c @@ -489,8 +489,9 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output if (output) { io.io.output = fopen(output, "w"); if (!io.io.output) { + error_errno("Could not write %s", output); fclose(io.input); - return error_errno("Could not write %s", output); + return -1; } } From 15d980a785f8c962bc032df40cb42fdc269c9dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Wed, 3 May 2017 17:16:56 +0700 Subject: [PATCH 364/566] log: fix memory leak in open_next_file() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed-by: Jeff King Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/log.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 26d6a3cf14..f075838df9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -842,8 +842,10 @@ static int open_next_file(struct commit *commit, const char *subject, if (output_directory) { strbuf_addstr(&filename, output_directory); if (filename.len >= - PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) + PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) { + strbuf_release(&filename); return error(_("name of output directory is too long")); + } strbuf_complete(&filename, '/'); } @@ -857,9 +859,11 @@ static int open_next_file(struct commit *commit, const char *subject, if (!quiet) printf("%s\n", filename.buf + outdir_offset); - if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) - return error_errno(_("Cannot open patch file %s"), - filename.buf); + if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) { + error_errno(_("Cannot open patch file %s"), filename.buf); + strbuf_release(&filename); + return -1; + } strbuf_release(&filename); return 0; From ef4fe5617eadc24fd09ce7778b77bb87f147480c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 25 May 2017 15:33:05 -0400 Subject: [PATCH 365/566] connect.c: fix leak in parse_one_symref_info() If we successfully parse a symref value like "HEAD:refs/heads/master", we add the result to a string list. But because the string list is marked STRING_LIST_INIT_DUP, the string list code will make a copy of the string and add the copy. This patch fixes it by adding the entry with string_list_append_nodup(), which lets the string list take ownership of our newly allocated string. There are two alternatives that seem like they would work, but aren't the right solution. The first is to initialize the list with the "NODUP" initializer. That would avoid the copy, but then the string list would not realize that it owns the strings. When we eventually call string_list_clear(), it would not free the strings, causing a leak. The second option would be to use the normal string_list_append(), but free the local copy in our function. We can't do this because the local copy actually contains _two_ strings; the symref name and its target. We point to the target pointer via the "util" field, and its memory must last as long as the string list does. You may also wonder whether it's safe to ever free the local copy, since the target points into it. The answer is yes, because we duplicate it in annotaate_refs_with_symref_info before clearing the string list. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connect.c b/connect.c index 8cb93b0720..8af002bf0f 100644 --- a/connect.c +++ b/connect.c @@ -71,7 +71,7 @@ static void parse_one_symref_info(struct string_list *symref, const char *val, i check_refname_format(target, REFNAME_ALLOW_ONELEVEL)) /* "symref=bogus:pair */ goto reject; - item = string_list_append(symref, sym); + item = string_list_append_nodup(symref, sym); item->util = target; return; reject: From 3878c7a5404dc20b24d7a3df2dce76bdc25fbb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:21 +0000 Subject: [PATCH 366/566] perf: add a comparison test of grep regex engines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a very basic performance comparison test comparing the POSIX basic, extended and perl engines. In theory the "basic" and "extended" engines should be implemented using the same underlying code with a slightly different pattern parser, but some implementations may not do this. Jump through some slight hoops to test both, which is worthwhile since "basic" is the default. Running this on an i7 3.4GHz Linux 4.9.0-2 Debian testing against a checkout of linux.git & latest upstream PCRE, both PCRE and git compiled with -O3 using gcc 7.1.1: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux ./run p7820-grep-engines.sh [...] Test this tree --------------------------------------------------------------- 7820.1: basic grep 'how.to' 0.34(1.24+0.53) 7820.2: extended grep 'how.to' 0.33(1.23+0.45) 7820.3: perl grep 'how.to' 0.31(1.05+0.56) 7820.5: basic grep '^how to' 0.32(1.24+0.42) 7820.6: extended grep '^how to' 0.33(1.20+0.44) 7820.7: perl grep '^how to' 0.57(2.67+0.42) 7820.9: basic grep '[how] to' 0.51(2.16+0.45) 7820.10: extended grep '[how] to' 0.49(2.20+0.43) 7820.11: perl grep '[how] to' 0.56(2.60+0.43) 7820.13: basic grep '\(e.t[^ ]*\|v.ry\) rare' 0.66(3.25+0.40) 7820.14: extended grep '(e.t[^ ]*|v.ry) rare' 0.65(3.19+0.46) 7820.15: perl grep '(e.t[^ ]*|v.ry) rare' 1.05(5.74+0.34) 7820.17: basic grep 'm\(ú\|u\)lt.b\(æ\|y\)te' 0.34(1.28+0.47) 7820.18: extended grep 'm(ú|u)lt.b(æ|y)te' 0.34(1.38+0.38) 7820.19: perl grep 'm(ú|u)lt.b(æ|y)te' 0.39(1.56+0.44) Options can also be passed to git-grep via the GIT_PERF_7820_GREP_OPTS environment variable. There are various modes such as "-v" that have very different performance profiles, but handling the combinatorial explosion of testing all those options would make this script much more complex and harder to maintain. Instead just add the ability to do one-shot runs with arbitrary options, e.g.: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_7820_GREP_OPTS=" -i" ./run p7820-grep-engines.sh [...] Test this tree ------------------------------------------------------------------ 7820.1: basic grep -i 'how.to' 0.49(1.72+0.38) 7820.2: extended grep -i 'how.to' 0.46(1.64+0.42) 7820.3: perl grep -i 'how.to' 0.44(1.45+0.45) 7820.5: basic grep -i '^how to' 0.47(1.76+0.38) 7820.6: extended grep -i '^how to' 0.47(1.70+0.42) 7820.7: perl grep -i '^how to' 0.65(2.72+0.37) 7820.9: basic grep -i '[how] to' 0.86(3.64+0.42) 7820.10: extended grep -i '[how] to' 0.84(3.62+0.46) 7820.11: perl grep -i '[how] to' 0.73(3.06+0.39) 7820.13: basic grep -i '\(e.t[^ ]*\|v.ry\) rare' 1.63(8.13+0.36) 7820.14: extended grep -i '(e.t[^ ]*|v.ry) rare' 1.64(8.01+0.44) 7820.15: perl grep -i '(e.t[^ ]*|v.ry) rare' 1.44(6.88+0.44) 7820.17: basic grep -i 'm\(ú\|u\)lt.b\(æ\|y\)te' 0.66(2.67+0.44) 7820.18: extended grep -i 'm(ú|u)lt.b(æ|y)te' 0.66(2.67+0.43) 7820.19: perl grep -i 'm(ú|u)lt.b(æ|y)te' 0.59(2.31+0.37) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/perf/p7820-grep-engines.sh | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 t/perf/p7820-grep-engines.sh diff --git a/t/perf/p7820-grep-engines.sh b/t/perf/p7820-grep-engines.sh new file mode 100755 index 0000000000..62aba19e76 --- /dev/null +++ b/t/perf/p7820-grep-engines.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +test_description="Comparison of git-grep's regex engines + +Set GIT_PERF_7820_GREP_OPTS in the environment to pass options to +git-grep. Make sure to include a leading space, +e.g. GIT_PERF_7820_GREP_OPTS=' -i'. Some options to try: + + -i + -w + -v + -vi + -vw + -viw +" + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +for pattern in \ + 'how.to' \ + '^how to' \ + '[how] to' \ + '\(e.t[^ ]*\|v.ry\) rare' \ + 'm\(ú\|u\)lt.b\(æ\|y\)te' +do + for engine in basic extended perl + do + if test $engine != "basic" + then + # Poor man's basic -> extended converter. + pattern=$(echo "$pattern" | sed 's/\\//g') + fi + if test $engine = "perl" && ! test_have_prereq PCRE + then + prereq="PCRE" + else + prereq="" + fi + test_perf $prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" " + git -c grep.patternType=$engine grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine' || : + " + done + + test_expect_success "assert that all engines found the same for$GIT_PERF_7820_GREP_OPTS '$pattern'" ' + test_cmp out.basic out.extended && + if test_have_prereq PCRE + then + test_cmp out.basic out.perl + fi + ' +done + +test_done From bc22d813709213e8c4e40f06f564ab5454216f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:22 +0000 Subject: [PATCH 367/566] perf: add a comparison test of grep regex engines with -F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a performance comparison test of grep regex engines given fixed strings. The current logic in compile_regexp() ignores the engine parameter and uses kwset() to search for these, so this test shows no difference between engines right now: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux ./run p7821-grep-engines-fixed.sh [...] Test this tree ------------------------------------------------ 7821.1: fixed grep int 0.56(1.67+0.68) 7821.2: basic grep int 0.57(1.70+0.57) 7821.3: extended grep int 0.59(1.76+0.51) 7821.4: perl grep int 1.08(1.71+0.55) 7821.6: fixed grep uncommon 0.23(0.55+0.50) 7821.7: basic grep uncommon 0.24(0.55+0.50) 7821.8: extended grep uncommon 0.26(0.55+0.52) 7821.9: perl grep uncommon 0.24(0.58+0.47) 7821.11: fixed grep æ 0.36(1.30+0.42) 7821.12: basic grep æ 0.36(1.32+0.40) 7821.13: extended grep æ 0.38(1.30+0.42) 7821.14: perl grep æ 0.35(1.24+0.48) Only when run with -i via GIT_PERF_7821_GREP_OPTS=' -i' do we avoid avoid going through the same kwset.[ch] codepath, see the "Even when -F..." comment in grep.c. This only kicks for the non-ASCII case: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_7821_GREP_OPTS=' -i' ./run p7821-grep-engines-fixed.sh [...] Test this tree --------------------------------------------------- 7821.1: fixed grep -i int 0.62(2.10+0.57) 7821.2: basic grep -i int 0.68(1.90+0.61) 7821.3: extended grep -i int 0.78(1.94+0.57) 7821.4: perl grep -i int 0.98(1.78+0.74) 7821.6: fixed grep -i uncommon 0.24(0.44+0.64) 7821.7: basic grep -i uncommon 0.25(0.56+0.54) 7821.8: extended grep -i uncommon 0.27(0.62+0.45) 7821.9: perl grep -i uncommon 0.24(0.59+0.49) 7821.11: fixed grep -i æ 0.30(0.96+0.39) 7821.12: basic grep -i æ 0.27(0.92+0.44) 7821.13: extended grep -i æ 0.28(0.90+0.46) 7821.14: perl grep -i æ 0.28(0.74+0.49) I'm planning to change how fixed-string searching happens. This test gives a baseline for comparing performance before & after any such change. See commit ("perf: add a comparison test of grep regex engines", 2017-04-19) for details on the machine the above test run was executed on. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/perf/p7821-grep-engines-fixed.sh | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100755 t/perf/p7821-grep-engines-fixed.sh diff --git a/t/perf/p7821-grep-engines-fixed.sh b/t/perf/p7821-grep-engines-fixed.sh new file mode 100755 index 0000000000..c7ef1e198f --- /dev/null +++ b/t/perf/p7821-grep-engines-fixed.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +test_description="Comparison of git-grep's regex engines with -F + +Set GIT_PERF_7821_GREP_OPTS in the environment to pass options to +git-grep. Make sure to include a leading space, +e.g. GIT_PERF_7821_GREP_OPTS=' -w'. See p7820-grep-engines.sh for more +options to try. +" + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +for pattern in 'int' 'uncommon' 'æ' +do + for engine in fixed basic extended perl + do + if test $engine = "perl" && ! test_have_prereq PCRE + then + prereq="PCRE" + else + prereq="" + fi + test_perf $prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern" " + git -c grep.patternType=$engine grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine' || : + " + done + + test_expect_success "assert that all engines found the same for$GIT_PERF_7821_GREP_OPTS $pattern" ' + test_cmp out.fixed out.basic && + test_cmp out.fixed out.extended && + if test_have_prereq PCRE + then + test_cmp out.fixed out.perl + fi + ' +done + +test_done From c8f39be67ee3417c3070f435aa3bf6044bc8e53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:23 +0000 Subject: [PATCH 368/566] perf: add a comparison test of log --grep regex engines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a very basic performance comparison test comparing the POSIX basic, extended and perl engines with patterns matching log messages via --grep=. $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux ./run p4220-log-grep-engines.sh [...] Test this tree --------------------------------------------------------------------- 4220.1: basic log --grep='how.to' 6.22(6.00+0.21) 4220.2: extended log --grep='how.to' 6.23(5.98+0.23) 4220.3: perl log --grep='how.to' 6.07(5.79+0.25) 4220.5: basic log --grep='^how to' 6.19(5.93+0.22) 4220.6: extended log --grep='^how to' 6.19(5.93+0.23) 4220.7: perl log --grep='^how to' 6.14(5.88+0.24) 4220.9: basic log --grep='[how] to' 6.96(6.65+0.28) 4220.10: extended log --grep='[how] to' 6.96(6.69+0.24) 4220.11: perl log --grep='[how] to' 6.95(6.58+0.33) 4220.13: basic log --grep='\(e.t[^ ]*\|v.ry\) rare' 7.10(6.80+0.27) 4220.14: extended log --grep='(e.t[^ ]*|v.ry) rare' 7.07(6.80+0.26) 4220.15: perl log --grep='(e.t[^ ]*|v.ry) rare' 7.70(7.46+0.22) 4220.17: basic log --grep='m\(ú\|u\)lt.b\(æ\|y\)te' 6.12(5.87+0.24) 4220.18: extended log --grep='m(ú|u)lt.b(æ|y)te' 6.14(5.84+0.26) 4220.19: perl log --grep='m(ú|u)lt.b(æ|y)te' 6.16(5.93+0.20) With -i: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_4220_LOG_OPTS=' -i' ./run p4220-log-grep-engines.sh [...] Test this tree ------------------------------------------------------------------------ 4220.1: basic log -i --grep='how.to' 6.74(6.41+0.32) 4220.2: extended log -i --grep='how.to' 6.78(6.55+0.22) 4220.3: perl log -i --grep='how.to' 6.06(5.77+0.28) 4220.5: basic log -i --grep='^how to' 6.80(6.57+0.22) 4220.6: extended log -i --grep='^how to' 6.83(6.52+0.29) 4220.7: perl log -i --grep='^how to' 6.16(5.94+0.20) 4220.9: basic log -i --grep='[how] to' 7.87(7.61+0.24) 4220.10: extended log -i --grep='[how] to' 7.85(7.57+0.27) 4220.11: perl log -i --grep='[how] to' 7.03(6.75+0.25) 4220.13: basic log -i --grep='\(e.t[^ ]*\|v.ry\) rare' 8.68(8.41+0.25) 4220.14: extended log -i --grep='(e.t[^ ]*|v.ry) rare' 8.80(8.44+0.28) 4220.15: perl log -i --grep='(e.t[^ ]*|v.ry) rare' 7.85(7.56+0.26) 4220.17: basic log -i --grep='m\(ú\|u\)lt.b\(æ\|y\)te' 6.94(6.68+0.24) 4220.18: extended log -i --grep='m(ú|u)lt.b(æ|y)te' 7.04(6.76+0.24) 4220.19: perl log -i --grep='m(ú|u)lt.b(æ|y)te' 6.26(5.92+0.29) See commit ("perf: add a comparison test of grep regex engines", 2017-04-19) for details on the machine the above test run was executed on. Before commit ("log: make --regexp-ignore-case work with --perl-regexp", 2017-05-20) this test will almost definitely fail (depending on the repo) if passed the -i option, since it wasn't properly supported under PCRE. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/perf/p4220-log-grep-engines.sh | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 t/perf/p4220-log-grep-engines.sh diff --git a/t/perf/p4220-log-grep-engines.sh b/t/perf/p4220-log-grep-engines.sh new file mode 100755 index 0000000000..2bc47ded4d --- /dev/null +++ b/t/perf/p4220-log-grep-engines.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +test_description="Comparison of git-log's --grep regex engines + +Set GIT_PERF_4220_LOG_OPTS in the environment to pass options to +git-grep. Make sure to include a leading space, +e.g. GIT_PERF_4220_LOG_OPTS=' -i'. Some options to try: + + -i + --invert-grep + -i --invert-grep +" + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +for pattern in \ + 'how.to' \ + '^how to' \ + '[how] to' \ + '\(e.t[^ ]*\|v.ry\) rare' \ + 'm\(ú\|u\)lt.b\(æ\|y\)te' +do + for engine in basic extended perl + do + if test $engine != "basic" + then + # Poor man's basic -> extended converter. + pattern=$(echo $pattern | sed 's/\\//g') + fi + if test $engine = "perl" && ! test_have_prereq PCRE + then + prereq="PCRE" + else + prereq="" + fi + test_perf $prereq "$engine log$GIT_PERF_4220_LOG_OPTS --grep='$pattern'" " + git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4220_LOG_OPTS --grep='$pattern' >'out.$engine' || : + " + done + + test_expect_success "assert that all engines found the same for$GIT_PERF_4220_LOG_OPTS '$pattern'" ' + test_cmp out.basic out.extended && + if test_have_prereq PCRE + then + test_cmp out.basic out.perl + fi + ' +done + +test_done From 723fc5a6e15326b78b554407e3058eb345ae6cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:24 +0000 Subject: [PATCH 369/566] perf: add a comparison test of log --grep regex engines with -F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a performance comparison test of log --grepgrep regex engines given fixed strings. See the preceding fixed-string t/perf change ("perf: add a comparison test of grep regex engines with -F", 2017-04-21) for notes about this, in particular this mostly tests exactly the same codepath now, but might not in the future: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux ./run p4221-log-grep-engines-fixed.sh [...] Test this tree -------------------------------------------------------- 4221.1: fixed log --grep='int' 5.99(5.55+0.40) 4221.2: basic log --grep='int' 5.92(5.56+0.31) 4221.3: extended log --grep='int' 6.01(5.51+0.45) 4221.4: perl log --grep='int' 5.99(5.56+0.38) 4221.6: fixed log --grep='uncommon' 5.06(4.76+0.27) 4221.7: basic log --grep='uncommon' 5.02(4.78+0.21) 4221.8: extended log --grep='uncommon' 4.99(4.78+0.20) 4221.9: perl log --grep='uncommon' 5.00(4.72+0.26) 4221.11: fixed log --grep='æ' 5.35(5.12+0.20) 4221.12: basic log --grep='æ' 5.34(5.11+0.20) 4221.13: extended log --grep='æ' 5.39(5.10+0.22) 4221.14: perl log --grep='æ' 5.44(5.16+0.23) Only the non-ASCII -i case is different: $ GIT_PERF_REPEAT_COUNT=10 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_4221_LOG_OPTS=' -i' ./run p4221-log-grep-engines-fixed.sh [...] Test this tree ----------------------------------------------------------- 4221.1: fixed log -i --grep='int' 6.17(5.77+0.35) 4221.2: basic log -i --grep='int' 6.16(5.59+0.39) 4221.3: extended log -i --grep='int' 6.15(5.70+0.39) 4221.4: perl log -i --grep='int' 6.15(5.69+0.38) 4221.6: fixed log -i --grep='uncommon' 5.10(4.88+0.21) 4221.7: basic log -i --grep='uncommon' 5.04(4.76+0.25) 4221.8: extended log -i --grep='uncommon' 5.07(4.82+0.23) 4221.9: perl log -i --grep='uncommon' 5.03(4.78+0.22) 4221.11: fixed log -i --grep='æ' 5.93(5.65+0.25) 4221.12: basic log -i --grep='æ' 5.88(5.62+0.25) 4221.13: extended log -i --grep='æ' 6.02(5.69+0.29) 4221.14: perl log -i --grep='æ' 5.36(5.06+0.29) See commit ("perf: add a comparison test of grep regex engines", 2017-04-19) for details on the machine the above test run was executed on. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/perf/p4221-log-grep-engines-fixed.sh | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100755 t/perf/p4221-log-grep-engines-fixed.sh diff --git a/t/perf/p4221-log-grep-engines-fixed.sh b/t/perf/p4221-log-grep-engines-fixed.sh new file mode 100755 index 0000000000..060971265a --- /dev/null +++ b/t/perf/p4221-log-grep-engines-fixed.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +test_description="Comparison of git-log's --grep regex engines with -F + +Set GIT_PERF_4221_LOG_OPTS in the environment to pass options to +git-grep. Make sure to include a leading space, +e.g. GIT_PERF_4221_LOG_OPTS=' -i'. Some options to try: + + -i + --invert-grep + -i --invert-grep +" + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +for pattern in 'int' 'uncommon' 'æ' +do + for engine in fixed basic extended perl + do + if test $engine = "perl" && ! test_have_prereq PCRE + then + prereq="PCRE" + else + prereq="" + fi + test_perf $prereq "$engine log$GIT_PERF_4221_LOG_OPTS --grep='$pattern'" " + git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4221_LOG_OPTS --grep='$pattern' >'out.$engine' || : + " + done + + test_expect_success "assert that all engines found the same for$GIT_PERF_4221_LOG_OPTS '$pattern'" ' + test_cmp out.fixed out.basic && + test_cmp out.fixed out.extended && + if test_have_prereq PCRE + then + test_cmp out.fixed out.perl + fi + ' +done + +test_done From 374166cb381eef5498ac41f1f2835163ac656bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:25 +0000 Subject: [PATCH 370/566] grep: catch a missing enum in switch statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a die(...) to a default case for the switch statement selecting between grep pattern types under --recurse-submodules. Normally this would be caught by -Wswitch, but the grep_pattern_type type is converted to int by going through parse_options(). Changing the argument type passed to compile_submodule_options() won't work, the value will just get coerced. The -Wswitch-default warning will warn about it, but that produces a lot of noise across the codebase, this potential issue would be drowned in that noise. Thus catching this at runtime is the least bad option. This won't ever trigger in practice, but if a new pattern type were to be added this catches an otherwise silent bug during development. See commit 0281e487fd ("grep: optionally recurse into submodules", 2016-12-16) for the initial addition of this code. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/grep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/grep.c b/builtin/grep.c index 3ffb5b4e81..a191e2976b 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -495,6 +495,8 @@ static void compile_submodule_options(const struct grep_opt *opt, break; case GREP_PATTERN_TYPE_UNSPECIFIED: break; + default: + die("BUG: Added a new grep pattern type without updating switch statement"); } for (pattern = opt->pattern_list; pattern != NULL; From e0b9f8ae09a47b59e0a658d35a3430d9c98fd177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:26 +0000 Subject: [PATCH 371/566] grep: remove redundant regflags assignments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove redundant assignments to the "regflags" variable. This variable is only used set under GREP_PATTERN_TYPE_ERE, so there's no need to un-set it under GREP_PATTERN_TYPE_{FIXED,BRE,PCRE}. Back in 5010cb5fcc[1], we did do "opt.regflags &= ~REG_EXTENDED" upon seeing "-G" on the command line and flipped the bit on upon seeing "-E", but I think that was perfectly sensible and it would have been a bug if we didn't. They were part of the command line parsing that could have seen "-E" on the command line earlier. When cca2c172 ("git-grep: do not die upon -F/-P when grep.extendedRegexp is set.", 2011-05-09) switched the command line parsing to "read into a 'tentatively this is what we saw the last' variable and then finally commit just once", we didn't touch opt.regflags for PCRE and FIXED, but we still had to flip regflags between BRE and ERE, because parsing of grep.extendedregexp configuration variable directly touched opt.regflags back then, which was done by b22520a3 ("grep: allow -E and -n to be turned on by default via configuration", 2011-03-30). When 84befcd0 ("grep: add a grep.patternType configuration setting", 2012-08-03) introduced extended_regexp_option field, we stopped flipping regflags while reading the configuration, and that was when we should have noticed and stopped dropping REG_EXTENDED bit in the "now we can commit what type to use" helper function. There is no reason to do this anymore, so stop doing it, more to reduce "wait this is used under fixed/BRE/PCRE how?" confusion when reading the code, than to to save ourselves trivial CPU cycles by removing one assignment. 1. "built-in "git grep"", 2006-04-30. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/grep.c b/grep.c index 47cee45067..bf6c2494fd 100644 --- a/grep.c +++ b/grep.c @@ -179,7 +179,6 @@ static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, st case GREP_PATTERN_TYPE_BRE: opt->fixed = 0; opt->pcre = 0; - opt->regflags &= ~REG_EXTENDED; break; case GREP_PATTERN_TYPE_ERE: @@ -191,13 +190,11 @@ static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, st case GREP_PATTERN_TYPE_FIXED: opt->fixed = 1; opt->pcre = 0; - opt->regflags &= ~REG_EXTENDED; break; case GREP_PATTERN_TYPE_PCRE: opt->fixed = 0; opt->pcre = 1; - opt->regflags &= ~REG_EXTENDED; break; } } @@ -415,10 +412,9 @@ static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt) { struct strbuf sb = STRBUF_INIT; int err; - int regflags; + int regflags = opt->regflags; basic_regex_quote_buf(&sb, p->pattern); - regflags = opt->regflags & ~REG_EXTENDED; if (opt->ignore_case) regflags |= REG_ICASE; err = regcomp(&p->regexp, sb.buf, regflags); From 219e65b65ca0f1352f5fbd4233520b6fc5be3726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:27 +0000 Subject: [PATCH 372/566] grep: factor test for \0 in grep patterns into a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor the test for \0 in grep patterns into a function. Since commit 9eceddeec6 ("Use kwset in grep", 2011-08-21) any pattern containing a \0 is considered fixed as regcomp() can't handle it. This change makes later changes that make use of either has_null() or is_fixed() (but not both) smaller. While I'm at it make the comment conform to the style guide, i.e. add an opening "/*\n". Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/grep.c b/grep.c index bf6c2494fd..18306bc605 100644 --- a/grep.c +++ b/grep.c @@ -321,6 +321,18 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p, die("%s'%s': %s", where, p->pattern, error); } +static int has_null(const char *s, size_t len) +{ + /* + * regcomp cannot accept patterns with NULs so when using it + * we consider any pattern containing a NUL fixed. + */ + if (memchr(s, 0, len)) + return 1; + + return 0; +} + #ifdef USE_LIBPCRE static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) { @@ -394,12 +406,6 @@ static int is_fixed(const char *s, size_t len) { size_t i; - /* regcomp cannot accept patterns with NULs so we - * consider any pattern containing a NUL fixed. - */ - if (memchr(s, 0, len)) - return 1; - for (i = 0; i < len; i++) { if (is_regex_special(s[i])) return 0; @@ -451,7 +457,9 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) * simple string match using kws. p->fixed tells us if we * want to use kws. */ - if (opt->fixed || is_fixed(p->pattern, p->patternlen)) + if (opt->fixed || + has_null(p->pattern, p->patternlen) || + is_fixed(p->pattern, p->patternlen)) p->fixed = !icase || ascii_only; else p->fixed = 0; From 3485bea1578986eab80ff1d4fa480d8d1aa224fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:28 +0000 Subject: [PATCH 373/566] grep: change the internal PCRE macro names to be PCRE1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the internal USE_LIBPCRE define, & build options flag to use a naming convention ending in PCRE1, without changing the long-standing USE_LIBPCRE Makefile flag which enables this code. This is for preparation for libpcre2 support where having things like USE_LIBPCRE and USE_LIBPCRE2 in any more places than we absolutely need to for backwards compatibility with old Makefile arguments would be confusing. In some ways it would be better to change everything that now uses USE_LIBPCRE to use USE_LIBPCRE1, and to make specifying USE_LIBPCRE (or --with-pcre) an error. This would impose a one-time burden on packagers of git to s/USE_LIBPCRE/USE_LIBPCRE1/ in their build scripts. However I'd like to leave the door open to making USE_LIBPCRE=YesPlease eventually mean USE_LIBPCRE2=YesPlease, i.e. once PCRE v2 is ubiquitous enough that it makes sense to make it the default. This code and the USE_LIBPCRE Makefile argument was added in commit 63e7e9d8b6 ("git-grep: Learn PCRE", 2011-05-09). At the time there was no indication that the PCRE project would release an entirely new & incompatible API around 3 years later. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 4 ++-- grep.c | 6 +++--- grep.h | 2 +- t/test-lib.sh | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index d1587452f1..374fbc7e58 100644 --- a/Makefile +++ b/Makefile @@ -1088,7 +1088,7 @@ ifdef NO_LIBGEN_H endif ifdef USE_LIBPCRE - BASIC_CFLAGS += -DUSE_LIBPCRE + BASIC_CFLAGS += -DUSE_LIBPCRE1 ifdef LIBPCREDIR BASIC_CFLAGS += -I$(LIBPCREDIR)/include EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib) @@ -2240,7 +2240,7 @@ GIT-BUILD-OPTIONS: FORCE @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+ @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+ @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+ - @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ + @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ diff --git a/grep.c b/grep.c index 18306bc605..7a3858a1c3 100644 --- a/grep.c +++ b/grep.c @@ -333,7 +333,7 @@ static int has_null(const char *s, size_t len) return 0; } -#ifdef USE_LIBPCRE +#ifdef USE_LIBPCRE1 static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) { const char *error; @@ -385,7 +385,7 @@ static void free_pcre_regexp(struct grep_pat *p) pcre_free(p->pcre_extra_info); pcre_free((void *)p->pcre_tables); } -#else /* !USE_LIBPCRE */ +#else /* !USE_LIBPCRE1 */ static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) { die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE"); @@ -400,7 +400,7 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol, static void free_pcre_regexp(struct grep_pat *p) { } -#endif /* !USE_LIBPCRE */ +#endif /* !USE_LIBPCRE1 */ static int is_fixed(const char *s, size_t len) { diff --git a/grep.h b/grep.h index 267534ca24..073b0e4c92 100644 --- a/grep.h +++ b/grep.h @@ -1,7 +1,7 @@ #ifndef GREP_H #define GREP_H #include "color.h" -#ifdef USE_LIBPCRE +#ifdef USE_LIBPCRE1 #include #else typedef int pcre; diff --git a/t/test-lib.sh b/t/test-lib.sh index 6abf1d1918..e5cfbcc36b 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1010,7 +1010,7 @@ esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PYTHON" && test_set_prereq PYTHON -test -n "$USE_LIBPCRE" && test_set_prereq PCRE +test -n "$USE_LIBPCRE1" && test_set_prereq PCRE test -z "$NO_GETTEXT" && test_set_prereq GETTEXT # Can we rely on git's output in the C locale? From 6d4b5747f06754efd467d3dde32367f27615fa7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:29 +0000 Subject: [PATCH 374/566] grep: change internal *pcre* variable & function names to be *pcre1* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the internal PCRE variable & function names to have a "1" suffix. This is for preparation for libpcre2 support, where having non-versioned names would be confusing. An earlier change in this series ("grep: change the internal PCRE macro names to be PCRE1", 2017-04-07) elaborates on the motivations behind this change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 52 ++++++++++++++++++++++++++-------------------------- grep.h | 8 ++++---- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/grep.c b/grep.c index 7a3858a1c3..2d54baeaa3 100644 --- a/grep.c +++ b/grep.c @@ -178,23 +178,23 @@ static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, st case GREP_PATTERN_TYPE_BRE: opt->fixed = 0; - opt->pcre = 0; + opt->pcre1 = 0; break; case GREP_PATTERN_TYPE_ERE: opt->fixed = 0; - opt->pcre = 0; + opt->pcre1 = 0; opt->regflags |= REG_EXTENDED; break; case GREP_PATTERN_TYPE_FIXED: opt->fixed = 1; - opt->pcre = 0; + opt->pcre1 = 0; break; case GREP_PATTERN_TYPE_PCRE: opt->fixed = 0; - opt->pcre = 1; + opt->pcre1 = 1; break; } } @@ -334,7 +334,7 @@ static int has_null(const char *s, size_t len) } #ifdef USE_LIBPCRE1 -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) { const char *error; int erroffset; @@ -342,23 +342,23 @@ static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) if (opt->ignore_case) { if (has_non_ascii(p->pattern)) - p->pcre_tables = pcre_maketables(); + p->pcre1_tables = pcre_maketables(); options |= PCRE_CASELESS; } if (is_utf8_locale() && has_non_ascii(p->pattern)) options |= PCRE_UTF8; - p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset, - p->pcre_tables); - if (!p->pcre_regexp) + p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset, + p->pcre1_tables); + if (!p->pcre1_regexp) compile_regexp_failed(p, error); - p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error); - if (!p->pcre_extra_info && error) + p->pcre1_extra_info = pcre_study(p->pcre1_regexp, 0, &error); + if (!p->pcre1_extra_info && error) die("%s", error); } -static int pcrematch(struct grep_pat *p, const char *line, const char *eol, +static int pcre1match(struct grep_pat *p, const char *line, const char *eol, regmatch_t *match, int eflags) { int ovector[30], ret, flags = 0; @@ -366,7 +366,7 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol, if (eflags & REG_NOTBOL) flags |= PCRE_NOTBOL; - ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line, + ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, eol - line, 0, flags, ovector, ARRAY_SIZE(ovector)); if (ret < 0 && ret != PCRE_ERROR_NOMATCH) die("pcre_exec failed with error code %d", ret); @@ -379,25 +379,25 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol, return ret; } -static void free_pcre_regexp(struct grep_pat *p) +static void free_pcre1_regexp(struct grep_pat *p) { - pcre_free(p->pcre_regexp); - pcre_free(p->pcre_extra_info); - pcre_free((void *)p->pcre_tables); + pcre_free(p->pcre1_regexp); + pcre_free(p->pcre1_extra_info); + pcre_free((void *)p->pcre1_tables); } #else /* !USE_LIBPCRE1 */ -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) { die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE"); } -static int pcrematch(struct grep_pat *p, const char *line, const char *eol, +static int pcre1match(struct grep_pat *p, const char *line, const char *eol, regmatch_t *match, int eflags) { return 1; } -static void free_pcre_regexp(struct grep_pat *p) +static void free_pcre1_regexp(struct grep_pat *p) { } #endif /* !USE_LIBPCRE1 */ @@ -479,8 +479,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) return; } - if (opt->pcre) { - compile_pcre_regexp(p, opt); + if (opt->pcre1) { + compile_pcre1_regexp(p, opt); return; } @@ -836,8 +836,8 @@ void free_grep_patterns(struct grep_opt *opt) case GREP_PATTERN_BODY: if (p->kws) kwsfree(p->kws); - else if (p->pcre_regexp) - free_pcre_regexp(p); + else if (p->pcre1_regexp) + free_pcre1_regexp(p); else regfree(&p->regexp); free(p->pattern); @@ -916,8 +916,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol, if (p->fixed) hit = !fixmatch(p, line, eol, match); - else if (p->pcre_regexp) - hit = !pcrematch(p, line, eol, match, eflags); + else if (p->pcre1_regexp) + hit = !pcre1match(p, line, eol, match, eflags); else hit = !regexec_buf(&p->regexp, line, eol - line, 1, match, eflags); diff --git a/grep.h b/grep.h index 073b0e4c92..38ac82b638 100644 --- a/grep.h +++ b/grep.h @@ -46,9 +46,9 @@ struct grep_pat { size_t patternlen; enum grep_header_field field; regex_t regexp; - pcre *pcre_regexp; - pcre_extra *pcre_extra_info; - const unsigned char *pcre_tables; + pcre *pcre1_regexp; + pcre_extra *pcre1_extra_info; + const unsigned char *pcre1_tables; kwset_t kws; unsigned fixed:1; unsigned ignore_case:1; @@ -111,7 +111,7 @@ struct grep_opt { int allow_textconv; int extended; int use_reflog_filter; - int pcre; + int pcre1; int relative; int pathname; int null_following_name; From 543f1c0cb059ca41e23ee1ee1c12f812b135b01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:30 +0000 Subject: [PATCH 375/566] grep: move is_fixed() earlier to avoid forward declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the is_fixed() function which are currently only used in compile_regexp() earlier so it can be used in the PCRE family of functions in a later change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/grep.c b/grep.c index 2d54baeaa3..d03d424e5c 100644 --- a/grep.c +++ b/grep.c @@ -321,6 +321,18 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p, die("%s'%s': %s", where, p->pattern, error); } +static int is_fixed(const char *s, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (is_regex_special(s[i])) + return 0; + } + + return 1; +} + static int has_null(const char *s, size_t len) { /* @@ -402,18 +414,6 @@ static void free_pcre1_regexp(struct grep_pat *p) } #endif /* !USE_LIBPCRE1 */ -static int is_fixed(const char *s, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (is_regex_special(s[i])) - return 0; - } - - return 1; -} - static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt) { struct strbuf sb = STRBUF_INIT; From 68c7d2761dd78173d311fbe4fa5f0f764a75e4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:31 +0000 Subject: [PATCH 376/566] test-lib: add a PTHREADS prerequisite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a PTHREADS prerequisite which is false when git is compiled with NO_PTHREADS=YesPlease. There's lots of custom code that runs when threading isn't available, but before this prerequisite there was no way to test it. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 1 + t/README | 4 ++++ t/test-lib.sh | 1 + 3 files changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 374fbc7e58..a79274e5e6 100644 --- a/Makefile +++ b/Makefile @@ -2242,6 +2242,7 @@ GIT-BUILD-OPTIONS: FORCE @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+ @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ + @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+ @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ diff --git a/t/README b/t/README index a90cb62583..2f95860369 100644 --- a/t/README +++ b/t/README @@ -817,6 +817,10 @@ use these, and "test_set_prereq" for how to define your own. Test is run on a filesystem which converts decomposed utf-8 (nfd) to precomposed utf-8 (nfc). + - PTHREADS + + Git wasn't compiled with NO_PTHREADS=YesPlease. + Tips for Writing Tests ---------------------- diff --git a/t/test-lib.sh b/t/test-lib.sh index e5cfbcc36b..ab92c0ebaa 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1009,6 +1009,7 @@ esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL +test -z "$NO_PTHREADS" && test_set_prereq PTHREADS test -z "$NO_PYTHON" && test_set_prereq PYTHON test -n "$USE_LIBPCRE1" && test_set_prereq PCRE test -z "$NO_GETTEXT" && test_set_prereq GETTEXT From 967a3eaf432db124cabaa4595677063d5b931ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:32 +0000 Subject: [PATCH 377/566] pack-objects & index-pack: add test for --threads warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test for the warning that's emitted when --threads or pack.threads is provided under NO_PTHREADS=YesPlease. This uses the new PTHREADS prerequisite. The assertion for C_LOCALE_OUTPUT in the latter test is currently redundant, since unlike index-pack the pack-objects warnings aren't i18n'd. However they might be changed to be i18n'd in the future, and there's no harm in future-proofing the test. There's an existing bug in the implementation of pack-objects which this test currently tests for as-is. Details about the bug & the fix are included in a follow-up change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t5300-pack-object.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 43a672c345..6ed23ee1d2 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -421,6 +421,42 @@ test_expect_success 'index-pack works in non-repo' ' test_path_is_file foo.idx ' +test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.threads=N warns when no pthreads' ' + test_must_fail git index-pack --threads=2 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep -F "no threads support, ignoring --threads=2" err && + + test_must_fail git -c pack.threads=2 index-pack 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep -F "no threads support, ignoring pack.threads" err && + + test_must_fail git -c pack.threads=2 index-pack --threads=4 2>err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep -F "no threads support, ignoring --threads=4" err && + grep -F "no threads support, ignoring pack.threads" err +' + +test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack.threads=N warns when no pthreads' ' + git pack-objects --threads=2 --stdout --all /dev/null 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep -F "no threads support, ignoring --threads" err && + + git -c pack.threads=2 pack-objects --stdout --all /dev/null 2>err && + grep ^warning: err >warnings && + test_must_fail test_line_count = 1 warnings && + grep -F "no threads support, ignoring pack.threads" err && + + git -c pack.threads=2 pack-objects --threads=4 --stdout --all /dev/null 2>err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep -F "no threads support, ignoring --threads" err && + grep -F "no threads support, ignoring pack.threads" err +' + # # WARNING! # From 2e96d8154fc072b4778650560acca31a79ef86b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:33 +0000 Subject: [PATCH 378/566] pack-objects: fix buggy warning about threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a buggy warning about threads under NO_PTHREADS=YesPlease. Due to re-using the delta_search_threads variable for both the state of the "pack.threads" config & the --threads option, setting "pack.threads" but not supplying --threads would trigger the warning for both "pack.threads" & --threads. Solve this bug by resetting the delta_search_threads variable in git_pack_config(), it might then be set by --threads again and be subsequently warned about, as the test I'm changing here asserts. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 4 +++- t/t5300-pack-object.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0fe35d1b5a..f1baf05dfe 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2472,8 +2472,10 @@ static int git_pack_config(const char *k, const char *v, void *cb) die("invalid number of threads specified (%d)", delta_search_threads); #ifdef NO_PTHREADS - if (delta_search_threads != 1) + if (delta_search_threads != 1) { warning("no threads support, ignoring %s", k); + delta_search_threads = 0; + } #endif return 0; } diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 6ed23ee1d2..9c68b99251 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -447,7 +447,7 @@ test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack. git -c pack.threads=2 pack-objects --stdout --all /dev/null 2>err && grep ^warning: err >warnings && - test_must_fail test_line_count = 1 warnings && + test_line_count = 1 warnings && grep -F "no threads support, ignoring pack.threads" err && git -c pack.threads=2 pack-objects --threads=4 --stdout --all /dev/null 2>err && From d1edee4adac8f516f1545d080fbffcdb148ac7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:34 +0000 Subject: [PATCH 379/566] grep: given --threads with NO_PTHREADS=YesPlease, warn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a warning about missing thread support when grep.threads or --threads is set to a non 0 (default) or 1 (no parallelism) value under NO_PTHREADS=YesPlease. This is for consistency with the index-pack & pack-objects commands, which also take a --threads option & are configurable via pack.threads, and have long warned about the same under NO_PTHREADS=YesPlease. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/grep.c | 13 +++++++++++++ t/t7810-grep.sh | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/builtin/grep.c b/builtin/grep.c index a191e2976b..3c721b75a5 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -289,6 +289,17 @@ static int grep_cmd_config(const char *var, const char *value, void *cb) if (num_threads < 0) die(_("invalid number of threads specified (%d) for %s"), num_threads, var); +#ifdef NO_PTHREADS + else if (num_threads && num_threads != 1) { + /* + * TRANSLATORS: %s is the configuration + * variable for tweaking threads, currently + * grep.threads + */ + warning(_("no threads support, ignoring %s"), var); + num_threads = 0; + } +#endif } return st; @@ -1229,6 +1240,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) else if (num_threads < 0) die(_("invalid number of threads specified (%d)"), num_threads); #else + if (num_threads) + warning(_("no threads support, ignoring --threads")); num_threads = 0; #endif diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 561709ef6a..f106387820 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -791,6 +791,24 @@ do " done +test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' ' + git grep --threads=2 Hello hello_world 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep -F "no threads support, ignoring --threads" err && + git -c grep.threads=2 grep Hello hello_world 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep -F "no threads support, ignoring grep.threads" err && + git -c grep.threads=2 grep --threads=4 Hello hello_world 2>err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep -F "no threads support, ignoring --threads" err && + grep -F "no threads support, ignoring grep.threads" err && + git -c grep.threads=0 grep --threads=0 Hello hello_world 2>err && + test_line_count = 0 err +' + test_expect_success 'grep from a subdirectory to search wider area (1)' ' mkdir -p s && ( From 8df4c2953f228020438eb53a7d54470c922ce9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 19:45:35 +0000 Subject: [PATCH 380/566] grep: assert that threading is enabled when calling grep_{lock,unlock} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the grep_{lock,unlock} functions to assert that num_threads is true, instead of only locking & unlocking the pthread mutex lock when it is. These functions are never called when num_threads isn't true, this logic has gone through multiple iterations since the initial introduction of grep threading in commit 5b594f457a ("Threaded grep", 2010-01-25), but ever since then they'd only be called if num_threads was true, so this check made the code confusing to read. Replace the check with an assertion, so that it's clear to the reader that this code path is never taken unless we're spawning threads. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/grep.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/grep.c b/builtin/grep.c index 3c721b75a5..b1095362fb 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -73,14 +73,14 @@ static pthread_mutex_t grep_mutex; static inline void grep_lock(void) { - if (num_threads) - pthread_mutex_lock(&grep_mutex); + assert(num_threads); + pthread_mutex_lock(&grep_mutex); } static inline void grep_unlock(void) { - if (num_threads) - pthread_mutex_unlock(&grep_mutex); + assert(num_threads); + pthread_mutex_unlock(&grep_mutex); } /* Signalled when a new work_item is added to todo. */ From 6d423dd542f062a9696c210c33cf33e0657ab923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 20:05:22 +0000 Subject: [PATCH 381/566] grep: don't redundantly compile throwaway patterns under threading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the pattern compilation logic under threading so that grep doesn't compile a pattern it never ends up using on the non-threaded code path, only to compile it again N times for N threads which will each use their own copy, ignoring the initially compiled pattern. This redundant compilation dates back to the initial introduction of the threaded grep in commit 5b594f457a ("Threaded grep", 2010-01-25). There was never any reason for doing this redundant work other than an oversight in the initial commit. Jeff King suggested on-list in <20170414212325.fefrl3qdjigwyitd@sigill.intra.peff.net> that this might be needed to check the pattern for sanity before threaded execution commences. That's not the case. The pattern is compiled under threading in start_threads() before any concurrent execution has started by calling pthread_create(), so if the pattern contains an error we still do the right thing. I.e. die with one error before any threaded execution has commenced, instead of e.g. spewing out an error for each N threads, which could be a regression a change like this might inadvertently introduce. This change is not meant as an optimization, any performance gains from this are in the hundreds to thousands of nanoseconds at most. If we wanted more performance here we could just re-use the compiled patterns in multiple threads (regcomp(3) is thread-safe), or partially re-use them and the associated structures in the case of later PCRE JIT changes. Rather, it's just to make the code easier to reason about. It's confusing to debug this under threading & non-threading when the threading codepaths redundantly compile a pattern which is never used. The reason the patterns are recompiled is as a side-effect of duplicating the whole grep_opt structure, which is not thread safe, writable, and munged during execution. The grep_opt structure then points to the grep_pat structure where pattern or patterns are stored. I looked into e.g. splitting the API into some "do & alloc threadsafe stuff", "spawn thread", "do and alloc non-threadsafe stuff", but the execution time of grep_opt_dup() & pattern compilation is trivial compared to actually executing the grep, so there was no point. Even with the more expensive JIT changes to follow the most expensive PCRE patterns take something like 0.0X milliseconds to compile at most[1]. The undocumented --debug mode added in commit 17bf35a3c7 ("grep: teach --debug option to dump the parse tree", 2012-09-13) still works properly with this change. It only emits debugging info during pattern compilation, which is now dumped by the pattern compiled just before the first thread is started. 1. http://sljit.sourceforge.net/pcre.html Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/grep.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/builtin/grep.c b/builtin/grep.c index b1095362fb..12e62fcbf3 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -224,7 +224,8 @@ static void start_threads(struct grep_opt *opt) int err; struct grep_opt *o = grep_opt_dup(opt); o->output = strbuf_out; - o->debug = 0; + if (i) + o->debug = 0; compile_grep_patterns(o); err = pthread_create(&threads[i], NULL, run, o); @@ -1167,8 +1168,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!opt.fixed && opt.ignore_case) opt.regflags |= REG_ICASE; - compile_grep_patterns(&opt); - /* * We have to find "--" in a separate pass, because its presence * influences how we will parse arguments that come before it. @@ -1245,6 +1244,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix) num_threads = 0; #endif + if (!num_threads) + /* + * The compiled patterns on the main path are only + * used when not using threading. Otherwise + * start_threads() below calls compile_grep_patterns() + * for each thread. + */ + compile_grep_patterns(&opt); + #ifndef NO_PTHREADS if (num_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count) From 9ec726a4120bb219530faf988198a704ec7dd1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 20:05:23 +0000 Subject: [PATCH 382/566] grep: skip pthreads overhead when using one thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip the administrative overhead of using pthreads when only using one thread. Instead take the non-threaded path which would be taken under NO_PTHREADS. The threading support was initially added in commit 5b594f457a ("Threaded grep", 2010-01-25) with a hardcoded compile-time number of 8 threads. Later the number of threads was made configurable in commit 89f09dd34e ("grep: add --threads= option and grep.threads configuration", 2015-12-15). That change did not add any special handling for --threads=1. Now we take a slightly faster path by skipping thread handling entirely when 1 thread is requested. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/grep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/grep.c b/builtin/grep.c index 12e62fcbf3..bd008cb100 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1238,6 +1238,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) num_threads = GREP_NUM_THREADS_DEFAULT; else if (num_threads < 0) die(_("invalid number of threads specified (%d)"), num_threads); + if (num_threads == 1) + num_threads = 0; #else if (num_threads) warning(_("no threads support, ignoring --threads")); From 7531a2dd876994479484968d361e18b70b2c764e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 20:05:24 +0000 Subject: [PATCH 383/566] log: add -P as a synonym for --perl-regexp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a short -P option as a synonym for the longer --perl-regexp, for consistency with the options the corresponding grep invocations accept. This was intentionally omitted in commit 727b6fc3ed ("log --grep: accept --basic-regexp and --perl-regexp", 2012-10-03) for unspecified future use. Make it consistent with "grep" rather than to keep it open for future use, and to avoid the confusion of -P meaning different things for grep & log, as is the case with the -G option. As noted in the aforementioned commit the --basic-regexp option can't have a corresponding -G argument, as the log command already uses that for -G. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 1 + revision.c | 2 +- t/t4202-log.sh | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index a46f70c2b1..9c44eae55d 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -91,6 +91,7 @@ endif::git-rev-list[] Consider the limiting patterns to be fixed strings (don't interpret pattern as a regular expression). +-P:: --perl-regexp:: Consider the limiting patterns to be Perl-compatible regular expressions. diff --git a/revision.c b/revision.c index c96265d89d..dea4d841b5 100644 --- a/revision.c +++ b/revision.c @@ -1996,7 +1996,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE); } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) { revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED; - } else if (!strcmp(arg, "--perl-regexp")) { + } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) { revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE; } else if (!strcmp(arg, "--all-match")) { revs->grep_filter.all_match = 1; diff --git a/t/t4202-log.sh b/t/t4202-log.sh index dbed3efeee..2b07d1c0c2 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -404,8 +404,20 @@ test_expect_success 'log with various grep.patternType configurations & command- --grep="(1|2)" >actual.fixed.short-arg && git log --pretty=tformat:%s -E \ --grep="\|2" >actual.extended.short-arg && + if test_have_prereq PCRE + then + git log --pretty=tformat:%s -P \ + --grep="[\d]\|" >actual.perl.short-arg + else + test_must_fail git log -P \ + --grep="[\d]\|" + fi && test_cmp expect.fixed actual.fixed.short-arg && test_cmp expect.extended actual.extended.short-arg && + if test_have_prereq PCRE + then + test_cmp expect.perl actual.perl.short-arg + fi && git log --pretty=tformat:%s --fixed-strings \ --grep="(1|2)" >actual.fixed.long-arg && From fbaceaac4731655540e48845eb3a3e2ce2282b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 20:05:25 +0000 Subject: [PATCH 384/566] grep: add support for the PCRE v1 JIT API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the grep PCRE v1 code to use JIT when available. When PCRE support was initially added in commit 63e7e9d8b6 ("git-grep: Learn PCRE", 2011-05-09) PCRE had no JIT support, it was integrated into 8.20 released on 2011-10-21. Enabling JIT support usually improves performance by more than 40%. The pattern compilation times are relatively slower, but those relative numbers are tiny, and are easily made back in all but the most trivial cases of grep. Detailed benchmarks & overview of compilation times is at: http://sljit.sourceforge.net/pcre.html With this change the difference in a t/perf/p7820-grep-engines.sh run is, with just the /perl/ tests shown: $ GIT_PERF_REPEAT_COUNT=30 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_MAKE_OPTS='-j8 USE_LIBPCRE=YesPlease CC=~/perl5/installed/bin/gcc NO_R_TO_GCC_LINKER=YesPlease CFLAGS=-O3 LIBPCREDIR=/home/avar/g/pcre/inst LDFLAGS=-Wl,-rpath,/home/avar/g/pcre/inst/lib' ./run HEAD~ HEAD p7820-grep-engines.sh Test HEAD~ HEAD --------------------------------------------------------------------------------------- 7820.3: perl grep 'how.to' 0.35(1.11+0.43) 0.23(0.42+0.46) -34.3% 7820.7: perl grep '^how to' 0.64(2.71+0.36) 0.27(0.66+0.44) -57.8% 7820.11: perl grep '[how] to' 0.63(2.51+0.42) 0.33(0.98+0.39) -47.6% 7820.15: perl grep '(e.t[^ ]*|v.ry) rare' 1.17(5.61+0.35) 0.34(1.08+0.46) -70.9% 7820.19: perl grep 'm(ú|u)lt.b(æ|y)te' 0.43(1.52+0.44) 0.30(0.88+0.42) -30.2% The conditional support for JIT is implemented as suggested in the pcrejit(3) man page. E.g. defining PCRE_STUDY_JIT_COMPILE to 0 if it's not present. The implementation is relatively verbose because even if PCRE_CONFIG_JIT is defined only a call to pcre_config() can determine if the JIT is available, and if so the faster pcre_jit_exec() function should be called instead of pcre_exec(), and a different (but not complimentary!) function needs to be called to free pcre1_extra_info. There's no graceful fallback if pcre_jit_stack_alloc() fails under PCRE_CONFIG_JIT, instead the program will simply abort. I don't think this is worth handling gracefully, it'll only fail in cases where malloc() doesn't work, in which case we're screwed anyway. That there's no assignment of `p->pcre1_jit_on = 0` when PCRE_CONFIG_JIT isn't defined isn't a bug. The create_grep_pat() function allocates the grep_pat allocates it with calloc(), so it's guaranteed to be 0 when PCRE_CONFIG_JIT isn't defined. I you're bisecting and find this change, check that your PCRE isn't older than 8.32. This change intentionally broke really old versions of PCRE, but that's fixed in follow-up commits. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 40 ++++++++++++++++++++++++++++++++++++---- grep.h | 6 ++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/grep.c b/grep.c index d03d424e5c..ffe95995ee 100644 --- a/grep.c +++ b/grep.c @@ -365,9 +365,22 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) if (!p->pcre1_regexp) compile_regexp_failed(p, error); - p->pcre1_extra_info = pcre_study(p->pcre1_regexp, 0, &error); + p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error); if (!p->pcre1_extra_info && error) die("%s", error); + +#ifdef PCRE_CONFIG_JIT + pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on); + if (p->pcre1_jit_on == 1) { + p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024); + if (!p->pcre1_jit_stack) + die("Couldn't allocate PCRE JIT stack"); + pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack); + } else if (p->pcre1_jit_on != 0) { + die("BUG: The pcre1_jit_on variable should be 0 or 1, not %d", + p->pcre1_jit_on); + } +#endif } static int pcre1match(struct grep_pat *p, const char *line, const char *eol, @@ -378,8 +391,19 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol, if (eflags & REG_NOTBOL) flags |= PCRE_NOTBOL; - ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, eol - line, - 0, flags, ovector, ARRAY_SIZE(ovector)); +#ifdef PCRE_CONFIG_JIT + if (p->pcre1_jit_on) { + ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line, + eol - line, 0, flags, ovector, + ARRAY_SIZE(ovector), p->pcre1_jit_stack); + } else +#endif + { + ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, + eol - line, 0, flags, ovector, + ARRAY_SIZE(ovector)); + } + if (ret < 0 && ret != PCRE_ERROR_NOMATCH) die("pcre_exec failed with error code %d", ret); if (ret > 0) { @@ -394,7 +418,15 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol, static void free_pcre1_regexp(struct grep_pat *p) { pcre_free(p->pcre1_regexp); - pcre_free(p->pcre1_extra_info); +#ifdef PCRE_CONFIG_JIT + if (p->pcre1_jit_on) { + pcre_free_study(p->pcre1_extra_info); + pcre_jit_stack_free(p->pcre1_jit_stack); + } else +#endif + { + pcre_free(p->pcre1_extra_info); + } pcre_free((void *)p->pcre1_tables); } #else /* !USE_LIBPCRE1 */ diff --git a/grep.h b/grep.h index 38ac82b638..14f47189f9 100644 --- a/grep.h +++ b/grep.h @@ -3,9 +3,13 @@ #include "color.h" #ifdef USE_LIBPCRE1 #include +#ifndef PCRE_STUDY_JIT_COMPILE +#define PCRE_STUDY_JIT_COMPILE 0 +#endif #else typedef int pcre; typedef int pcre_extra; +typedef int pcre_jit_stack; #endif #include "kwset.h" #include "thread-utils.h" @@ -48,7 +52,9 @@ struct grep_pat { regex_t regexp; pcre *pcre1_regexp; pcre_extra *pcre1_extra_info; + pcre_jit_stack *pcre1_jit_stack; const unsigned char *pcre1_tables; + int pcre1_jit_on; kwset_t kws; unsigned fixed:1; unsigned ignore_case:1; From e87de7cab4f4e3a8ebf4cc588d26636c3d828e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 20:05:26 +0000 Subject: [PATCH 385/566] grep: un-break building with PCRE < 8.32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend my change earlier in this series ("grep: add support for the PCRE v1 JIT API", 2017-04-11) to un-break the build on PCRE v1 versions earlier than 8.32. The JIT support was added in version 8.20 released on 2011-10-21, but it wasn't until 8.32 released on 2012-11-30 that the fast code path to use the JIT via pcre_jit_exec() was added[1] (see also [2]). This means that versions 8.20 through 8.31 could still use the JIT, but supporting it on those versions would add to the already verbose macro soup around JIT support it, and I don't expect that the use-case of compiling a brand new git against a 5 year old PCRE is particularly common, and if someone does that they can just get the existing pre-JIT slow codepath. So just take the easy way out and disable the JIT on any version older than 8.32. The reason this change isn't part of the initial change PCRE JIT support is to have a cleaner history showing which parts of the implementation are only used for ancient PCRE versions. This also makes it easier to revert this change if we ever decide to stop supporting those old versions. 1. http://www.pcre.org/original/changelog.txt ("28. Introducing a native interface for JIT. Through this interface, the compiled[...]") 2. https://bugs.exim.org/show_bug.cgi?id=2121 Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 6 +++--- grep.h | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/grep.c b/grep.c index ffe95995ee..19fa67c34c 100644 --- a/grep.c +++ b/grep.c @@ -369,7 +369,7 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) if (!p->pcre1_extra_info && error) die("%s", error); -#ifdef PCRE_CONFIG_JIT +#ifdef GIT_PCRE1_USE_JIT pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on); if (p->pcre1_jit_on == 1) { p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024); @@ -391,7 +391,7 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol, if (eflags & REG_NOTBOL) flags |= PCRE_NOTBOL; -#ifdef PCRE_CONFIG_JIT +#ifdef GIT_PCRE1_USE_JIT if (p->pcre1_jit_on) { ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line, eol - line, 0, flags, ovector, @@ -418,7 +418,7 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol, static void free_pcre1_regexp(struct grep_pat *p) { pcre_free(p->pcre1_regexp); -#ifdef PCRE_CONFIG_JIT +#ifdef GIT_PCRE1_USE_JIT if (p->pcre1_jit_on) { pcre_free_study(p->pcre1_extra_info); pcre_jit_stack_free(p->pcre1_jit_stack); diff --git a/grep.h b/grep.h index 14f47189f9..3b948d9155 100644 --- a/grep.h +++ b/grep.h @@ -3,6 +3,11 @@ #include "color.h" #ifdef USE_LIBPCRE1 #include +#ifdef PCRE_CONFIG_JIT +#if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 +#define GIT_PCRE1_USE_JIT +#endif +#endif #ifndef PCRE_STUDY_JIT_COMPILE #define PCRE_STUDY_JIT_COMPILE 0 #endif From c30cf827a8a3d9e85b2459418599ae874b3e4787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 25 May 2017 20:05:27 +0000 Subject: [PATCH 386/566] grep: un-break building with PCRE < 8.20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend my change earlier in this series ("grep: add support for the PCRE v1 JIT API", 2017-04-11) to un-break the build on PCRE v1 versions earlier than 8.20. The 8.20 release was the first release to have JIT & pcre_jit_stack in the headers, so a mock type needs to be provided for it on those releases. Now git should compile with all PCRE versions that it supported before my JIT change. I've tested it as far back as version 7.5 released on 2008-01-10, once I got down to version 7.0 it wouldn't build anymore with GCC 7.1.1, and I couldn't be bothered to anything older than 7.5 as I'm confident that if the build breaks on those older versions it's not because of my JIT change. See the "un-break" change in this series ("grep: un-break building with PCRE < 8.32", 2017-05-10) for why this isn't squashed into the main PCRE JIT commit. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grep.h b/grep.h index 3b948d9155..ce90969736 100644 --- a/grep.h +++ b/grep.h @@ -11,6 +11,9 @@ #ifndef PCRE_STUDY_JIT_COMPILE #define PCRE_STUDY_JIT_COMPILE 0 #endif +#if PCRE_MAJOR <= 8 && PCRE_MINOR < 20 +typedef int pcre_jit_stack; +#endif #else typedef int pcre; typedef int pcre_extra; From ba746ff9c931000d123aff35cfa922093dc29380 Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Fri, 26 May 2017 19:36:54 +0200 Subject: [PATCH 387/566] doc: filter-branch does not require re-export of vars The function `set_ident` in `filter-branch` exported the variables GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL|DATE) at least since 6f6826c52b in 2007. Therefore the filter scripts don't need to re-eport them again. Signed-off-by: Andreas Heiduk Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 0a09698c03..a78655d913 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -86,8 +86,7 @@ OPTIONS This filter may be used if you only need to modify the environment in which the commit will be performed. Specifically, you might want to rewrite the author/committer name/email/time environment - variables (see linkgit:git-commit-tree[1] for details). Do not forget - to re-export the variables. + variables (see linkgit:git-commit-tree[1] for details). --tree-filter :: This is the filter for rewriting the tree and its contents. @@ -342,12 +341,10 @@ git filter-branch --env-filter ' if test "$GIT_AUTHOR_EMAIL" = "root@localhost" then GIT_AUTHOR_EMAIL=john@example.com - export GIT_AUTHOR_EMAIL fi if test "$GIT_COMMITTER_EMAIL" = "root@localhost" then GIT_COMMITTER_EMAIL=john@example.com - export GIT_COMMITTER_EMAIL fi ' -- --all -------------------------------------------------------- From 44e2ff09cef17eb8b25ceee3a950d88dbdd19252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Fri, 26 May 2017 20:56:57 +0000 Subject: [PATCH 388/566] wildmatch test: remove redundant duplicate test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove a test line that's exactly the same as the preceding line. This was brought in in commit feabcc173b ("Integrate wildmatch to git", 2012-10-15), these tests are originally copied from rsync.git, but the duplicate line was never present there, so must have just snuck in during integration with git by accident. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index ef509df351..7ca69f4bed 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -135,7 +135,6 @@ match 1 x '5' '[[:xdigit:]]' match 1 x 'f' '[[:xdigit:]]' match 1 x 'D' '[[:xdigit:]]' match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' -match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' match 1 x '5' '[a-c[:digit:]x-z]' match 1 x 'b' '[a-c[:digit:]x-z]' From be6ed3f3346a24f4be2d771384528b1c87952b8f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 26 May 2017 15:06:41 -0400 Subject: [PATCH 389/566] t4208: add check for ":/" without matching file The DWIM magic in check_filename() doesn't just recognize ":/". It actually makes sure that the file it points to exists. t4208 checks only the case where the path is present, not the opposite. Since the next patches will be touching this area, let's add a test to make sure it continues working. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t4208-log-magic-pathspec.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index 001343e2fc..70bc64100b 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -29,6 +29,12 @@ test_expect_success '"git log -- :/a" should not be ambiguous' ' git log -- :/a ' +# This differs from the ":/a" check above in that :/in looks like a pathspec, +# but doesn't match an actual file. +test_expect_success '"git log :/in" should not be ambiguous' ' + git log :/in +' + test_expect_success '"git log :" should be ambiguous' ' test_must_fail git log : 2>error && test_i18ngrep ambiguous error From a08cbcda1782993d83cf8763a394dab24e2c52b3 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 26 May 2017 15:07:31 -0400 Subject: [PATCH 390/566] check_filename(): refactor ":/" handling We handle arguments with the ":/" pathspec magic specially, making sure the name exists at the top-level. We'll want to handle more pathspec magic in future patches, so let's do a little rearranging to make that easier. Instead of relying on an if/else cascade to avoid the prefix_filename() call, we'll just set prefix to NULL. Likewise, we'll get rid of the "name" variable entirely, and just push the "arg" pointer forward to skip past the magic. That means by the time we get to the prefix-handling, we're set up appropriately whether we saw ":/" or not. Note that this does impact the final error message we produce when stat() fails, as it shows "arg" (which we'll have modified to skip magic and include the prefix). This is a good thing; the original message would say something like "failed to stat ':/foo'", which is confusing (we tried to stat "foo"). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- setup.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/setup.c b/setup.c index 0309c27821..000ffa810e 100644 --- a/setup.c +++ b/setup.c @@ -134,19 +134,20 @@ int path_inside_repo(const char *prefix, const char *path) int check_filename(const char *prefix, const char *arg) { - const char *name; char *to_free = NULL; struct stat st; if (starts_with(arg, ":/")) { if (arg[2] == '\0') /* ":/" is root dir, always exists */ return 1; - name = arg + 2; - } else if (prefix) - name = to_free = prefix_filename(prefix, arg); - else - name = arg; - if (!lstat(name, &st)) { + arg += 2; + prefix = NULL; + } + + if (prefix) + arg = to_free = prefix_filename(prefix, arg); + + if (!lstat(arg, &st)) { free(to_free); return 1; /* file exists */ } From d51c6ee0d4bbeaf28bcd6c5f1681d208ee75763d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 26 May 2017 15:07:42 -0400 Subject: [PATCH 391/566] check_filename(): use skip_prefix This avoids some magic numbers (and we'll be adding more similar calls in a minute). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- setup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.c b/setup.c index 000ffa810e..f2a8070bd4 100644 --- a/setup.c +++ b/setup.c @@ -137,10 +137,9 @@ int check_filename(const char *prefix, const char *arg) char *to_free = NULL; struct stat st; - if (starts_with(arg, ":/")) { - if (arg[2] == '\0') /* ":/" is root dir, always exists */ + if (skip_prefix(arg, ":/", &arg)) { + if (!*arg) /* ":/" is root dir, always exists */ return 1; - arg += 2; prefix = NULL; } From 42471bcee44e87669eb17f9fa13c8041a0fc35a1 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 26 May 2017 15:08:39 -0400 Subject: [PATCH 392/566] check_filename(): handle ":^" path magic We special-case "git log :/foo" to work when "foo" exists in the working tree. But :^ (and its alias :!) do not get the same treatment, requiring the user to supply a disambiguating "--". Let's make them work without requiring the user to type the "--". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- setup.c | 4 ++++ t/t4208-log-magic-pathspec.sh | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/setup.c b/setup.c index f2a8070bd4..86bb7c9a96 100644 --- a/setup.c +++ b/setup.c @@ -141,6 +141,10 @@ int check_filename(const char *prefix, const char *arg) if (!*arg) /* ":/" is root dir, always exists */ return 1; prefix = NULL; + } else if (skip_prefix(arg, ":!", &arg) || + skip_prefix(arg, ":^", &arg)) { + if (!*arg) /* excluding everything is silly, but allowed */ + return 1; } if (prefix) diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index 70bc64100b..627890d7d8 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -52,6 +52,19 @@ test_expect_success 'git log HEAD -- :/' ' test_cmp expected actual ' +test_expect_success '"git log :^sub" is not ambiguous' ' + git log :^sub +' + +test_expect_success '"git log :^does-not-exist" does not match anything' ' + test_must_fail git log :^does-not-exist +' + +test_expect_success '"git log :!" behaves the same as :^' ' + git log :!sub && + test_must_fail git log :!does-not-exist +' + test_expect_success 'command line pathspec parsing for "git log"' ' git reset --hard && >a && From c99eddd83519811d6604b4ce4a8d2001787a30d4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 26 May 2017 15:10:31 -0400 Subject: [PATCH 393/566] verify_filename(): treat ":(magic)" as a pathspec For commands that take revisions and pathspecs, magic pathspecs like ":(exclude)foo" require the user to specify a disambiguating "--", since they do not match a file in the filesystem, like: git grep foo -- :(exclude)bar This makes them more annoying to use than they need to be. We loosened the rules for wildcards in 28fcc0b71 (pathspec: avoid the need of "--" when wildcard is used, 2015-05-02). Let's do the same for pathspecs with long-form magic. We already handle the short-forms ":/" and ":^" specially in check_filename(), so we don't need to handle them here. And in fact, we could do the same with long-form magic, parsing out the actual filename and making sure it exists. But there are a few reasons not to do it that way: - the parsing gets much more complicated, and we'd want to hand it off to the pathspec code. But that code isn't ready to do this kind of speculative parsing (it's happy to die() when it sees a syntactically invalid pathspec). - not all pathspec magic maps to a filesystem path. E.g., :(attr) should be treated as a pathspec regardless of what is in the filesystem - we can be a bit looser with ":(" than with the short-form ":/", because it is much less likely to have a false positive. Whereas ":/" also means "search for a commit with this regex". Note that because the change is in verify_filename() and not in its helper check_filename(), this doesn't affect the verify_non_filename() case. I.e., if an item that matches our new rule doesn't resolve as an object, we may fallback to treating it as a pathspec (rather than complaining it doesn't exist). But if it does resolve (e.g., as a file in the index that starts with an open-paren), we won't then complain that it's also a valid pathspec. This matches the wildcard-exception behavior. And of course in either case, one can always insert the "--" to get more precise results. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- setup.c | 20 +++++++++++++++++++- t/t4208-log-magic-pathspec.sh | 13 +++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/setup.c b/setup.c index 86bb7c9a96..89fcc12ab7 100644 --- a/setup.c +++ b/setup.c @@ -185,6 +185,24 @@ static void NORETURN die_verify_filename(const char *prefix, } +/* + * Check for arguments that don't resolve as actual files, + * but which look sufficiently like pathspecs that we'll consider + * them such for the purposes of rev/pathspec DWIM parsing. + */ +static int looks_like_pathspec(const char *arg) +{ + /* anything with a wildcard character */ + if (!no_wildcard(arg)) + return 1; + + /* long-form pathspec magic */ + if (starts_with(arg, ":(")) + return 1; + + return 0; +} + /* * Verify a filename that we got as an argument for a pathspec * entry. Note that a filename that begins with "-" never verifies @@ -211,7 +229,7 @@ void verify_filename(const char *prefix, { if (*arg == '-') die("bad flag '%s' used after filename", arg); - if (check_filename(prefix, arg) || !no_wildcard(arg)) + if (check_filename(prefix, arg) || looks_like_pathspec(arg)) return; die_verify_filename(prefix, arg, diagnose_misspelt_rev); } diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index 627890d7d8..935df6a65c 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -65,6 +65,19 @@ test_expect_success '"git log :!" behaves the same as :^' ' test_must_fail git log :!does-not-exist ' +test_expect_success '"git log :(exclude)sub" is not ambiguous' ' + git log ":(exclude)sub" +' + +test_expect_success '"git log :(exclude)sub --" must resolve as an object' ' + test_must_fail git log ":(exclude)sub" -- +' + +test_expect_success '"git log :(unknown-magic) complains of bogus magic' ' + test_must_fail git log ":(unknown-magic)" 2>error && + test_i18ngrep pathspec.magic error +' + test_expect_success 'command line pathspec parsing for "git log"' ' git reset --hard && >a && From 2cb47ab6958192a4b4b3b0616b2ab37f6680547f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 26 May 2017 15:10:53 -0400 Subject: [PATCH 394/566] verify_filename(): flip order of checks The looks_like_pathspec() check is much cheaper than check_filename(), which actually stats the file. Since either is sufficient for our return value, we should do the cheaper one first, potentially short-circuiting the other. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.c b/setup.c index 89fcc12ab7..1de87ed842 100644 --- a/setup.c +++ b/setup.c @@ -229,7 +229,7 @@ void verify_filename(const char *prefix, { if (*arg == '-') die("bad flag '%s' used after filename", arg); - if (check_filename(prefix, arg) || looks_like_pathspec(arg)) + if (looks_like_pathspec(arg) || check_filename(prefix, arg)) return; die_verify_filename(prefix, arg, diagnose_misspelt_rev); } From e83352ef23cca2701953ed3c915f1db49b255a7d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 29 May 2017 12:39:46 +0900 Subject: [PATCH 395/566] Third batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 93 +++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index 4fc8f0db81..ee6c6075ad 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -36,6 +36,14 @@ UI, Workflows & Features necessary to go beyond the 4GB limit. (merge 867e40ff3a rs/large-zip later to maint). + * "git reset" learned "--recurse-submodules" option. + + * "git diff --submodule=diff" now recurses into nested submodules. + (merge 5a5221427c jk/diff-submodule-diff-inline later to maint). + + * "git repack" learned to accept the --threads= option and pass it + to pack-objects. + Performance, Internal Implementation, Development Support etc. @@ -65,6 +73,9 @@ Performance, Internal Implementation, Development Support etc. * We can trigger Windows auto-build tester (credits: Dscho & Microsoft) from our existing Travis CI tester now. + * Conversion from uchar[20] to struct object_id continues. + + Also contains various documentation updates and code clean-ups. @@ -118,9 +129,91 @@ notes for details). not translated by mistake), and TravisCI is told to run them. (merge b8e188f6f5 ab/fix-poison-tests later to maint). + * "git checkout --recurse-submodules" did not quite work with a + submodule that itself has submodules. + (merge 218c883783 sb/checkout-recurse-submodules later to maint). + + * Plug some leaks and updates internal API used to implement the + split index feature to make it easier to avoid such a leak in the + future. + (merge de950c5773 nd/split-index-unshare later to maint). + + * "pack-objects" can stream a slice of an existing packfile out when + the pack bitmap can tell that the reachable objects are all needed + in the output, without inspecting individual objects. This + strategy however would not work well when "--local" and other + options are in use, and need to be disabled. + (merge da5a1f8100 jk/disable-pack-reuse-when-broken later to maint). + + * Fix memory leaks pointed out by Coverity (and people). + (merge 443a12f37b js/plug-leaks later to maint). + + * "git read-tree -m" (no tree-ish) gave a nonsense suggestion "use + --empty if you want to clear the index". With "-m", such a request + will still fail anyway, as you'd need to name at least one tree-ish + to be merged. + (merge b9b10d3681 jc/read-tree-empty-with-m later to maint). + + * Make sure our tests would pass when the sources are checked out + with "platform native" line ending convention by default on + Windows. Some "text" files out tests use and the test scripts + themselves that are meant to be run with /bin/sh, ought to be + checked out with eol=LF even on Windows. + (merge 2779f66505 js/eol-on-ourselves later to maint). + + * Introduce the BUG() macro to improve die("BUG: ..."). + (merge 3d7dd2d3b6 jk/bug-to-abort later to maint). + + * Clarify documentation for include.path and includeIf..path + configuration variables. + (merge ce933ebd5a jk/doc-config-include later to maint). + + * Git sometimes gives an advice in a rhetorical question that does + not require an answer, which can confuse new users and non native + speakers. Attempt to rephrase them. + (merge 6963893943 ja/do-not-ask-needless-questions later to maint). + + * A few http:// links that are redirected to https:// in the + documentation have been updated to https:// links. + (merge 5e68729fd9 jk/update-links-in-docs later to maint). + + * "git for-each-ref --format=..." with %(HEAD) in the format used to + resolve the HEAD symref as many times as it had processed refs, + which was wasteful, and "git branch" shared the same problem. + (merge 613a0e52ea kn/ref-filter-branch-list later to maint). + + * Regression fix to topic recently merged to 'master'. + (merge d096d7f1ef pw/rebase-i-regression-fix later to maint). + + * The shell completion script (in contrib/) learned "git stash" has + a new "push" subcommand. + (merge 3851e4483f tg/stash-push-fixup later to maint). + + * "git interpret-trailers", when used as GIT_EDITOR for "git commit + -v", looked for and appended to a trailer block at the very end, + i.e. at the end of the "diff" output. The command has been + corrected to pay attention to the cut-mark line "commit -v" adds to + the buffer---the real trailer block should appear just before it. + (merge d76650b8d1 bm/interpret-trailers-cut-line-is-eom later to maint). + + * A test allowed both "git push" and "git receive-pack" on the other + end write their traces into the same file. This is OK on platforms + that allows atomically appending to a file opened with O_APPEND, + but on other platforms led to a mangled output, causing + intermittent test failures. This has been fixed by disabling + traces from "receive-pack" in the test. + (merge 71406ed4d6 jk/alternate-ref-optim later to maint). + + * Tag objects, which are not reachable from any ref, that point at + missing objects were mishandled by "git gc" and friends (they + should silently be ignored instead) + (merge a3ba6bf10a jk/ignore-broken-tags-when-ignoring-missing-links later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 515360f9e9 jn/credential-doc-on-clear later to maint). (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). (merge e294e8959f jc/apply-fix-mismerge later to maint). (merge 7f1b225153 bw/submodule-with-bs-path later to maint). (merge c8f7c8b704 tb/dedup-crlf-tests later to maint). + (merge 449456ad47 sg/core-filemode-doc-typofix later to maint). + (merge ba4dce784e km/log-showsignature-doc later to maint). From b8f354f294185eac98bb6978f9f92ba11932558b Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Sun, 28 May 2017 19:12:16 +0200 Subject: [PATCH 396/566] branch test: fix invalid config key access Fixes the test by changing "branch.s/s/dummy" to "branch.s/s.dummy" which is the right way of accessing config key "branch.s/s.dummy". Purpose of this test is to confirm that this key doesn't exist after the branch "s/s" has been renamed to "s". Earlier it was trying to access invalid config key and hence was getting an error. However, this wasn't caught because we were expecting the command to fail for other reason as mentioned above. Signed-off-by: Sahil Dua Signed-off-by: Junio C Hamano --- t/t3200-branch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 8a833f354e..6d355cc7aa 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -307,7 +307,7 @@ test_expect_success 'git branch -m s/s s should work when s/t is deleted' ' test_expect_success 'config information was renamed, too' ' test $(git config branch.s.dummy) = Hello && - test_must_fail git config branch.s/s/dummy + test_must_fail git config branch.s/s.dummy ' test_expect_success 'deleting a symref' ' From dc5a18b3643553cacd6f1a3e4bff6a678b067055 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 26 May 2017 12:09:01 +0900 Subject: [PATCH 397/566] compat-util: is_missing_file_error() Our code often opens a path to an optional file, to work on its contents when we can successfully open it. We can ignore a failure to open if such an optional file does not exist, but we do want to report a failure in opening for other reasons (e.g. we got an I/O error, or the file is there, but we lack the permission to open). The exact errors we need to ignore are ENOENT (obviously) and ENOTDIR (less obvious). Instead of repeating comparison of errno with these two constants, introduce a helper function to do so. Signed-off-by: Junio C Hamano --- git-compat-util.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index 8a4a3f85e7..8a3c680626 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1115,6 +1115,21 @@ struct tm *git_gmtime_r(const time_t *, struct tm *); #define getc_unlocked(fh) getc(fh) #endif +/* + * Our code often opens a path to an optional file, to work on its + * contents when we can successfully open it. We can ignore a failure + * to open if such an optional file does not exist, but we do want to + * report a failure in opening for other reasons (e.g. we got an I/O + * error, or the file is there, but we lack the permission to open). + * + * Call this function after seeing an error from open() or fopen() to + * see if the errno indicates a missing file that we can safely ignore. + */ +static inline int is_missing_file_error(int errno_) +{ + return (errno_ == ENOENT || errno_ == ENOTDIR); +} + extern int cmd_main(int, const char **); #endif From c7054209d65db430bdbcb2243288e63cea3e417c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 30 May 2017 09:23:33 +0900 Subject: [PATCH 398/566] treewide: use is_missing_file_error() where ENOENT and ENOTDIR are checked Using the is_missing_file_error() helper introduced in the previous step, update all hits from $ git grep -e ENOENT --and -e ENOTDIR There are codepaths that only check ENOENT, and it is possible that some of them should be checking both. Updating them is kept out of this step deliberately, as we do not want to change behaviour in this step. Signed-off-by: Junio C Hamano --- apply.c | 2 +- builtin/rm.c | 2 +- builtin/update-index.c | 2 +- diff-lib.c | 2 +- dir.c | 2 +- setup.c | 2 +- sha1_name.c | 4 ++-- wrapper.c | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apply.c b/apply.c index 0e2caeab9c..59bb3497de 100644 --- a/apply.c +++ b/apply.c @@ -3741,7 +3741,7 @@ static int check_to_create(struct apply_state *state, return 0; return EXISTS_IN_WORKTREE; - } else if ((errno != ENOENT) && (errno != ENOTDIR)) { + } else if (!is_missing_file_error(errno)) { return error_errno("%s", new_name); } return 0; diff --git a/builtin/rm.c b/builtin/rm.c index fb79dcab18..30c4332c68 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -129,7 +129,7 @@ static int check_local_mod(struct object_id *head, int index_only) ce = active_cache[pos]; if (lstat(ce->name, &st) < 0) { - if (errno != ENOENT && errno != ENOTDIR) + if (!is_missing_file_error(errno)) warning_errno(_("failed to stat '%s'"), ce->name); /* It already vanished from the working tree */ continue; diff --git a/builtin/update-index.c b/builtin/update-index.c index d530e89368..4e9402984a 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -253,7 +253,7 @@ static int remove_one_path(const char *path) */ static int process_lstat_error(const char *path, int err) { - if (err == ENOENT || err == ENOTDIR) + if (is_missing_file_error(err)) return remove_one_path(path); return error("lstat(\"%s\"): %s", path, strerror(err)); } diff --git a/diff-lib.c b/diff-lib.c index 52447466b5..88fc71e89e 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -29,7 +29,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st) { if (lstat(ce->name, st) < 0) { - if (errno != ENOENT && errno != ENOTDIR) + if (!is_missing_file_error(errno)) return -1; return 1; } diff --git a/dir.c b/dir.c index aeeb5ce104..98efe9d1c5 100644 --- a/dir.c +++ b/dir.c @@ -2235,7 +2235,7 @@ int remove_path(const char *name) { char *slash; - if (unlink(name) && errno != ENOENT && errno != ENOTDIR) + if (unlink(name) && !is_missing_file_error(errno)) return -1; slash = strrchr(name, '/'); diff --git a/setup.c b/setup.c index 8f64fbdfb2..bb6a2c1beb 100644 --- a/setup.c +++ b/setup.c @@ -147,7 +147,7 @@ int check_filename(const char *prefix, const char *arg) name = arg; if (!lstat(name, &st)) return 1; /* file exists */ - if (errno == ENOENT || errno == ENOTDIR) + if (is_missing_file_error(errno)) return 0; /* file does not exist */ die_errno("failed to stat '%s'", arg); } diff --git a/sha1_name.c b/sha1_name.c index 26ceec1d79..af7500037d 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1406,7 +1406,7 @@ static void diagnose_invalid_sha1_path(const char *prefix, if (file_exists(filename)) die("Path '%s' exists on disk, but not in '%.*s'.", filename, object_name_len, object_name); - if (errno == ENOENT || errno == ENOTDIR) { + if (is_missing_file_error(errno)) { char *fullname = xstrfmt("%s%s", prefix, filename); if (!get_tree_entry(tree_sha1, fullname, @@ -1471,7 +1471,7 @@ static void diagnose_invalid_index_path(int stage, if (file_exists(filename)) die("Path '%s' exists on disk, but not in the index.", filename); - if (errno == ENOENT || errno == ENOTDIR) + if (is_missing_file_error(errno)) die("Path '%s' does not exist (neither on disk nor in the index).", filename); diff --git a/wrapper.c b/wrapper.c index 0542fc7582..2fbbd81359 100644 --- a/wrapper.c +++ b/wrapper.c @@ -583,8 +583,8 @@ void warn_on_inaccessible(const char *path) static int access_error_is_ok(int err, unsigned flag) { - return err == ENOENT || err == ENOTDIR || - ((flag & ACCESS_EACCES_OK) && err == EACCES); + return (is_missing_file_error(err) || + ((flag & ACCESS_EACCES_OK) && err == EACCES)); } int access_or_warn(const char *path, int mode, unsigned flag) From 0339965c70d68fd3831c9a5306443c869de3f6a8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 30 May 2017 11:20:10 +0900 Subject: [PATCH 399/566] Fourth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index ee6c6075ad..d0f7ef5590 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -44,6 +44,22 @@ UI, Workflows & Features * "git repack" learned to accept the --threads= option and pass it to pack-objects. + * "git send-email" learned to run sendemail-validate hook to inspect + and reject a message before sending it out. + (merge 6489660b4b jt/send-email-validate-hook later to maint). + + * There is no good reason why "git fetch $there $sha1" should fail + when the $sha1 names an object at the tip of an advertised ref, + even when the other side hasn't enabled allowTipSHA1InWant. + + * The recently introduced "[includeIf "gitdir:$dir"] path=..." + mechansim has further been taught to take symlinks into account. + The directory "$dir" specified in "gitdir:$dir" may be a symlink to + a real location, not something that $(getcwd) may return. In such + a case, a realpath of "$dir" is compared with the real path of the + current repository to determine if the contents from the named path + should be included. + Performance, Internal Implementation, Development Support etc. @@ -75,6 +91,24 @@ Performance, Internal Implementation, Development Support etc. * Conversion from uchar[20] to struct object_id continues. + * Simplify parse_pathspec() codepath and stop it from looking at the + default in-core index. + (merge 08de9151a8 bw/pathspec-sans-the-index later to maint). + + * Add perf-test for wildmatch. + (merge 62ca75a6b9 ab/perf-wildmatch later to maint). + + * Code from "conversion using external process" codepath has been + extracted to a separate sub-process.[ch] module. + (merge 4f2a2e9f0e bp/sub-process-convert-filter later to maint). + + * When "git checkout", "git merge", etc. manipulates the in-core + index, various pieces of information in the index extensions are + discarded from the original state, as it is usually not the case + that they are kept up-to-date and in-sync with the operation on the + main index. The untracked cache extension is copied across these + operations now, which would speed up "git status" (as long as the + cache is properly invalidated). Also contains various documentation updates and code clean-ups. @@ -209,6 +243,26 @@ notes for details). should silently be ignored instead) (merge a3ba6bf10a jk/ignore-broken-tags-when-ignoring-missing-links later to maint). + * "git describe --contains" penalized light-weight tags so much that + they were almost never considered. Instead, give them about the + same chance to be considered as an annotated tag that is the same + age as the underlying commit would. + (merge ef1e74065c jc/name-rev-lw-tag later to maint). + + * The "run-command" API implementation has been made more robust + against dead-locking in a threaded environment. + (merge e3f43ce765 bw/forking-and-threading later to maint). + + * A recent update to t5545-push-options.sh started skipping all the + tests in the script when a web server testing is disabled or + unavailable, not just the ones that require a web server. Non HTTP + tests have been salvaged to always run in this script. + (merge 2e397e4ddf jc/skip-test-in-the-middle later to maint). + + * "git send-email" now uses Net::SMTP::SSL, which is obsolete, only + when needed. Recent versions of Net::SMTP can do TLS natively. + (merge 0ead000c3a dk/send-email-avoid-net-smtp-ssl-when-able later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 515360f9e9 jn/credential-doc-on-clear later to maint). (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). @@ -217,3 +271,4 @@ notes for details). (merge c8f7c8b704 tb/dedup-crlf-tests later to maint). (merge 449456ad47 sg/core-filemode-doc-typofix later to maint). (merge ba4dce784e km/log-showsignature-doc later to maint). + (merge c5a9157393 jh/memihash-opt later to maint). From 9ee4aa95dbddddf51a3a0d1b53a6c3df956236e1 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sun, 28 May 2017 14:13:25 +0200 Subject: [PATCH 400/566] completion: add completions for git config commit Add missing completions for git config: * commit.cleanup * commit.gpgSign * commit.verbose Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index af658995d5..535a19a70c 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2392,8 +2392,11 @@ _git_config () color.status.untracked color.status.updated color.ui + commit.cleanup + commit.gpgSign commit.status commit.template + commit.verbose core.abbrev core.askpass core.attributesfile From 58b75bd6db45a5108af41b764d617d677f26a6ca Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 26 May 2017 12:10:10 -0700 Subject: [PATCH 401/566] submodule recursing: do not write a config variable twice The command line option for '--recurse-submodules' is implemented using an OPTION_CALLBACK, which takes both the callback (that sets the file static global variable) as well as passes the same file static global variable to the option parsing machinery to assign it. This is fixed in this commit by passing NULL as the variable. The callback sets it instead Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/checkout.c | 2 +- builtin/read-tree.c | 2 +- builtin/reset.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 6c3d2e4f4c..da5c9dc11c 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1182,7 +1182,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) N_("second guess 'git checkout '")), OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees, N_("do not check if another worktree is holding the given ref")), - { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, + { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "checkout", "control recursive updating of submodules", PARSE_OPT_OPTARG, option_parse_recurse_submodules }, OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")), diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 23e212ee8c..2a1b8a530e 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -157,7 +157,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) N_("skip applying sparse checkout filter")), OPT_BOOL(0, "debug-unpack", &opts.debug_unpack, N_("debug unpack-trees")), - { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, + { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "checkout", "control recursive updating of submodules", PARSE_OPT_OPTARG, option_parse_recurse_submodules }, OPT_END() diff --git a/builtin/reset.c b/builtin/reset.c index 5ce27fcaed..1e5f85b1fb 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -304,7 +304,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), - { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, + { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "reset", "control recursive updating of submodules", PARSE_OPT_OPTARG, option_parse_recurse_submodules }, OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), From 17f38cb704623561fb4efd143429d63149f585d8 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 26 May 2017 12:10:11 -0700 Subject: [PATCH 402/566] submodule test invocation: only pass additional arguments In a later patch we want to introduce a config option to trigger the submodule recursing by default. As this option should be available and uniform across all commands that deal with submodules we'd want to test for this option in the submodule update library. So instead of calling the whole test set again for "git -c submodule.recurse foo" instead of "git foo --recurse-submodules", we'd only want to introduce one basic test that tests if the option is recognized and respected to not overload the test suite. Change the test functions by taking only the argument and assemble the command inside the test function by embedding the arguments into the command that is "git $arguments --recurse-submodules". It would be nice to do this for all functions in lib-submodule-update, but we cannot do that for the non-recursing tests, as there we do not just pass in a git command but whole functions. (See t3426 for example) Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- t/lib-submodule-update.sh | 10 ++++++---- t/t1013-read-tree-submodule.sh | 4 ++-- t/t2013-checkout-submodule.sh | 4 ++-- t/t7112-reset-submodule.sh | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index f0b1b18206..0272c4d8ca 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -781,8 +781,9 @@ test_submodule_forced_switch () { # - Removing a submodule with a git directory absorbs the submodules # git directory first into the superproject. -test_submodule_switch_recursing () { - command="$1" +test_submodule_switch_recursing_with_args () { + cmd_args="$1" + command="git $cmd_args --recurse-submodules" RESULTDS=success if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1 then @@ -1021,8 +1022,9 @@ test_submodule_switch_recursing () { # Test that submodule contents are updated when switching between commits # that change a submodule, but throwing away local changes in # the superproject as well as the submodule is allowed. -test_submodule_forced_switch_recursing () { - command="$1" +test_submodule_forced_switch_recursing_with_args () { + cmd_args="$1" + command="git $cmd_args --recurse-submodules" RESULT=success if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1 then diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh index de1ba02dc5..2c8d620324 100755 --- a/t/t1013-read-tree-submodule.sh +++ b/t/t1013-read-tree-submodule.sh @@ -9,9 +9,9 @@ KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1 -test_submodule_switch_recursing "git read-tree --recurse-submodules -u -m" +test_submodule_switch_recursing_with_args "read-tree -u -m" -test_submodule_forced_switch_recursing "git read-tree --recurse-submodules -u --reset" +test_submodule_forced_switch_recursing_with_args "read-tree -u --reset" test_submodule_switch "git read-tree -u -m" diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh index e8f70b806f..c962a02277 100755 --- a/t/t2013-checkout-submodule.sh +++ b/t/t2013-checkout-submodule.sh @@ -65,9 +65,9 @@ test_expect_success '"checkout " honors submodule.*.ignore from .git/ KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 -test_submodule_switch_recursing "git checkout --recurse-submodules" +test_submodule_switch_recursing_with_args "checkout" -test_submodule_forced_switch_recursing "git checkout -f --recurse-submodules" +test_submodule_forced_switch_recursing_with_args "checkout -f" test_submodule_switch "git checkout" diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh index f86ccdf215..a1cb9ff858 100755 --- a/t/t7112-reset-submodule.sh +++ b/t/t7112-reset-submodule.sh @@ -9,9 +9,9 @@ KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1 KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1 KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1 -test_submodule_switch_recursing "git reset --recurse-submodules --keep" +test_submodule_switch_recursing_with_args "reset --keep" -test_submodule_forced_switch_recursing "git reset --hard --recurse-submodules" +test_submodule_forced_switch_recursing_with_args "reset --hard" test_submodule_switch "git reset --keep" From d7a3803f9e83242adac0f02af843ef0520c71f0a Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 26 May 2017 12:10:12 -0700 Subject: [PATCH 403/566] reset/checkout/read-tree: unify config callback for submodule recursion The callback function is essentially duplicated 3 times. Remove all of them and offer a new callback function, that lives in submodule.c By putting the callback function there, we no longer need the function 'set_config_update_recurse_submodules', nor duplicate the global variable in each builtin as well as submodule.c In the three builtins we have different 2 ways how to load the .gitmodules and config file, which are slightly different. git-checkout has to load the submodule config all the time due to 23b4c7bcc5 (checkout: Use submodule.*.ignore settings from .git/config and .gitmodules, 2010-08-28) git-reset and git-read-tree do not respect these diff settings, so loading the submodule configuration is optional. Also put that into submodule.c for code deduplication. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/checkout.c | 27 +-------------------------- builtin/read-tree.c | 28 +++------------------------- builtin/reset.c | 27 ++------------------------- submodule.c | 33 +++++++++++++++++++++++++++------ submodule.h | 6 +++++- 5 files changed, 38 insertions(+), 83 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index da5c9dc11c..56ea723b75 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -21,31 +21,12 @@ #include "submodule-config.h" #include "submodule.h" -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; - static const char * const checkout_usage[] = { N_("git checkout [] "), N_("git checkout [] [] -- ..."), NULL, }; -static int option_parse_recurse_submodules(const struct option *opt, - const char *arg, int unset) -{ - if (unset) { - recurse_submodules = RECURSE_SUBMODULES_OFF; - return 0; - } - if (arg) - recurse_submodules = - parse_update_recurse_submodules_arg(opt->long_name, - arg); - else - recurse_submodules = RECURSE_SUBMODULES_ON; - - return 0; -} - struct checkout_opts { int patch_mode; int quiet; @@ -1184,7 +1165,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) N_("do not check if another worktree is holding the given ref")), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "checkout", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules }, + PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater }, OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")), OPT_END(), }; @@ -1215,12 +1196,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) git_xmerge_config("merge.conflictstyle", conflict_style, NULL); } - if (recurse_submodules != RECURSE_SUBMODULES_OFF) { - git_config(submodule_config, NULL); - if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) - set_config_update_recurse_submodules(recurse_submodules); - } - if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1) die(_("-b, -B and --orphan are mutually exclusive")); diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 2a1b8a530e..8a889ef4c3 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -21,7 +21,6 @@ static int nr_trees; static int read_empty; static struct tree *trees[MAX_UNPACK_TREES]; -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int list_tree(unsigned char *sha1) { @@ -99,23 +98,6 @@ static int debug_merge(const struct cache_entry * const *stages, return 0; } -static int option_parse_recurse_submodules(const struct option *opt, - const char *arg, int unset) -{ - if (unset) { - recurse_submodules = RECURSE_SUBMODULES_OFF; - return 0; - } - if (arg) - recurse_submodules = - parse_update_recurse_submodules_arg(opt->long_name, - arg); - else - recurse_submodules = RECURSE_SUBMODULES_ON; - - return 0; -} - static struct lock_file lock_file; int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) @@ -159,7 +141,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) N_("debug unpack-trees")), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "checkout", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules }, + PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater }, OPT_END() }; @@ -173,13 +155,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) argc = parse_options(argc, argv, unused_prefix, read_tree_options, read_tree_usage, 0); - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); + load_submodule_cache(); - if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) { - gitmodules_config(); - git_config(submodule_config, NULL); - set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON); - } + hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); prefix_set = opts.prefix ? 1 : 0; if (1 < opts.merge + opts.reset + prefix_set) diff --git a/builtin/reset.c b/builtin/reset.c index 1e5f85b1fb..6f89dc5494 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -24,25 +24,6 @@ #include "submodule.h" #include "submodule-config.h" -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; - -static int option_parse_recurse_submodules(const struct option *opt, - const char *arg, int unset) -{ - if (unset) { - recurse_submodules = RECURSE_SUBMODULES_OFF; - return 0; - } - if (arg) - recurse_submodules = - parse_update_recurse_submodules_arg(opt->long_name, - arg); - else - recurse_submodules = RECURSE_SUBMODULES_ON; - - return 0; -} - static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] []"), N_("git reset [-q] [] [--] ..."), @@ -306,7 +287,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) N_("reset HEAD but keep local changes"), KEEP), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "reset", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules }, + PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater }, OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), @@ -319,11 +300,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); - if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) { - gitmodules_config(); - git_config(submodule_config, NULL); - set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON); - } + load_submodule_cache(); unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash); if (unborn) { diff --git a/submodule.c b/submodule.c index 54825100b2..c9e764b519 100644 --- a/submodule.c +++ b/submodule.c @@ -18,7 +18,7 @@ #include "worktree.h" static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; -static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT; +static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; static int parallel_jobs = 1; static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP; static int initialized_fetch_ref_tips; @@ -169,6 +169,32 @@ int submodule_config(const char *var, const char *value, void *cb) return 0; } +int option_parse_recurse_submodules_worktree_updater(const struct option *opt, + const char *arg, int unset) +{ + if (unset) { + config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; + return 0; + } + if (arg) + config_update_recurse_submodules = + parse_update_recurse_submodules_arg(opt->long_name, + arg); + else + config_update_recurse_submodules = RECURSE_SUBMODULES_ON; + + return 0; +} + +void load_submodule_cache(void) +{ + if (config_update_recurse_submodules == RECURSE_SUBMODULES_OFF) + return; + + gitmodules_config(); + git_config(submodule_config, NULL); +} + void gitmodules_config(void) { const char *work_tree = get_git_work_tree(); @@ -596,11 +622,6 @@ void set_config_fetch_recurse_submodules(int value) config_fetch_recurse_submodules = value; } -void set_config_update_recurse_submodules(int value) -{ - config_update_recurse_submodules = value; -} - int should_update_submodules(void) { return config_update_recurse_submodules == RECURSE_SUBMODULES_ON; diff --git a/submodule.h b/submodule.h index 1277480add..b13f120f76 100644 --- a/submodule.h +++ b/submodule.h @@ -39,6 +39,11 @@ extern void stage_updated_gitmodules(void); extern void set_diffopt_flags_from_submodule_config(struct diff_options *, const char *path); extern int submodule_config(const char *var, const char *value, void *cb); + +struct option; +int option_parse_recurse_submodules_worktree_updater(const struct option *opt, + const char *arg, int unset); +void load_submodule_cache(void); extern void gitmodules_config(void); extern void gitmodules_config_sha1(const unsigned char *commit_sha1); extern int is_submodule_initialized(const char *path); @@ -65,7 +70,6 @@ extern void show_submodule_inline_diff(FILE *f, const char *path, const char *del, const char *add, const char *reset, const struct diff_options *opt); extern void set_config_fetch_recurse_submodules(int value); -extern void set_config_update_recurse_submodules(int value); /* Check if we want to update any submodule.*/ extern int should_update_submodules(void); /* From 1d789d089280539ca39b83aabb67860929d39b75 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 26 May 2017 12:10:13 -0700 Subject: [PATCH 404/566] submodule loading: separate code path for .gitmodules and config overlay The .gitmodules file is not supposed to have all the options available, that are available in the configuration so separate it out. A configuration option such as the hypothetical submodule.color.diff that determines in which color a submodule change is printed, is a very user specific thing, that the .gitmodules file should not tamper with. The .gitmodules file should only be used for settings that required to setup the project in which the .gitmodules file is tracked. As the minimum this would only include the name<->path mapping of the submodule and its URL and branch. Any further setting (such as 'fetch.recursesubmodules' or 'submodule..{update, ignore, shallow}') is not specific to the project setup requirements, but rather is a distribution of suggested developer configurations. In other areas of Git a suggested developer configuration is not transported in-tree but via other means. In an organisation this could be done by deploying an opinionated system wide config (/etc/gitconfig) or by putting the settings in the users home directory when they start at the organisation. In open source projects this is often accomplished via extensive READMEs (cf. our SubmittingPatches/CodingGuidlines). As a later patch in this series wants to introduce a generic submodule recursion option, we want to make sure that switch is not exposed via the gitmodules file. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/submodule.c b/submodule.c index c9e764b519..78cccb7563 100644 --- a/submodule.c +++ b/submodule.c @@ -153,7 +153,8 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, } } -int submodule_config(const char *var, const char *value, void *cb) +/* For loading from the .gitmodules file. */ +static int git_modules_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "submodule.fetchjobs")) { parallel_jobs = git_config_int(var, value); @@ -169,6 +170,12 @@ int submodule_config(const char *var, const char *value, void *cb) return 0; } +/* Loads all submodule settings from the config */ +int submodule_config(const char *var, const char *value, void *cb) +{ + return git_modules_config(var, value, cb); +} + int option_parse_recurse_submodules_worktree_updater(const struct option *opt, const char *arg, int unset) { @@ -222,7 +229,8 @@ void gitmodules_config(void) } if (!gitmodules_is_unmerged) - git_config_from_file(submodule_config, gitmodules_path.buf, NULL); + git_config_from_file(git_modules_config, + gitmodules_path.buf, NULL); strbuf_release(&gitmodules_path); } } @@ -233,7 +241,7 @@ void gitmodules_config_sha1(const unsigned char *commit_sha1) unsigned char sha1[20]; if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) { - git_config_from_blob_sha1(submodule_config, rev.buf, + git_config_from_blob_sha1(git_modules_config, rev.buf, sha1, NULL); } strbuf_release(&rev); From f3a2fffe06d13b216cbc18acba5a46b8a00ae326 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:11:23 -0400 Subject: [PATCH 405/566] am: handle "-h" argument earlier If the user provides "-h" on the command line, then our parse_options() invocation will show a usage message and quit. But if "-h" is the only argument, the git wrapper behaves specially: it ignores our RUN_SETUP flag and calls cmd_am() without having done repository setup at all. This is due to 99caeed05 (Let 'git -h' show usage without a git dir, 2009-11-09). Before cmd_am() calls parse_options(), though, it runs a few other setup functions. One of these is am_state_init(), which uses git_pathdup() to set up the default rebase-apply path. But calling git_pathdup() when we haven't done repository setup will fall back to using ".git". That's mostly harmless (since we won't use the value anyway), but is forbidden since b1ef400eec ("setup_git_env: avoid blind fall-back to ".git"", 2016-10-20), and we now BUG(). We can't easily move that setup to after the parse_options() call; the point is to set up defaults that are overwritten by the option parsing. Instead, we'll detect the "-h" case early and show the usage then. This matches the behavior of other builtins which have a similar setup-ordering issue (e.g., git-branch). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/am.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin/am.c b/builtin/am.c index a95dd8b4e6..c74df14722 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2319,6 +2319,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) OPT_END() }; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(usage, options); + git_config(git_am_config, NULL); am_state_init(&state); From 42fa0cbfe02bfb5f3e11d6d04f0205e1650f2e39 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:12:33 -0400 Subject: [PATCH 406/566] credential: handle invalid arguments earlier The git-credential command only takes one argument: the operation to perform. If we don't have one, we complain immediately. But if we have one that we don't recognize, we don't notice until after we've read the credential from stdin. This is likely to confuse a user invoking "git credential -h", as the program will hang waiting for their input before showing anything. Let's detect this case early. Likewise, we never noticed when there are extra arguments beyond the one we're expecting. Let's catch this with the same conditional. Note that we don't need to handle "--help" similarly, because the git wrapper does this before even calling cmd_credential(). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/credential.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/credential.c b/builtin/credential.c index 0412fa00f0..879acfbcda 100644 --- a/builtin/credential.c +++ b/builtin/credential.c @@ -10,9 +10,9 @@ int cmd_credential(int argc, const char **argv, const char *prefix) const char *op; struct credential c = CREDENTIAL_INIT; - op = argv[1]; - if (!op) + if (argc != 2 || !strcmp(argv[1], "-h")) usage(usage_msg); + op = argv[1]; if (credential_read(&c, stdin) < 0) die("unable to read credential from stdin"); From 619b6c1710f726866f62e34b01cc57c03b390299 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:13:43 -0400 Subject: [PATCH 407/566] upload-archive: handle "-h" option early Normally upload-archive forks off upload-archive--writer to do the real work, and relays any errors back over the sideband channel. This is a good thing when the command is properly invoked remotely via ssh or git-daemon. But it's confusing to curious users who try "git upload-archive -h". Let's catch this invocation early and give a real usage message, rather than spewing "-h does not appear to be a git repository" amidst packet-lines. The chance of a false positive due to a real client asking for the repo "-h" is quite small. Likewise, we'll catch "-h" in upload-archive--writer. People shouldn't be invoking it manually, but it doesn't hurt to give a sane message if they do. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/upload-archive.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index cde06977b7..84532ae9a9 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -22,7 +22,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) struct argv_array sent_argv = ARGV_ARRAY_INIT; const char *arg_cmd = "argument "; - if (argc != 2) + if (argc != 2 || !strcmp(argv[1], "-h")) usage(upload_archive_usage); if (!enter_repo(argv[1], 0)) @@ -76,6 +76,9 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) { struct child_process writer = { argv }; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage(upload_archive_usage); + /* * Set up sideband subprocess. * From bb246590a1b648ac23f6a22d1d9e119129ba2f03 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:15:09 -0400 Subject: [PATCH 408/566] remote-{ext,fd}: print usage message on invalid arguments We just say "Expected two arguments" when we get a different number of arguments, but we can be slightly friendlier. People shouldn't generally be running remote helpers themselves, but curious users might say "git remote-ext -h". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/remote-ext.c | 5 ++++- builtin/remote-fd.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index 11b48bfb41..bfb21ba7d2 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -3,6 +3,9 @@ #include "run-command.h" #include "pkt-line.h" +static const char usage_msg[] = + "git remote-ext "; + /* * URL syntax: * 'command [arg1 [arg2 [...]]]' Invoke command with given arguments. @@ -193,7 +196,7 @@ static int command_loop(const char *child) int cmd_remote_ext(int argc, const char **argv, const char *prefix) { if (argc != 3) - die("Expected two arguments"); + usage(usage_msg); return command_loop(argv[2]); } diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c index 08d7121b6d..91dfe07e06 100644 --- a/builtin/remote-fd.c +++ b/builtin/remote-fd.c @@ -1,6 +1,9 @@ #include "builtin.h" #include "transport.h" +static const char usage_msg[] = + "git remote-fd "; + /* * URL syntax: * 'fd::[/]' Read/write socket pair @@ -57,7 +60,7 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix) char *end; if (argc != 3) - die("Expected two arguments"); + usage(usage_msg); input_fd = (int)strtoul(argv[2], &end, 10); From f0994fa85dfcbd9854686d7b6de3b05b7952f41c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:16:50 -0400 Subject: [PATCH 409/566] submodule--helper: show usage for "-h" Normal users shouldn't ever call submodule--helper, but it doesn't hurt to give them a normal usage message if they try "-h". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 566a5b6a6f..a78b003c25 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1222,9 +1222,8 @@ static struct cmd_struct commands[] = { int cmd_submodule__helper(int argc, const char **argv, const char *prefix) { int i; - if (argc < 2) - die(_("submodule--helper subcommand must be " - "called with a subcommand")); + if (argc < 2 || !strcmp(argv[1], "-h")) + usage("git submodule--helper "); for (i = 0; i < ARRAY_SIZE(commands); i++) { if (!strcmp(argv[1], commands[i].cmd)) { From 66f5f6dca95eda46bf02c8d06de8cb265995fd6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 11 May 2017 21:20:12 +0000 Subject: [PATCH 410/566] C style: use standard style for "TRANSLATORS" comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change all the "TRANSLATORS: [...]" comments in the C code to use the regular Git coding style, and amend the style guide so that the example there uses that style. This custom style was necessary back in 2010 when the gettext support was initially added, and was subsequently documented in commit cbcfd4e3ea ("i18n: mention "TRANSLATORS:" marker in Documentation/CodingGuidelines", 2014-04-18). GNU xgettext hasn't had the parsing limitation that necessitated this exception for almost 3 years. Since its 0.19 release on 2014-06-02 it's been able to recognize TRANSLATOR comments in the standard Git comment syntax[1]. Usually we'd like to keep compatibility with software that's that young, but in this case literally the only person who needs to be using a gettext newer than 3 years old is Jiang Xin (the only person who runs & commits "make pot" results), so I think in this case we can make an exception. This xgettext parsing feature was added after a thread on the Git mailing list[2] which continued on the bug-gettext[3] list, but we never subsequently changed our style & styleguide, do so. There are already longstanding changes in git that use the standard comment style & have their TRANSLATORS comments extracted properly without getting the literal "*"'s mixed up in the text, as would happen before xgettext 0.19. Commit 7ff2683253 ("builtin-am: implement -i/--interactive", 2015-08-04) added one such comment, which in commit df0617bfa7 ("l10n: git.pot: v2.6.0 round 1 (123 new, 41 removed)", 2015-09-05) got picked up in the po/git.pot file with the right format, showing that Jiang already runs a modern xgettext. The xgettext parser does not handle the sort of non-standard comment style that I'm amending here in sequencer.c, but that isn't standard Git comment syntax anyway. With this change to sequencer.c & "make pot" the comment in the pot file is now correct: #. TRANSLATORS: %s will be "revert", "cherry-pick" or -#. * "rebase -i". +#. "rebase -i". 1. http://git.savannah.gnu.org/cgit/gettext.git/commit/?id=10af7fe6bd 2. <2ce9ec406501d112e032c8208417f8100bed04c6.1397712142.git.worldhello.net@gmail.com> (https://public-inbox.org/git/2ce9ec406501d112e032c8208417f8100bed04c6.1397712142.git.worldhello.net@gmail.com/) 3. https://lists.gnu.org/archive/html/bug-gettext/2014-04/msg00016.html Signed-off-by: Ævar Arnfjörð Bjarmason Acked-by: Jiang Xin Signed-off-by: Junio C Hamano --- Documentation/CodingGuidelines | 10 +++++----- bisect.c | 6 ++++-- builtin/blame.c | 15 +++++++++------ builtin/notes.c | 6 ++++-- builtin/remote.c | 7 +++++-- notes-utils.c | 7 +++++-- parse-options.c | 6 ++++-- ref-filter.c | 12 ++++++++---- sequencer.c | 3 ++- 9 files changed, 46 insertions(+), 26 deletions(-) diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index a4191aa388..9fd7383819 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -256,12 +256,12 @@ For C programs: Note however that a comment that explains a translatable string to translators uses a convention of starting with a magic token - "TRANSLATORS: " immediately after the opening delimiter, even when - it spans multiple lines. We do not add an asterisk at the beginning - of each line, either. E.g. + "TRANSLATORS: ", e.g. - /* TRANSLATORS: here is a comment that explains the string - to be translated, that follows immediately after it */ + /* + * TRANSLATORS: here is a comment that explains the string to + * be translated, that follows immediately after it. + */ _("Here is a translatable string explained by the above."); - Double negation is often harder to understand than no negation diff --git a/bisect.c b/bisect.c index 08c9fb7266..c5d5a2b64b 100644 --- a/bisect.c +++ b/bisect.c @@ -995,8 +995,10 @@ int bisect_next_all(const char *prefix, int no_checkout) steps_msg = xstrfmt(Q_("(roughly %d step)", "(roughly %d steps)", steps), steps); - /* TRANSLATORS: the last %s will be replaced with - "(roughly %d steps)" translation */ + /* + * TRANSLATORS: the last %s will be replaced with "(roughly %d + * steps)" translation. + */ printf(Q_("Bisecting: %d revision left to test after this %s\n", "Bisecting: %d revisions left to test after this %s\n", nr), nr, steps_msg); diff --git a/builtin/blame.c b/builtin/blame.c index 07506a3e45..ca9ebe40e7 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2688,12 +2688,15 @@ parse_done: blame_date_width = sizeof("2006-10-19"); break; case DATE_RELATIVE: - /* TRANSLATORS: This string is used to tell us the maximum - display width for a relative timestamp in "git blame" - output. For C locale, "4 years, 11 months ago", which - takes 22 places, is the longest among various forms of - relative timestamps, but your language may need more or - fewer display columns. */ + /* + * TRANSLATORS: This string is used to tell us the + * maximum display width for a relative timestamp in + * "git blame" output. For C locale, "4 years, 11 + * months ago", which takes 22 places, is the longest + * among various forms of relative timestamps, but + * your language may need more or fewer display + * columns. + */ blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */ break; case DATE_NORMAL: diff --git a/builtin/notes.c b/builtin/notes.c index 7b891471c4..fb856e53b6 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -340,8 +340,10 @@ static struct notes_tree *init_notes_check(const char *subcommand, ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref; if (!starts_with(ref, "refs/notes/")) - /* TRANSLATORS: the first %s will be replaced by a - git notes command: 'add', 'merge', 'remove', etc.*/ + /* + * TRANSLATORS: the first %s will be replaced by a git + * notes command: 'add', 'merge', 'remove', etc. + */ die(_("refusing to %s notes in %s (outside of refs/notes/)"), subcommand, ref); return t; diff --git a/builtin/remote.c b/builtin/remote.c index addf97ad29..9054e2858e 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1151,8 +1151,11 @@ static int show(int argc, const char **argv) url_nr = states.remote->url_nr; } for (i = 0; i < url_nr; i++) - /* TRANSLATORS: the colon ':' should align with - the one in " Fetch URL: %s" translation */ + /* + * TRANSLATORS: the colon ':' should align + * with the one in " Fetch URL: %s" + * translation. + */ printf_ln(_(" Push URL: %s"), url[i]); if (!i) printf_ln(_(" Push URL: %s"), _("(no URL)")); diff --git a/notes-utils.c b/notes-utils.c index 24a33616a4..8f9ad7d1f8 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -132,8 +132,11 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd) c->mode_from_env = 1; c->combine = parse_combine_notes_fn(rewrite_mode_env); if (!c->combine) - /* TRANSLATORS: The first %s is the name of the - environment variable, the second %s is its value */ + /* + * TRANSLATORS: The first %s is the name of + * the environment variable, the second %s is + * its value. + */ error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT, rewrite_mode_env); } diff --git a/parse-options.c b/parse-options.c index a23a1e67f0..e5ad34a2c3 100644 --- a/parse-options.c +++ b/parse-options.c @@ -589,8 +589,10 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx, fprintf_ln(outfile, _("usage: %s"), _(*usagestr++)); while (*usagestr && **usagestr) - /* TRANSLATORS: the colon here should align with the - one in "usage: %s" translation */ + /* + * TRANSLATORS: the colon here should align with the + * one in "usage: %s" translation. + */ fprintf_ln(outfile, _(" or: %s"), _(*usagestr++)); while (*usagestr) { if (**usagestr) diff --git a/ref-filter.c b/ref-filter.c index 3a640448fd..5632841753 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1251,13 +1251,17 @@ char *get_head_description(void) state.branch); else if (state.detached_from) { if (state.detached_at) - /* TRANSLATORS: make sure this matches - "HEAD detached at " in wt-status.c */ + /* + * TRANSLATORS: make sure this matches "HEAD + * detached at " in wt-status.c + */ strbuf_addf(&desc, _("(HEAD detached at %s)"), state.detached_from); else - /* TRANSLATORS: make sure this matches - "HEAD detached from " in wt-status.c */ + /* + * TRANSLATORS: make sure this matches "HEAD + * detached from " in wt-status.c + */ strbuf_addf(&desc, _("(HEAD detached from %s)"), state.detached_from); } diff --git a/sequencer.c b/sequencer.c index 10c3b4ff81..b77f359ca2 100644 --- a/sequencer.c +++ b/sequencer.c @@ -464,7 +464,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next, if (active_cache_changed && write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) - /* TRANSLATORS: %s will be "revert", "cherry-pick" or + /* + * TRANSLATORS: %s will be "revert", "cherry-pick" or * "rebase -i". */ return error(_("%s: Unable to write new index file"), From a56eea28c45a8f76e4167c4e6baa87e89b7be3bc Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 31 May 2017 00:27:47 -0400 Subject: [PATCH 411/566] remote: drop free_refspecs() function We already have free_refspec(), a public function which does the same thing as the static free_refspecs(). Let's just keep one. There are two minor differences between the functions: 1. free_refspecs() is a noop when the refspec argument is NULL. This probably doesn't matter in practice. The nr_refspec parameter would presumably be 0 in that case, skipping the loop. And free(NULL) is explicitly OK. But it doesn't hurt for us to port this extra safety to free_refspec(), as one of the callers passes a funny "i+1" count. 2. The order of arguments is reversed between the two functions. This patch uses the already-public order of free_refspec(), as it matches the argument order on the parsing side. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- remote.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/remote.c b/remote.c index 801137c72e..fbb405660a 100644 --- a/remote.c +++ b/remote.c @@ -477,26 +477,6 @@ static void read_config(void) alias_all_urls(); } -/* - * This function frees a refspec array. - * Warning: code paths should be checked to ensure that the src - * and dst pointers are always freeable pointers as well - * as the refspec pointer itself. - */ -static void free_refspecs(struct refspec *refspec, int nr_refspec) -{ - int i; - - if (!refspec) - return; - - for (i = 0; i < nr_refspec; i++) { - free(refspec[i].src); - free(refspec[i].dst); - } - free(refspec); -} - static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; @@ -610,7 +590,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp * since it is only possible to reach this point from within * the for loop above. */ - free_refspecs(rs, i+1); + free_refspec(i+1, rs); return NULL; } die("Invalid refspec '%s'", refspec[i]); @@ -621,7 +601,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str) struct refspec *refspec; refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1); - free_refspecs(refspec, 1); + free_refspec(1, refspec); return !!refspec; } @@ -638,6 +618,10 @@ struct refspec *parse_push_refspec(int nr_refspec, const char **refspec) void free_refspec(int nr_refspec, struct refspec *refspec) { int i; + + if (!refspec) + return; + for (i = 0; i < nr_refspec; i++) { free(refspec[i].src); free(refspec[i].dst); From 2c9a2ae285b0806a5f754dcf6064f7cedc7e1fa8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 31 May 2017 01:18:04 -0400 Subject: [PATCH 412/566] docs/config: mention protocol implications of url.insteadOf If a URL rewrite switches the protocol to something nonstandard (like "persistent-https" for "https"), the user may be bitten by the fact that the default protocol restrictions are different between the two. Let's drop a note in insteadOf that points the user in the right direction. It would be nice if we could make this work out of the box, but we can't without knowing the security implications of the user's rewrite. Only the documentation for a particular remote helper can advise one way or the other. Since we do include the persistent-https helper in contrib/ (and since it was the helper in the real-world case that inspired that patch), let's also drop a note there. Suggested-by: Elliott Cable Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ contrib/persistent-https/README | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 475e874d51..2c1d7494b9 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -3208,6 +3208,13 @@ url..insteadOf:: the best alternative for the particular user, even for a never-before-seen repository on the site. When more than one insteadOf strings match a given URL, the longest match is used. ++ +Note that any protocol restrictions will be applied to the rewritten +URL. If the rewrite changes the URL to use a custom protocol or remote +helper, you may need to adjust the `protocol.*.allow` config to permit +the request. In particular, protocols you expect to use for submodules +must be set to `always` rather than the default of `user`. See the +description of `protocol.allow` above. url..pushInsteadOf:: Any URL that starts with this value will not be pushed to; diff --git a/contrib/persistent-https/README b/contrib/persistent-https/README index f784dd2e66..7c4cd8d257 100644 --- a/contrib/persistent-https/README +++ b/contrib/persistent-https/README @@ -35,6 +35,16 @@ to use persistent-https: [url "persistent-http"] insteadof = http +You may also want to allow the use of the persistent-https helper for +submodule URLs (since any https URLs pointing to submodules will be +rewritten, and Git's out-of-the-box defaults forbid submodules from +using unknown remote helpers): + +[protocol "persistent-https"] + allow = always +[protocol "persistent-http"] + allow = always + ##################################################################### # BUILDING FROM SOURCE From 046b48239eca34425909330e59da57f5fd421bdc Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Wed, 31 May 2017 17:30:47 -0700 Subject: [PATCH 413/566] Introduce 'submodule.recurse' option for worktree manipulators Any command that understands '--recurse-submodules' can have its default changed to true, by setting the new 'submodule.recurse' option. This patch includes read-tree/checkout/reset for working tree manipulating commands. Later patches will cover other commands. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- Documentation/config.txt | 5 +++++ builtin/checkout.c | 2 +- builtin/read-tree.c | 10 +++++++++- builtin/reset.c | 10 +++++++++- submodule.c | 23 +++++++++++++++++++++-- submodule.h | 1 + t/lib-submodule-update.sh | 12 ++++++++++++ 7 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index e0b9fd0bc3..f60c504e86 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -3065,6 +3065,11 @@ submodule.active:: submodule's path to determine if the submodule is of interest to git commands. +submodule.recurse:: + Specifies if commands recurse into submodules by default. This + applies to all commands that have a `--recurse-submodules` option. + Defaults to false. + submodule.fetchJobs:: Specifies how many submodules are fetched/cloned at the same time. A positive integer allows up to that number of submodules fetched diff --git a/builtin/checkout.c b/builtin/checkout.c index 56ea723b75..e289b7d477 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -855,7 +855,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb) } if (starts_with(var, "submodule.")) - return parse_submodule_config_option(var, value); + return submodule_config(var, value, NULL); return git_xmerge_config(var, value, NULL); } diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 8a889ef4c3..7fd55140db 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -98,6 +98,14 @@ static int debug_merge(const struct cache_entry * const *stages, return 0; } +static int git_read_tree_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "submodule.recurse")) + return git_default_submodule_config(var, value, cb); + + return git_default_config(var, value, cb); +} + static struct lock_file lock_file; int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) @@ -150,7 +158,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) opts.src_index = &the_index; opts.dst_index = &the_index; - git_config(git_default_config, NULL); + git_config(git_read_tree_config, NULL); argc = parse_options(argc, argv, unused_prefix, read_tree_options, read_tree_usage, 0); diff --git a/builtin/reset.c b/builtin/reset.c index 6f89dc5494..585cfe0745 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -266,6 +266,14 @@ static int reset_refs(const char *rev, const struct object_id *oid) return update_ref_status; } +static int git_reset_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "submodule.recurse")) + return git_default_submodule_config(var, value, cb); + + return git_default_config(var, value, cb); +} + int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; @@ -294,7 +302,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_default_config, NULL); + git_config(git_reset_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); diff --git a/submodule.c b/submodule.c index 78cccb7563..2b157dc995 100644 --- a/submodule.c +++ b/submodule.c @@ -16,6 +16,7 @@ #include "quote.h" #include "remote.h" #include "worktree.h" +#include "parse-options.h" static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; @@ -170,10 +171,28 @@ static int git_modules_config(const char *var, const char *value, void *cb) return 0; } -/* Loads all submodule settings from the config */ +/* Loads all submodule settings from the config. */ int submodule_config(const char *var, const char *value, void *cb) { - return git_modules_config(var, value, cb); + if (!strcmp(var, "submodule.recurse")) { + int v = git_config_bool(var, value) ? + RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; + config_update_recurse_submodules = v; + return 0; + } else { + return git_modules_config(var, value, cb); + } +} + +/* Cheap function that only determines if we're interested in submodules at all */ +int git_default_submodule_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "submodule.recurse")) { + int v = git_config_bool(var, value) ? + RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; + config_update_recurse_submodules = v; + } + return 0; } int option_parse_recurse_submodules_worktree_updater(const struct option *opt, diff --git a/submodule.h b/submodule.h index b13f120f76..d920ca1d5a 100644 --- a/submodule.h +++ b/submodule.h @@ -39,6 +39,7 @@ extern void stage_updated_gitmodules(void); extern void set_diffopt_flags_from_submodule_config(struct diff_options *, const char *path); extern int submodule_config(const char *var, const char *value, void *cb); +extern int git_default_submodule_config(const char *var, const char *value, void *cb); struct option; int option_parse_recurse_submodules_worktree_updater(const struct option *opt, diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index 0272c4d8ca..52beadad96 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -990,6 +990,18 @@ test_submodule_switch_recursing_with_args () { ) ' + test_expect_success "git -c submodule.recurse=true $cmd_args: modified submodule updates submodule work tree" ' + prolog && + reset_work_tree_to_interested add_sub1 && + ( + cd submodule_update && + git branch -t modify_sub1 origin/modify_sub1 && + git -c submodule.recurse=true $cmd_args modify_sub1 && + test_superproject_content origin/modify_sub1 && + test_submodule_content sub1 origin/modify_sub1 + ) + ' + # Updating a submodule to an invalid sha1 doesn't update the # superproject nor the submodule's work tree. test_expect_success "$command: updating to a missing submodule commit fails" ' From 9071c078af637573a3068c6920dd4ceff6a5a69f Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Wed, 31 May 2017 17:30:48 -0700 Subject: [PATCH 414/566] builtin/grep.c: respect 'submodule.recurse' option In builtin/grep.c we parse the config before evaluating the command line options. This makes the task of teaching grep to respect the new config option 'submodule.recurse' very easy by just parsing that option. As an alternative I had implemented a similar structure to treat submodules as the fetch/push command have, including * aligning the meaning of the 'recurse_submodules' to possible submodule values RECURSE_SUBMODULES_* as defined in submodule.h. * having a callback to parse the value and * reacting to the RECURSE_SUBMODULES_DEFAULT state that was the initial state. However all this is not needed for a true boolean value, so let's keep it simple. However this adds another place where "submodule.recurse" is parsed. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/grep.c | 3 +++ t/t7814-grep-recurse-submodules.sh | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/builtin/grep.c b/builtin/grep.c index b1095362fb..454e263820 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -302,6 +302,9 @@ static int grep_cmd_config(const char *var, const char *value, void *cb) #endif } + if (!strcmp(var, "submodule.recurse")) + recurse_submodules = git_config_bool(var, value); + return st; } diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 3a58197f47..7184113b9b 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -33,6 +33,24 @@ test_expect_success 'grep correctly finds patterns in a submodule' ' test_cmp expect actual ' +test_expect_success 'grep finds patterns in a submodule via config' ' + test_config submodule.recurse true && + # expect from previous test + git grep -e "(3|4)" >actual && + test_cmp expect actual +' + +test_expect_success 'grep --no-recurse-submodules overrides config' ' + test_config submodule.recurse true && + cat >expect <<-\EOF && + a:(1|2)d(3|4) + b/b:(3|4) + EOF + + git grep -e "(3|4)" --no-recurse-submodules >actual && + test_cmp expect actual +' + test_expect_success 'grep and basic pathspecs' ' cat >expect <<-\EOF && submodule/a:(1|2)d(3|4) From 4e53d6a54122b6034b9b0346c84e3e5525aed542 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Wed, 31 May 2017 17:30:49 -0700 Subject: [PATCH 415/566] builtin/push.c: respect 'submodule.recurse' option The closest mapping from the boolean 'submodule.recurse' set to "yes" to the variety of submodule push modes is "on-demand", so implement that. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/push.c | 4 ++++ t/t5531-deep-submodule-push.sh | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/builtin/push.c b/builtin/push.c index a597759d8f..258648d5fd 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -498,6 +498,10 @@ static int git_push_config(const char *k, const char *v, void *cb) const char *value; if (!git_config_get_value("push.recursesubmodules", &value)) recurse_submodules = parse_push_recurse_submodules_arg(k, value); + } else if (!strcmp(k, "submodule.recurse")) { + int val = git_config_bool(k, v) ? + RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF; + recurse_submodules = val; } return git_default_config(k, v, NULL); diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index 57ba322628..712c595fd8 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh @@ -126,6 +126,27 @@ test_expect_success 'push succeeds if submodule commit not on remote but using o ) ' +test_expect_success 'push succeeds if submodule commit not on remote but using auto-on-demand via submodule.recurse config' ' + ( + cd work/gar/bage && + >recurse-on-demand-from-submodule-recurse-config && + git add recurse-on-demand-from-submodule-recurse-config && + git commit -m "Recurse submodule.recurse from config junk" + ) && + ( + cd work && + git add gar/bage && + git commit -m "Recurse submodule.recurse from config for gar/bage" && + git -c submodule.recurse push ../pub.git master && + # Check that the supermodule commit got there + git fetch ../pub.git && + git diff --quiet FETCH_HEAD master && + # Check that the submodule commit got there too + cd gar/bage && + git diff --quiet origin/master master + ) +' + test_expect_success 'push recurse-submodules on command line overrides config' ' ( cd work/gar/bage && From 58f4203e7db265cd2d8315ca5d9c9979cd577427 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Wed, 31 May 2017 17:30:50 -0700 Subject: [PATCH 416/566] builtin/fetch.c: respect 'submodule.recurse' option Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/fetch.c | 7 +++++++ t/t5526-fetch-submodules.sh | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/builtin/fetch.c b/builtin/fetch.c index 5f2c2ab23e..c1ec3b03c3 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -73,6 +73,13 @@ static int git_fetch_config(const char *k, const char *v, void *cb) fetch_prune_config = git_config_bool(k, v); return 0; } + + if (!strcmp(k, "submodule.recurse")) { + int r = git_config_bool(k, v) ? + RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; + recurse_submodules = r; + } + return git_default_config(k, v, cb); } diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index f3b0a8d30a..162baf101f 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -71,6 +71,16 @@ test_expect_success "fetch --recurse-submodules recurses into submodules" ' test_i18ncmp expect.err actual.err ' +test_expect_success "submodule.recurse option triggers recursive fetch" ' + add_upstream_commit && + ( + cd downstream && + git -c submodule.recurse fetch >../actual.out 2>../actual.err + ) && + test_must_be_empty actual.out && + test_i18ncmp expect.err actual.err +' + test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" ' add_upstream_commit && ( From fb87327aeed9c0079266e74b80d46b0119142601 Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Wed, 31 May 2017 23:39:29 +0200 Subject: [PATCH 417/566] doc: rewrite description for rev-parse --short `git rev-parse --short` is not a generic modifier but just a variant of `--verify` and considers the given length only as a suggestion to ensure uniqueness. Signed-off-by: Andreas Heiduk Signed-off-by: Junio C Hamano --- Documentation/config.txt | 1 + Documentation/git-rev-parse.txt | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 475e874d51..4865ac92e8 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -862,6 +862,7 @@ core.abbrev:: computed based on the approximate number of packed objects in your repository, which hopefully is enough for abbreviated object names to stay unique for some time. + The minimum length is 4. add.ignoreErrors:: add.ignore-errors (deprecated):: diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index c40c470448..b1293f24bb 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -126,6 +126,12 @@ can be used. 'git diff-{asterisk}'). In contrast to the `--sq-quote` option, the command input is still interpreted as usual. +--short[=length]:: + Same as `--verify` but shortens the object name to a unique + prefix with at least `length` characters. The minimum length + is 4, the default is the effective value of the `core.abbrev` + configuration variable (see linkgit:git-config[1]). + --not:: When showing object names, prefix them with '{caret}' and strip '{caret}' prefix from the object names that already have @@ -136,12 +142,6 @@ can be used. The option core.warnAmbiguousRefs is used to select the strict abbreviation mode. ---short:: ---short=number:: - Instead of outputting the full SHA-1 values of object names try to - abbreviate them to a shorter unique name. When no length is specified - 7 is used. The minimum length is 4. - --symbolic:: Usually the object names are output in SHA-1 form (with possible '{caret}' prefix); this option makes them output in a From bfbfc9a953d8e26b029110c05581b22f9f0d77e9 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Wed, 31 May 2017 17:17:43 -0700 Subject: [PATCH 418/566] send-email: Net::SMTP::starttls was introduced in v2.34 We cannot rely on the starttls method being present in Net::SMTP until c274b798e6881a941d941808c6d89966975cb8c8 (Merge branch 'ipv6_ssl' of https://github.com/noxxi/perl-libnet into noxxi-ipv6_ssl, 2014-06-02), which set the module version to 2.34. This version was first shipped as part of perl in v5.21.5~169 (Update libnet to CPAN version 3.01, 2014-10-10). Noticed on an Ubuntu system with perl 5.18.2-2ubuntu1.1, which provides Net::SMTP version 2.31. The error message is Can't locate object method "starttls" via package "Net::SMTP" at /usr/lib/git-core/git-send-email line 1410. Reported-by: Brandon Williams Reported-and-tested-by: Eric Biggers Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 0d90439d9a..d326238c0a 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1354,7 +1354,7 @@ EOF } require Net::SMTP; - my $use_net_smtp_ssl = version->parse($Net::SMTP::VERSION) < version->parse("1.28"); + my $use_net_smtp_ssl = version->parse($Net::SMTP::VERSION) < version->parse("2.34"); $smtp_domain ||= maildomain(); if ($smtp_encryption eq 'ssl') { From f15e7cf5cc95cbfd0d05c260f75631781e290edc Mon Sep 17 00:00:00 2001 From: Tyler Brazier Date: Thu, 1 Jun 2017 04:18:36 +0000 Subject: [PATCH 419/566] pull: ff --rebase --autostash works in dirty repo When `git pull --rebase --autostash` in a dirty repository resulted in a fast-forward, nothing was being autostashed and the pull failed. This was due to a shortcut to avoid running rebase when we can fast-forward, but autostash is ignored on that codepath. Now we will only take the shortcut if autostash is not in effect. Based on a few tests against the git.git repo, the shortcut does not seem to give us significant performance benefits, on Linux at least. Regardless, it is more important to be correct than to be fast. Signed-off-by: Tyler Brazier Signed-off-by: Junio C Hamano --- builtin/pull.c | 23 +++++++++++++---------- t/t5520-pull.sh | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/builtin/pull.c b/builtin/pull.c index dd1a4a94e4..42f0560252 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -772,6 +772,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) struct oid_array merge_heads = OID_ARRAY_INIT; struct object_id orig_head, curr_head; struct object_id rebase_fork_point; + int autostash; if (!getenv("GIT_REFLOG_ACTION")) set_reflog_message(argc, argv); @@ -800,8 +801,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (!opt_rebase && opt_autostash != -1) die(_("--[no-]autostash option is only valid with --rebase.")); + autostash = config_autostash; if (opt_rebase) { - int autostash = config_autostash; if (opt_autostash != -1) autostash = opt_autostash; @@ -862,16 +863,18 @@ int cmd_pull(int argc, const char **argv, const char *prefix) die(_("Cannot rebase onto multiple branches.")); if (opt_rebase) { - struct commit_list *list = NULL; - struct commit *merge_head, *head; + if (!autostash) { + struct commit_list *list = NULL; + struct commit *merge_head, *head; - head = lookup_commit_reference(orig_head.hash); - commit_list_insert(head, &list); - merge_head = lookup_commit_reference(merge_heads.oid[0].hash); - if (is_descendant_of(merge_head, list)) { - /* we can fast-forward this without invoking rebase */ - opt_ff = "--ff-only"; - return run_merge(); + head = lookup_commit_reference(orig_head.hash); + commit_list_insert(head, &list); + merge_head = lookup_commit_reference(merge_heads.oid[0].hash); + if (is_descendant_of(merge_head, list)) { + /* we can fast-forward this without invoking rebase */ + opt_ff = "--ff-only"; + return run_merge(); + } } return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point); } else { diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 17f4d0fe4e..f15f7a3329 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -272,6 +272,24 @@ test_expect_success '--rebase fast forward' ' test_cmp reflog.expected reflog.fuzzy ' +test_expect_success '--rebase --autostash fast forward' ' + test_when_finished " + git reset --hard + git checkout to-rebase + git branch -D to-rebase-ff + git branch -D behind" && + git branch behind && + git checkout -b to-rebase-ff && + echo another modification >>file && + git add file && + git commit -m mod && + + git checkout behind && + echo dirty >file && + git pull --rebase --autostash . to-rebase-ff && + test "$(git rev-parse HEAD)" = "$(git rev-parse to-rebase-ff)" +' + test_expect_success '--rebase with conflicts shows advice' ' test_when_finished "git rebase --abort; git checkout -f to-rebase" && git checkout -b seq && From fb95e2e38dd4e8301b3992bacfac4c87b8262181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Jun 2017 18:20:55 +0000 Subject: [PATCH 420/566] grep: un-break building with PCRE >= 8.32 without --enable-jit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend my change earlier in this series ("grep: add support for the PCRE v1 JIT API", 2017-04-11) to un-break the build on PCRE v1 versions later than 8.31 compiled without --enable-jit. As explained in that change and a later compatibility change in this series ("grep: un-break building with PCRE < 8.32", 2017-05-10) the pcre_jit_exec() function is a faster path to execute the JIT. Unfortunately there's no compatibility stub for that function compiled into the library if pcre_config(PCRE_CONFIG_JIT, &ret) would return 0, and no macro that can be used to check for it, so the only portable option to support builds without --enable-jit is via a new NO_LIBPCRE1_JIT=UnfortunatelyYes Makefile option[1]. Another option would be to make the JIT opt-in via USE_LIBPCRE1_JIT=YesPlease, after all it's not a default option of PCRE v1. I think it makes more sense to make it opt-out since even though it's not a default option, most packagers of PCRE seem to turn it on by default, with the notable exception of the MinGW package. Make the MinGW platform work by default by changing the build defaults to turn on NO_LIBPCRE1_JIT=UnfortunatelyYes. It is the only platform that turns on USE_LIBPCRE=YesPlease by default, see commit df5218b4c3 ("config.mak.uname: support MSys2", 2016-01-13) for that change. 1. "How do I support pcre1 JIT on all versions?" (https://lists.exim.org/lurker/thread/20170601.103148.10253788.en.html) 2. https://github.com/Alexpux/MINGW-packages/blob/master/mingw-w64-pcre/PKGBUILD (referenced from "Re: PCRE v2 compile error, was Re: What's cooking in git.git (May 2017, #01; Mon, 1)"; ) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 13 +++++++++++++ config.mak.uname | 1 + grep.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/Makefile b/Makefile index a79274e5e6..502c07246f 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,14 @@ all:: # Perl-compatible regular expressions instead of standard or extended # POSIX regular expressions. # +# When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if the PCRE v1 +# library is compiled without --enable-jit. We will auto-detect +# whether the version of the PCRE v1 library in use has JIT support at +# all, but we unfortunately can't auto-detect whether JIT support +# hasn't been compiled in in an otherwise JIT-supporting version. If +# you have link-time errors about a missing `pcre_jit_exec` define +# this, or recompile PCRE v1 with --enable-jit. +# # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in # /foo/bar/include and /foo/bar/lib directories. # @@ -1094,6 +1102,10 @@ ifdef USE_LIBPCRE EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib) endif EXTLIBS += -lpcre + +ifdef NO_LIBPCRE1_JIT + BASIC_CFLAGS += -DNO_LIBPCRE1_JIT +endif endif ifdef HAVE_ALLOCA_H @@ -2241,6 +2253,7 @@ GIT-BUILD-OPTIONS: FORCE @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+ @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+ @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ + @echo NO_LIBPCRE1_JIT=\''$(subst ','\'',$(subst ','\'',$(NO_LIBPCRE1_JIT)))'\' >>$@+ @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+ @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ diff --git a/config.mak.uname b/config.mak.uname index 399fe19271..2a577794ba 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -550,6 +550,7 @@ else NO_GETTEXT = USE_GETTEXT_SCHEME = fallthrough USE_LIBPCRE= YesPlease + NO_LIBPCRE1_JIT = UnfortunatelyYes NO_CURL = USE_NED_ALLOCATOR = YesPlease else diff --git a/grep.h b/grep.h index ce90969736..3bff0870b2 100644 --- a/grep.h +++ b/grep.h @@ -5,9 +5,11 @@ #include #ifdef PCRE_CONFIG_JIT #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 +#ifndef NO_LIBPCRE1_JIT #define GIT_PCRE1_USE_JIT #endif #endif +#endif #ifndef PCRE_STUDY_JIT_COMPILE #define PCRE_STUDY_JIT_COMPILE 0 #endif From 94da9193a6eb8f1085d611c04ff8bbb4f5ae1e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 1 Jun 2017 18:20:56 +0000 Subject: [PATCH 421/566] grep: add support for PCRE v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for v2 of the PCRE API. This is a new major version of PCRE that came out in early 2015[1]. The regular expression syntax is the same, but while the API is similar, pretty much every function is either renamed or takes different arguments. Thus using it via entirely new functions makes sense, as opposed to trying to e.g. have one compile_pcre_pattern() that would call either PCRE v1 or v2 functions. Git can now be compiled with either USE_LIBPCRE1=YesPlease or USE_LIBPCRE2=YesPlease, with USE_LIBPCRE=YesPlease currently being a synonym for the former. Providing both is a compile-time error. With earlier patches to enable JIT for PCRE v1 the performance of the release versions of both libraries is almost exactly the same, with PCRE v2 being around 1% slower. However after I reported this to the pcre-dev mailing list[2] I got a lot of help with the API use from Zoltán Herczeg, he subsequently optimized some of the JIT functionality in v2 of the library. Running the p7820-grep-engines.sh performance test against the latest Subversion trunk of both, with both them and git compiled as -O3, and the test run against linux.git, gives the following results. Just the /perl/ tests shown: $ GIT_PERF_REPEAT_COUNT=30 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_MAKE_COMMAND='grep -q LIBPCRE2 Makefile && make -j8 USE_LIBPCRE2=YesPlease CC=~/perl5/installed/bin/gcc NO_R_TO_GCC_LINKER=YesPlease CFLAGS=-O3 LIBPCREDIR=/home/avar/g/pcre2/inst LDFLAGS=-Wl,-rpath,/home/avar/g/pcre2/inst/lib || make -j8 USE_LIBPCRE=YesPlease CC=~/perl5/installed/bin/gcc NO_R_TO_GCC_LINKER=YesPlease CFLAGS=-O3 LIBPCREDIR=/home/avar/g/pcre/inst LDFLAGS=-Wl,-rpath,/home/avar/g/pcre/inst/lib' ./run HEAD~5 HEAD~ HEAD p7820-grep-engines.sh [...] Test HEAD~5 HEAD~ HEAD ----------------------------------------------------------------------------------------------------------------- 7820.3: perl grep 'how.to' 0.31(1.10+0.48) 0.21(0.35+0.56) -32.3% 0.21(0.34+0.55) -32.3% 7820.7: perl grep '^how to' 0.56(2.70+0.40) 0.24(0.64+0.52) -57.1% 0.20(0.28+0.60) -64.3% 7820.11: perl grep '[how] to' 0.56(2.66+0.38) 0.29(0.95+0.45) -48.2% 0.23(0.45+0.54) -58.9% 7820.15: perl grep '(e.t[^ ]*|v.ry) rare' 1.02(5.77+0.42) 0.31(1.02+0.54) -69.6% 0.23(0.50+0.54) -77.5% 7820.19: perl grep 'm(ú|u)lt.b(æ|y)te' 0.38(1.57+0.42) 0.27(0.85+0.46) -28.9% 0.21(0.33+0.57) -44.7% See commit ("perf: add a comparison test of grep regex engines", 2017-04-19) for details on the machine the above test run was executed on. Here HEAD~2 is git with PCRE v1 without JIT, HEAD~ is PCRE v1 with JIT, and HEAD is PCRE v2 (also with JIT). See previous commits of mine mentioning p7820-grep-engines.sh for more details on the test setup. For ease of readability, a different run just of HEAD~ (PCRE v1 with JIT v.s. PCRE v2), again with just the /perl/ tests shown: [...] Test HEAD~ HEAD ---------------------------------------------------------------------------------------- 7820.3: perl grep 'how.to' 0.21(0.42+0.52) 0.21(0.31+0.58) +0.0% 7820.7: perl grep '^how to' 0.25(0.65+0.50) 0.20(0.31+0.57) -20.0% 7820.11: perl grep '[how] to' 0.30(0.90+0.50) 0.23(0.46+0.53) -23.3% 7820.15: perl grep '(e.t[^ ]*|v.ry) rare' 0.30(1.19+0.38) 0.23(0.51+0.51) -23.3% 7820.19: perl grep 'm(ú|u)lt.b(æ|y)te' 0.27(0.84+0.48) 0.21(0.34+0.57) -22.2% I.e. the two are either neck-to-neck, but PCRE v2 usually pulls ahead, when it does it's around 20% faster. A brief note on thread safety: As noted in pcre2api(3) & pcre2jit(3) the compiled pattern can be shared between threads, but not some of the JIT context, however the grep threading support does all pattern & JIT compilation in separate threads, so this code doesn't need to concern itself with thread safety. See commit 63e7e9d8b6 ("git-grep: Learn PCRE", 2011-05-09) for the initial addition of PCRE v1. This change follows some of the same patterns it did (and which were discussed on list at the time), e.g. mocking up types with typedef instead of ifdef-ing them out when USE_LIBPCRE2 isn't defined. This adds some trivial memory use to the program, but makes the code look nicer. 1. https://lists.exim.org/lurker/message/20150105.162835.0666407a.en.html 2. https://lists.exim.org/lurker/thread/20170419.172322.833ee099.en.html Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 36 ++++++++++--- configure.ac | 79 ++++++++++++++++++++++----- grep.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++ grep.h | 17 ++++++ t/test-lib.sh | 2 +- 5 files changed, 257 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 502c07246f..96ebbed87e 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,11 @@ all:: # Perl-compatible regular expressions instead of standard or extended # POSIX regular expressions. # +# Currently USE_LIBPCRE is a synonym for USE_LIBPCRE1, define +# USE_LIBPCRE2 instead if you'd like to use version 2 of the PCRE +# library. The USE_LIBPCRE flag will likely be changed to mean v2 by +# default in future releases. +# # When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if the PCRE v1 # library is compiled without --enable-jit. We will auto-detect # whether the version of the PCRE v1 library in use has JIT support at @@ -37,8 +42,10 @@ all:: # you have link-time errors about a missing `pcre_jit_exec` define # this, or recompile PCRE v1 with --enable-jit. # -# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in -# /foo/bar/include and /foo/bar/lib directories. +# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are +# in /foo/bar/include and /foo/bar/lib directories. Which version of +# PCRE this points to determined by the USE_LIBPCRE1 and USE_LIBPCRE2 +# variables. # # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header. # @@ -1095,12 +1102,14 @@ ifdef NO_LIBGEN_H COMPAT_OBJS += compat/basename.o endif -ifdef USE_LIBPCRE - BASIC_CFLAGS += -DUSE_LIBPCRE1 - ifdef LIBPCREDIR - BASIC_CFLAGS += -I$(LIBPCREDIR)/include - EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib) +USE_LIBPCRE1 ?= $(USE_LIBPCRE) + +ifneq (,$(USE_LIBPCRE1)) + ifdef USE_LIBPCRE2 +$(error Only set USE_LIBPCRE1 (or its alias USE_LIBPCRE) or USE_LIBPCRE2, not both!) endif + + BASIC_CFLAGS += -DUSE_LIBPCRE1 EXTLIBS += -lpcre ifdef NO_LIBPCRE1_JIT @@ -1108,6 +1117,16 @@ ifdef NO_LIBPCRE1_JIT endif endif +ifdef USE_LIBPCRE2 + BASIC_CFLAGS += -DUSE_LIBPCRE2 + EXTLIBS += -lpcre2-8 +endif + +ifdef LIBPCREDIR + BASIC_CFLAGS += -I$(LIBPCREDIR)/include + EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib) +endif + ifdef HAVE_ALLOCA_H BASIC_CFLAGS += -DHAVE_ALLOCA_H endif @@ -2252,7 +2271,8 @@ GIT-BUILD-OPTIONS: FORCE @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+ @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+ @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+ - @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ + @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE1)))'\' >>$@+ + @echo USE_LIBPCRE2=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE2)))'\' >>$@+ @echo NO_LIBPCRE1_JIT=\''$(subst ','\'',$(subst ','\'',$(NO_LIBPCRE1_JIT)))'\' >>$@+ @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+ diff --git a/configure.ac b/configure.ac index deeb968daa..11d083fbe0 100644 --- a/configure.ac +++ b/configure.ac @@ -255,21 +255,61 @@ GIT_PARSE_WITH([openssl])) # Perl-compatible regular expressions instead of standard or extended # POSIX regular expressions. # -# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in +# Currently USE_LIBPCRE is a synonym for USE_LIBPCRE1, define +# USE_LIBPCRE2 instead if you'd like to use version 2 of the PCRE +# library. The USE_LIBPCRE flag will likely be changed to mean v2 by +# default in future releases. +# +# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are in # /foo/bar/include and /foo/bar/lib directories. # AC_ARG_WITH(libpcre, -AS_HELP_STRING([--with-libpcre],[support Perl-compatible regexes (default is NO)]) -AS_HELP_STRING([], [ARG can be also prefix for libpcre library and headers]), +AS_HELP_STRING([--with-libpcre],[synonym for --with-libpcre1]), if test "$withval" = "no"; then - USE_LIBPCRE= + USE_LIBPCRE1= elif test "$withval" = "yes"; then - USE_LIBPCRE=YesPlease + USE_LIBPCRE1=YesPlease else - USE_LIBPCRE=YesPlease + USE_LIBPCRE1=YesPlease LIBPCREDIR=$withval AC_MSG_NOTICE([Setting LIBPCREDIR to $LIBPCREDIR]) - dnl USE_LIBPCRE can still be modified below, so don't substitute + dnl USE_LIBPCRE1 can still be modified below, so don't substitute + dnl it yet. + GIT_CONF_SUBST([LIBPCREDIR]) + fi) + +AC_ARG_WITH(libpcre1, +AS_HELP_STRING([--with-libpcre1],[support Perl-compatible regexes via libpcre1 (default is NO)]) +AS_HELP_STRING([], [ARG can be also prefix for libpcre library and headers]), + if test "$withval" = "no"; then + USE_LIBPCRE1= + elif test "$withval" = "yes"; then + USE_LIBPCRE1=YesPlease + else + USE_LIBPCRE1=YesPlease + LIBPCREDIR=$withval + AC_MSG_NOTICE([Setting LIBPCREDIR to $LIBPCREDIR]) + dnl USE_LIBPCRE1 can still be modified below, so don't substitute + dnl it yet. + GIT_CONF_SUBST([LIBPCREDIR]) + fi) + +AC_ARG_WITH(libpcre2, +AS_HELP_STRING([--with-libpcre2],[support Perl-compatible regexes via libpcre2 (default is NO)]) +AS_HELP_STRING([], [ARG can be also prefix for libpcre library and headers]), + if test -n "$USE_LIBPCRE1"; then + AC_MSG_ERROR([Only supply one of --with-libpcre1 or --with-libpcre2!]) + fi + + if test "$withval" = "no"; then + USE_LIBPCRE2= + elif test "$withval" = "yes"; then + USE_LIBPCRE2=YesPlease + else + USE_LIBPCRE2=YesPlease + LIBPCREDIR=$withval + AC_MSG_NOTICE([Setting LIBPCREDIR to $LIBPCREDIR]) + dnl USE_LIBPCRE2 can still be modified below, so don't substitute dnl it yet. GIT_CONF_SUBST([LIBPCREDIR]) fi) @@ -501,13 +541,11 @@ GIT_CONF_SUBST([NEEDS_SSL_WITH_CRYPTO]) GIT_CONF_SUBST([NO_OPENSSL]) # -# Define USE_LIBPCRE if you have and want to use libpcre. Various -# commands such as log and grep offer runtime options to use -# Perl-compatible regular expressions instead of standard or extended -# POSIX regular expressions. +# Handle the USE_LIBPCRE1 and USE_LIBPCRE2 options potentially set +# above. # -if test -n "$USE_LIBPCRE"; then +if test -n "$USE_LIBPCRE1"; then GIT_STASH_FLAGS($LIBPCREDIR) @@ -517,7 +555,22 @@ AC_CHECK_LIB([pcre], [pcre_version], GIT_UNSTASH_FLAGS($LIBPCREDIR) -GIT_CONF_SUBST([USE_LIBPCRE]) +GIT_CONF_SUBST([USE_LIBPCRE1]) + +fi + + +if test -n "$USE_LIBPCRE2"; then + +GIT_STASH_FLAGS($LIBPCREDIR) + +AC_CHECK_LIB([pcre2-8], [pcre2_config_8], +[USE_LIBPCRE2=YesPlease], +[USE_LIBPCRE2=]) + +GIT_UNSTASH_FLAGS($LIBPCREDIR) + +GIT_CONF_SUBST([USE_LIBPCRE2]) fi diff --git a/grep.c b/grep.c index 19fa67c34c..d0bf37858a 100644 --- a/grep.c +++ b/grep.c @@ -179,22 +179,37 @@ static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, st case GREP_PATTERN_TYPE_BRE: opt->fixed = 0; opt->pcre1 = 0; + opt->pcre2 = 0; break; case GREP_PATTERN_TYPE_ERE: opt->fixed = 0; opt->pcre1 = 0; + opt->pcre2 = 0; opt->regflags |= REG_EXTENDED; break; case GREP_PATTERN_TYPE_FIXED: opt->fixed = 1; opt->pcre1 = 0; + opt->pcre2 = 0; break; case GREP_PATTERN_TYPE_PCRE: opt->fixed = 0; +#ifdef USE_LIBPCRE2 + opt->pcre1 = 0; + opt->pcre2 = 1; +#else + /* + * It's important that pcre1 always be assigned to + * even when there's no USE_LIBPCRE* defined. We still + * call the PCRE stub function, it just dies with + * "cannot use Perl-compatible regexes[...]". + */ opt->pcre1 = 1; + opt->pcre2 = 0; +#endif break; } } @@ -446,6 +461,127 @@ static void free_pcre1_regexp(struct grep_pat *p) } #endif /* !USE_LIBPCRE1 */ +#ifdef USE_LIBPCRE2 +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) +{ + int error; + PCRE2_UCHAR errbuf[256]; + PCRE2_SIZE erroffset; + int options = PCRE2_MULTILINE; + const uint8_t *character_tables = NULL; + int jitret; + + assert(opt->pcre2); + + p->pcre2_compile_context = NULL; + + if (opt->ignore_case) { + if (has_non_ascii(p->pattern)) { + character_tables = pcre2_maketables(NULL); + p->pcre2_compile_context = pcre2_compile_context_create(NULL); + pcre2_set_character_tables(p->pcre2_compile_context, character_tables); + } + options |= PCRE2_CASELESS; + } + if (is_utf8_locale() && has_non_ascii(p->pattern)) + options |= PCRE2_UTF; + + p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern, + p->patternlen, options, &error, &erroffset, + p->pcre2_compile_context); + + if (p->pcre2_pattern) { + p->pcre2_match_data = pcre2_match_data_create_from_pattern(p->pcre2_pattern, NULL); + if (!p->pcre2_match_data) + die("Couldn't allocate PCRE2 match data"); + } else { + pcre2_get_error_message(error, errbuf, sizeof(errbuf)); + compile_regexp_failed(p, (const char *)&errbuf); + } + + pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on); + if (p->pcre2_jit_on == 1) { + jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE); + if (jitret) + die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret); + p->pcre2_jit_stack = pcre2_jit_stack_create(1, 1024 * 1024, NULL); + if (!p->pcre2_jit_stack) + die("Couldn't allocate PCRE2 JIT stack"); + p->pcre2_match_context = pcre2_match_context_create(NULL); + if (!p->pcre2_jit_stack) + die("Couldn't allocate PCRE2 match context"); + pcre2_jit_stack_assign(p->pcre2_match_context, NULL, p->pcre2_jit_stack); + } else if (p->pcre2_jit_on != 0) { + die("BUG: The pcre2_jit_on variable should be 0 or 1, not %d", + p->pcre1_jit_on); + } +} + +static int pcre2match(struct grep_pat *p, const char *line, const char *eol, + regmatch_t *match, int eflags) +{ + int ret, flags = 0; + PCRE2_SIZE *ovector; + PCRE2_UCHAR errbuf[256]; + + if (eflags & REG_NOTBOL) + flags |= PCRE2_NOTBOL; + + if (p->pcre2_jit_on) + ret = pcre2_jit_match(p->pcre2_pattern, (unsigned char *)line, + eol - line, 0, flags, p->pcre2_match_data, + NULL); + else + ret = pcre2_match(p->pcre2_pattern, (unsigned char *)line, + eol - line, 0, flags, p->pcre2_match_data, + NULL); + + if (ret < 0 && ret != PCRE2_ERROR_NOMATCH) { + pcre2_get_error_message(ret, errbuf, sizeof(errbuf)); + die("%s failed with error code %d: %s", + (p->pcre2_jit_on ? "pcre2_jit_match" : "pcre2_match"), ret, + errbuf); + } + if (ret > 0) { + ovector = pcre2_get_ovector_pointer(p->pcre2_match_data); + ret = 0; + match->rm_so = (int)ovector[0]; + match->rm_eo = (int)ovector[1]; + } + + return ret; +} + +static void free_pcre2_pattern(struct grep_pat *p) +{ + pcre2_compile_context_free(p->pcre2_compile_context); + pcre2_code_free(p->pcre2_pattern); + pcre2_match_data_free(p->pcre2_match_data); + pcre2_jit_stack_free(p->pcre2_jit_stack); + pcre2_match_context_free(p->pcre2_match_context); +} +#else /* !USE_LIBPCRE2 */ +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) +{ + /* + * Unreachable until USE_LIBPCRE2 becomes synonymous with + * USE_LIBPCRE. See the sibling comment in + * grep_set_pattern_type_option(). + */ + die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE"); +} + +static int pcre2match(struct grep_pat *p, const char *line, const char *eol, + regmatch_t *match, int eflags) +{ + return 1; +} + +static void free_pcre2_pattern(struct grep_pat *p) +{ +} +#endif /* !USE_LIBPCRE2 */ + static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt) { struct strbuf sb = STRBUF_INIT; @@ -511,6 +647,11 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) return; } + if (opt->pcre2) { + compile_pcre2_pattern(p, opt); + return; + } + if (opt->pcre1) { compile_pcre1_regexp(p, opt); return; @@ -870,6 +1011,8 @@ void free_grep_patterns(struct grep_opt *opt) kwsfree(p->kws); else if (p->pcre1_regexp) free_pcre1_regexp(p); + else if (p->pcre2_pattern) + free_pcre2_pattern(p); else regfree(&p->regexp); free(p->pattern); @@ -950,6 +1093,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol, hit = !fixmatch(p, line, eol, match); else if (p->pcre1_regexp) hit = !pcre1match(p, line, eol, match, eflags); + else if (p->pcre2_pattern) + hit = !pcre2match(p, line, eol, match, eflags); else hit = !regexec_buf(&p->regexp, line, eol - line, 1, match, eflags); diff --git a/grep.h b/grep.h index 3bff0870b2..6f3d4e1954 100644 --- a/grep.h +++ b/grep.h @@ -21,6 +21,16 @@ typedef int pcre; typedef int pcre_extra; typedef int pcre_jit_stack; #endif +#ifdef USE_LIBPCRE2 +#define PCRE2_CODE_UNIT_WIDTH 8 +#include +#else +typedef int pcre2_code; +typedef int pcre2_match_data; +typedef int pcre2_compile_context; +typedef int pcre2_match_context; +typedef int pcre2_jit_stack; +#endif #include "kwset.h" #include "thread-utils.h" #include "userdiff.h" @@ -65,6 +75,12 @@ struct grep_pat { pcre_jit_stack *pcre1_jit_stack; const unsigned char *pcre1_tables; int pcre1_jit_on; + pcre2_code *pcre2_pattern; + pcre2_match_data *pcre2_match_data; + pcre2_compile_context *pcre2_compile_context; + pcre2_match_context *pcre2_match_context; + pcre2_jit_stack *pcre2_jit_stack; + uint32_t pcre2_jit_on; kwset_t kws; unsigned fixed:1; unsigned ignore_case:1; @@ -128,6 +144,7 @@ struct grep_opt { int extended; int use_reflog_filter; int pcre1; + int pcre2; int relative; int pathname; int null_following_name; diff --git a/t/test-lib.sh b/t/test-lib.sh index ab92c0ebaa..44d4679384 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1011,7 +1011,7 @@ esac test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PTHREADS" && test_set_prereq PTHREADS test -z "$NO_PYTHON" && test_set_prereq PYTHON -test -n "$USE_LIBPCRE1" && test_set_prereq PCRE +test -n "$USE_LIBPCRE1$USE_LIBPCRE2" && test_set_prereq PCRE test -z "$NO_GETTEXT" && test_set_prereq GETTEXT # Can we rely on git's output in the C locale? From 5dcc969e79ebb1e9c1996ffd972bb76467d3bc84 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:37 -0700 Subject: [PATCH 422/566] notes: convert internal structures to struct object_id Convert the internal structures using unsigned char [20] to take struct object_id using the following semantic patch and the standard object_id transforms: @@ struct leaf_node E1; @@ - E1.key_sha1 + E1.key_oid.hash @@ struct leaf_node *E1; @@ - E1->key_sha1 + E1->key_oid.hash @@ struct leaf_node E1; @@ - E1.key_sha1 + E1.key_oid.hash @@ struct leaf_node *E1; @@ - E1->key_sha1 + E1->key_oid.hash @@ struct non_note E1; @@ - E1.sha1 + E1.oid.hash @@ struct non_note *E1; @@ - E1->sha1 + E1->oid.hash Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- notes.c | 98 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/notes.c b/notes.c index 542563b280..251cf11c95 100644 --- a/notes.c +++ b/notes.c @@ -35,8 +35,8 @@ struct int_node { * subtree. */ struct leaf_node { - unsigned char key_sha1[20]; - unsigned char val_sha1[20]; + struct object_id key_oid; + struct object_id val_oid; }; /* @@ -51,7 +51,7 @@ struct non_note { struct non_note *next; /* grounded (last->next == NULL) */ char *path; unsigned int mode; - unsigned char sha1[20]; + struct object_id oid; }; #define PTR_TYPE_NULL 0 @@ -100,7 +100,7 @@ static void **note_tree_search(struct notes_tree *t, struct int_node **tree, if (GET_PTR_TYPE(p) == PTR_TYPE_SUBTREE) { l = (struct leaf_node *) CLR_PTR_TYPE(p); - if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_sha1)) { + if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_oid.hash)) { /* unpack tree and resume search */ (*tree)->a[0] = NULL; load_subtree(t, l, *tree, *n); @@ -118,7 +118,7 @@ static void **note_tree_search(struct notes_tree *t, struct int_node **tree, return note_tree_search(t, tree, n, key_sha1); case PTR_TYPE_SUBTREE: l = (struct leaf_node *) CLR_PTR_TYPE(p); - if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_sha1)) { + if (!SUBTREE_SHA1_PREFIXCMP(key_sha1, l->key_oid.hash)) { /* unpack tree and resume search */ (*tree)->a[i] = NULL; load_subtree(t, l, *tree, *n); @@ -143,7 +143,7 @@ static struct leaf_node *note_tree_find(struct notes_tree *t, void **p = note_tree_search(t, &tree, &n, key_sha1); if (GET_PTR_TYPE(*p) == PTR_TYPE_NOTE) { struct leaf_node *l = (struct leaf_node *) CLR_PTR_TYPE(*p); - if (!hashcmp(key_sha1, l->key_sha1)) + if (!hashcmp(key_sha1, l->key_oid.hash)) return l; } return NULL; @@ -196,17 +196,17 @@ static void note_tree_remove(struct notes_tree *t, struct leaf_node *l; struct int_node *parent_stack[20]; unsigned char i, j; - void **p = note_tree_search(t, &tree, &n, entry->key_sha1); + void **p = note_tree_search(t, &tree, &n, entry->key_oid.hash); assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */ if (GET_PTR_TYPE(*p) != PTR_TYPE_NOTE) return; /* type mismatch, nothing to remove */ l = (struct leaf_node *) CLR_PTR_TYPE(*p); - if (hashcmp(l->key_sha1, entry->key_sha1)) + if (oidcmp(&l->key_oid, &entry->key_oid)) return; /* key mismatch, nothing to remove */ /* we have found a matching entry */ - hashcpy(entry->val_sha1, l->val_sha1); + oidcpy(&entry->val_oid, &l->val_oid); free(l); *p = SET_PTR_TYPE(NULL, PTR_TYPE_NULL); @@ -216,14 +216,14 @@ static void note_tree_remove(struct notes_tree *t, /* first, build stack of ancestors between root and current node */ parent_stack[0] = t->root; for (i = 0; i < n; i++) { - j = GET_NIBBLE(i, entry->key_sha1); + j = GET_NIBBLE(i, entry->key_oid.hash); parent_stack[i + 1] = CLR_PTR_TYPE(parent_stack[i]->a[j]); } assert(i == n && parent_stack[i] == tree); /* next, unwind stack until note_tree_consolidate() is done */ while (i > 0 && !note_tree_consolidate(parent_stack[i], parent_stack[i - 1], - GET_NIBBLE(i - 1, entry->key_sha1))) + GET_NIBBLE(i - 1, entry->key_oid.hash))) i--; } @@ -246,7 +246,7 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree, { struct int_node *new_node; struct leaf_node *l; - void **p = note_tree_search(t, &tree, &n, entry->key_sha1); + void **p = note_tree_search(t, &tree, &n, entry->key_oid.hash); int ret = 0; assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */ @@ -254,7 +254,7 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree, switch (GET_PTR_TYPE(*p)) { case PTR_TYPE_NULL: assert(!*p); - if (is_null_sha1(entry->val_sha1)) + if (is_null_oid(&entry->val_oid)) free(entry); else *p = SET_PTR_TYPE(entry, type); @@ -262,22 +262,22 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree, case PTR_TYPE_NOTE: switch (type) { case PTR_TYPE_NOTE: - if (!hashcmp(l->key_sha1, entry->key_sha1)) { + if (!oidcmp(&l->key_oid, &entry->key_oid)) { /* skip concatenation if l == entry */ - if (!hashcmp(l->val_sha1, entry->val_sha1)) + if (!oidcmp(&l->val_oid, &entry->val_oid)) return 0; - ret = combine_notes(l->val_sha1, - entry->val_sha1); - if (!ret && is_null_sha1(l->val_sha1)) + ret = combine_notes(l->val_oid.hash, + entry->val_oid.hash); + if (!ret && is_null_oid(&l->val_oid)) note_tree_remove(t, tree, n, entry); free(entry); return ret; } break; case PTR_TYPE_SUBTREE: - if (!SUBTREE_SHA1_PREFIXCMP(l->key_sha1, - entry->key_sha1)) { + if (!SUBTREE_SHA1_PREFIXCMP(l->key_oid.hash, + entry->key_oid.hash)) { /* unpack 'entry' */ load_subtree(t, entry, tree, n); free(entry); @@ -287,7 +287,7 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree, } break; case PTR_TYPE_SUBTREE: - if (!SUBTREE_SHA1_PREFIXCMP(entry->key_sha1, l->key_sha1)) { + if (!SUBTREE_SHA1_PREFIXCMP(entry->key_oid.hash, l->key_oid.hash)) { /* unpack 'l' and restart insert */ *p = NULL; load_subtree(t, l, tree, n); @@ -301,7 +301,7 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree, /* non-matching leaf_node */ assert(GET_PTR_TYPE(*p) == PTR_TYPE_NOTE || GET_PTR_TYPE(*p) == PTR_TYPE_SUBTREE); - if (is_null_sha1(entry->val_sha1)) { /* skip insertion of empty note */ + if (is_null_oid(&entry->val_oid)) { /* skip insertion of empty note */ free(entry); return 0; } @@ -373,7 +373,7 @@ static void add_non_note(struct notes_tree *t, char *path, n->next = NULL; n->path = path; n->mode = mode; - hashcpy(n->sha1, sha1); + hashcpy(n->oid.hash, sha1); t->prev_non_note = n; if (!t->first_non_note) { @@ -399,7 +399,7 @@ static void add_non_note(struct notes_tree *t, char *path, if (non_note_cmp(p, n) == 0) { /* n ~= p; overwrite p with n */ assert(strcmp(p->path, n->path) == 0); p->mode = n->mode; - hashcpy(p->sha1, n->sha1); + oidcpy(&p->oid, &n->oid); free(n); t->prev_non_note = p; return; @@ -422,14 +422,14 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, unsigned char type; struct leaf_node *l; - buf = fill_tree_descriptor(&desc, subtree->val_sha1); + buf = fill_tree_descriptor(&desc, subtree->val_oid.hash); if (!buf) die("Could not read %s for notes-index", - sha1_to_hex(subtree->val_sha1)); + oid_to_hex(&subtree->val_oid)); - prefix_len = subtree->key_sha1[19]; + prefix_len = subtree->key_oid.hash[19]; assert(prefix_len * 2 >= n); - memcpy(object_sha1, subtree->key_sha1, prefix_len); + memcpy(object_sha1, subtree->key_oid.hash, prefix_len); while (tree_entry(&desc, &entry)) { path_len = strlen(entry.path); len = get_sha1_hex_segment(entry.path, path_len, @@ -447,12 +447,12 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, type = PTR_TYPE_NOTE; l = (struct leaf_node *) xcalloc(1, sizeof(struct leaf_node)); - hashcpy(l->key_sha1, object_sha1); - hashcpy(l->val_sha1, entry.oid->hash); + hashcpy(l->key_oid.hash, object_sha1); + oidcpy(&l->val_oid, entry.oid); if (len < 20) { if (!S_ISDIR(entry.mode) || path_len != 2) goto handle_non_note; /* not subtree */ - l->key_sha1[19] = (unsigned char) len; + l->key_oid.hash[19] = (unsigned char) len; type = PTR_TYPE_SUBTREE; } if (note_tree_insert(t, node, n, l, type, @@ -460,7 +460,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, die("Failed to load %s %s into notes tree " "from %s", type == PTR_TYPE_NOTE ? "note" : "subtree", - sha1_to_hex(l->key_sha1), t->ref); + oid_to_hex(&l->key_oid), t->ref); } continue; @@ -486,7 +486,7 @@ handle_non_note: */ { struct strbuf non_note_path = STRBUF_INIT; - const char *q = sha1_to_hex(subtree->key_sha1); + const char *q = oid_to_hex(&subtree->key_oid); int i; for (i = 0; i < prefix_len; i++) { strbuf_addch(&non_note_path, *q++); @@ -599,15 +599,17 @@ redo: flags & FOR_EACH_NOTE_YIELD_SUBTREES) { /* invoke callback with subtree */ unsigned int path_len = - l->key_sha1[19] * 2 + fanout; + l->key_oid.hash[19] * 2 + fanout; assert(path_len < FANOUT_PATH_MAX - 1); - construct_path_with_fanout(l->key_sha1, fanout, + construct_path_with_fanout(l->key_oid.hash, + fanout, path); /* Create trailing slash, if needed */ if (path[path_len - 1] != '/') path[path_len++] = '/'; path[path_len] = '\0'; - ret = fn(l->key_sha1, l->val_sha1, path, + ret = fn(l->key_oid.hash, l->val_oid.hash, + path, cb_data); } if (n > fanout * 2 || @@ -621,8 +623,10 @@ redo: break; case PTR_TYPE_NOTE: l = (struct leaf_node *) CLR_PTR_TYPE(p); - construct_path_with_fanout(l->key_sha1, fanout, path); - ret = fn(l->key_sha1, l->val_sha1, path, cb_data); + construct_path_with_fanout(l->key_oid.hash, fanout, + path); + ret = fn(l->key_oid.hash, l->val_oid.hash, path, + cb_data); break; } if (ret) @@ -742,7 +746,7 @@ static int write_each_non_note_until(const char *note_path, ; /* do nothing, prefer note to non-note */ else { ret = write_each_note_helper(d->root, n->path, n->mode, - n->sha1); + n->oid.hash); if (ret) return ret; } @@ -1027,8 +1031,8 @@ void init_notes(struct notes_tree *t, const char *notes_ref, die("Failed to read notes tree referenced by %s (%s)", notes_ref, oid_to_hex(&object_oid)); - hashclr(root_tree.key_sha1); - hashcpy(root_tree.val_sha1, oid.hash); + oidclr(&root_tree.key_oid); + oidcpy(&root_tree.val_oid, &oid); load_subtree(t, &root_tree, t->root, 0); } @@ -1092,8 +1096,8 @@ int add_note(struct notes_tree *t, const unsigned char *object_sha1, if (!combine_notes) combine_notes = t->combine_notes; l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node)); - hashcpy(l->key_sha1, object_sha1); - hashcpy(l->val_sha1, note_sha1); + hashcpy(l->key_oid.hash, object_sha1); + hashcpy(l->val_oid.hash, note_sha1); return note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes); } @@ -1104,10 +1108,10 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1) if (!t) t = &default_notes_tree; assert(t->initialized); - hashcpy(l.key_sha1, object_sha1); - hashclr(l.val_sha1); + hashcpy(l.key_oid.hash, object_sha1); + oidclr(&l.val_oid); note_tree_remove(t, t->root, 0, &l); - if (is_null_sha1(l.val_sha1)) /* no note was removed */ + if (is_null_oid(&l.val_oid)) /* no note was removed */ return 1; t->dirty = 1; return 0; @@ -1122,7 +1126,7 @@ const unsigned char *get_note(struct notes_tree *t, t = &default_notes_tree; assert(t->initialized); found = note_tree_find(t, t->root, 0, object_sha1); - return found ? found->val_sha1 : NULL; + return found ? found->val_oid.hash : NULL; } int for_each_note(struct notes_tree *t, int flags, each_note_fn fn, From 89c149f55b93e052e46e46ebc4d7e76b0b25a90c Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:38 -0700 Subject: [PATCH 423/566] notes: convert internal parts to struct object_id Convert several portions of the internals of the code to struct object_id. Introduce two macros to denote the different constants in the code: KEY_INDEX for the last byte of the object ID, and FANOUT_PATH_SEPARATORS for the number of possible path separators (on Unix, "/"). While these constants are both 19 (one less than the number of bytes in the hash), distinguish them to make the code more understandable, and define them logically based on their intended purpose. Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- notes.c | 64 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/notes.c b/notes.c index 251cf11c95..babe0c0ebf 100644 --- a/notes.c +++ b/notes.c @@ -65,8 +65,10 @@ struct non_note { #define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f) +#define KEY_INDEX (GIT_SHA1_RAWSZ - 1) +#define FANOUT_PATH_SEPARATORS ((GIT_SHA1_HEXSZ / 2) - 1) #define SUBTREE_SHA1_PREFIXCMP(key_sha1, subtree_sha1) \ - (memcmp(key_sha1, subtree_sha1, subtree_sha1[19])) + (memcmp(key_sha1, subtree_sha1, subtree_sha1[KEY_INDEX])) struct notes_tree default_notes_tree; @@ -194,7 +196,7 @@ static void note_tree_remove(struct notes_tree *t, struct leaf_node *entry) { struct leaf_node *l; - struct int_node *parent_stack[20]; + struct int_node *parent_stack[GIT_SHA1_RAWSZ]; unsigned char i, j; void **p = note_tree_search(t, &tree, &n, entry->key_oid.hash); @@ -341,21 +343,21 @@ static void note_tree_free(struct int_node *tree) * Otherwise, returns number of bytes written to sha1 (i.e. hex_len / 2). * Pads sha1 with NULs up to sha1_len (not included in returned length). */ -static int get_sha1_hex_segment(const char *hex, unsigned int hex_len, - unsigned char *sha1, unsigned int sha1_len) +static int get_oid_hex_segment(const char *hex, unsigned int hex_len, + unsigned char *oid, unsigned int oid_len) { unsigned int i, len = hex_len >> 1; - if (hex_len % 2 != 0 || len > sha1_len) + if (hex_len % 2 != 0 || len > oid_len) return -1; for (i = 0; i < len; i++) { unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]); if (val & ~0xff) return -1; - *sha1++ = val; + *oid++ = val; hex += 2; } - for (; i < sha1_len; i++) - *sha1++ = 0; + for (; i < oid_len; i++) + *oid++ = 0; return len; } @@ -413,7 +415,7 @@ static void add_non_note(struct notes_tree *t, char *path, static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, struct int_node *node, unsigned int n) { - unsigned char object_sha1[20]; + struct object_id object_oid; unsigned int prefix_len; void *buf; struct tree_desc desc; @@ -427,13 +429,13 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, die("Could not read %s for notes-index", oid_to_hex(&subtree->val_oid)); - prefix_len = subtree->key_oid.hash[19]; + prefix_len = subtree->key_oid.hash[KEY_INDEX]; assert(prefix_len * 2 >= n); - memcpy(object_sha1, subtree->key_oid.hash, prefix_len); + memcpy(object_oid.hash, subtree->key_oid.hash, prefix_len); while (tree_entry(&desc, &entry)) { path_len = strlen(entry.path); - len = get_sha1_hex_segment(entry.path, path_len, - object_sha1 + prefix_len, 20 - prefix_len); + len = get_oid_hex_segment(entry.path, path_len, + object_oid.hash + prefix_len, GIT_SHA1_RAWSZ - prefix_len); if (len < 0) goto handle_non_note; /* entry.path is not a SHA1 */ len += prefix_len; @@ -443,16 +445,16 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, * If object SHA1 is incomplete (len < 20), and current * component consists of 2 hex chars, assume note subtree */ - if (len <= 20) { + if (len <= GIT_SHA1_RAWSZ) { type = PTR_TYPE_NOTE; l = (struct leaf_node *) xcalloc(1, sizeof(struct leaf_node)); - hashcpy(l->key_oid.hash, object_sha1); + oidcpy(&l->key_oid, &object_oid); oidcpy(&l->val_oid, entry.oid); - if (len < 20) { + if (len < GIT_SHA1_RAWSZ) { if (!S_ISDIR(entry.mode) || path_len != 2) goto handle_non_note; /* not subtree */ - l->key_oid.hash[19] = (unsigned char) len; + l->key_oid.hash[KEY_INDEX] = (unsigned char) len; type = PTR_TYPE_SUBTREE; } if (note_tree_insert(t, node, n, l, type, @@ -542,14 +544,14 @@ static unsigned char determine_fanout(struct int_node *tree, unsigned char n, } /* hex SHA1 + 19 * '/' + NUL */ -#define FANOUT_PATH_MAX 40 + 19 + 1 +#define FANOUT_PATH_MAX GIT_SHA1_HEXSZ + FANOUT_PATH_SEPARATORS + 1 static void construct_path_with_fanout(const unsigned char *sha1, unsigned char fanout, char *path) { unsigned int i = 0, j = 0; const char *hex_sha1 = sha1_to_hex(sha1); - assert(fanout < 20); + assert(fanout < GIT_SHA1_RAWSZ); while (fanout) { path[i++] = hex_sha1[j++]; path[i++] = hex_sha1[j++]; @@ -599,7 +601,7 @@ redo: flags & FOR_EACH_NOTE_YIELD_SUBTREES) { /* invoke callback with subtree */ unsigned int path_len = - l->key_oid.hash[19] * 2 + fanout; + l->key_oid.hash[KEY_INDEX] * 2 + fanout; assert(path_len < FANOUT_PATH_MAX - 1); construct_path_with_fanout(l->key_oid.hash, fanout, @@ -654,7 +656,7 @@ static void write_tree_entry(struct strbuf *buf, unsigned int mode, unsigned char *sha1) { strbuf_addf(buf, "%o %.*s%c", mode, path_len, path, '\0'); - strbuf_add(buf, sha1, 20); + strbuf_add(buf, sha1, GIT_SHA1_RAWSZ); } static void tree_write_stack_init_subtree(struct tree_write_stack *tws, @@ -666,7 +668,7 @@ static void tree_write_stack_init_subtree(struct tree_write_stack *tws, n = (struct tree_write_stack *) xmalloc(sizeof(struct tree_write_stack)); n->next = NULL; - strbuf_init(&n->buf, 256 * (32 + 40)); /* assume 256 entries per tree */ + strbuf_init(&n->buf, 256 * (32 + GIT_SHA1_HEXSZ)); /* assume 256 entries per tree */ n->path[0] = n->path[1] = '\0'; tws->next = n; tws->path[0] = path[0]; @@ -677,18 +679,18 @@ static int tree_write_stack_finish_subtree(struct tree_write_stack *tws) { int ret; struct tree_write_stack *n = tws->next; - unsigned char s[20]; + struct object_id s; if (n) { ret = tree_write_stack_finish_subtree(n); if (ret) return ret; - ret = write_sha1_file(n->buf.buf, n->buf.len, tree_type, s); + ret = write_sha1_file(n->buf.buf, n->buf.len, tree_type, s.hash); if (ret) return ret; strbuf_release(&n->buf); free(n); tws->next = NULL; - write_tree_entry(&tws->buf, 040000, tws->path, 2, s); + write_tree_entry(&tws->buf, 040000, tws->path, 2, s.hash); tws->path[0] = tws->path[1] = '\0'; } return 0; @@ -771,7 +773,7 @@ static int write_each_note(const unsigned char *object_sha1, note_path[note_path_len] = '\0'; mode = 040000; } - assert(note_path_len <= 40 + 19); + assert(note_path_len <= GIT_SHA1_HEXSZ + FANOUT_PATH_SEPARATORS); /* Weave non-note entries into note entries */ return write_each_non_note_until(note_path, d) || @@ -946,8 +948,8 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob) if (has_glob_specials(glob)) { for_each_glob_ref(string_list_add_one_ref, glob, list); } else { - unsigned char sha1[20]; - if (get_sha1(glob, sha1)) + struct object_id oid; + if (get_oid(glob, &oid)) warning("notes ref %s is invalid", glob); if (!unsorted_string_list_has_string(list, glob)) string_list_append(list, glob); @@ -1150,7 +1152,7 @@ int write_notes_tree(struct notes_tree *t, unsigned char *result) /* Prepare for traversal of current notes tree */ root.next = NULL; /* last forward entry in list is grounded */ - strbuf_init(&root.buf, 256 * (32 + 40)); /* assume 256 entries */ + strbuf_init(&root.buf, 256 * (32 + GIT_SHA1_HEXSZ)); /* assume 256 entries */ root.path[0] = root.path[1] = '\0'; cb_data.root = &root; cb_data.next_non_note = t->first_non_note; @@ -1315,9 +1317,9 @@ void expand_notes_ref(struct strbuf *sb) void expand_loose_notes_ref(struct strbuf *sb) { - unsigned char object[20]; + struct object_id object; - if (get_sha1(sb->buf, object)) { + if (get_oid(sb->buf, &object)) { /* fallback to expand_notes_ref */ expand_notes_ref(sb); } From 490bc83a01acfefa11e98f8852b1f4a9dd962331 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:39 -0700 Subject: [PATCH 424/566] notes: convert for_each_note to struct object_id Convert for_each_note and each of the callbacks to use struct object_id. Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/notes.c | 6 +++--- notes.c | 24 ++++++++++++------------ notes.h | 4 ++-- remote-testsvn.c | 8 ++++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/builtin/notes.c b/builtin/notes.c index f2847c41e0..53fe6d34d4 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -109,11 +109,11 @@ static void free_note_data(struct note_data *d) strbuf_release(&d->buf); } -static int list_each_note(const unsigned char *object_sha1, - const unsigned char *note_sha1, char *note_path, +static int list_each_note(const struct object_id *object_oid, + const struct object_id *note_oid, char *note_path, void *cb_data) { - printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1)); + printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid)); return 0; } diff --git a/notes.c b/notes.c index babe0c0ebf..e881c10ee9 100644 --- a/notes.c +++ b/notes.c @@ -610,7 +610,7 @@ redo: if (path[path_len - 1] != '/') path[path_len++] = '/'; path[path_len] = '\0'; - ret = fn(l->key_oid.hash, l->val_oid.hash, + ret = fn(&l->key_oid, &l->val_oid, path, cb_data); } @@ -627,7 +627,7 @@ redo: l = (struct leaf_node *) CLR_PTR_TYPE(p); construct_path_with_fanout(l->key_oid.hash, fanout, path); - ret = fn(l->key_oid.hash, l->val_oid.hash, path, + ret = fn(&l->key_oid, &l->val_oid, path, cb_data); break; } @@ -698,7 +698,7 @@ static int tree_write_stack_finish_subtree(struct tree_write_stack *tws) static int write_each_note_helper(struct tree_write_stack *tws, const char *path, unsigned int mode, - const unsigned char *sha1) + const struct object_id *oid) { size_t path_len = strlen(path); unsigned int n = 0; @@ -728,7 +728,7 @@ static int write_each_note_helper(struct tree_write_stack *tws, /* Finally add given entry to the current tree object */ write_tree_entry(&tws->buf, mode, path + 3 * n, path_len - (3 * n), - sha1); + oid->hash); return 0; } @@ -748,7 +748,7 @@ static int write_each_non_note_until(const char *note_path, ; /* do nothing, prefer note to non-note */ else { ret = write_each_note_helper(d->root, n->path, n->mode, - n->oid.hash); + &n->oid); if (ret) return ret; } @@ -758,8 +758,8 @@ static int write_each_non_note_until(const char *note_path, return 0; } -static int write_each_note(const unsigned char *object_sha1, - const unsigned char *note_sha1, char *note_path, +static int write_each_note(const struct object_id *object_oid, + const struct object_id *note_oid, char *note_path, void *cb_data) { struct write_each_note_data *d = @@ -777,7 +777,7 @@ static int write_each_note(const unsigned char *object_sha1, /* Weave non-note entries into note entries */ return write_each_non_note_until(note_path, d) || - write_each_note_helper(d->root, note_path, mode, note_sha1); + write_each_note_helper(d->root, note_path, mode, note_oid); } struct note_delete_list { @@ -785,20 +785,20 @@ struct note_delete_list { const unsigned char *sha1; }; -static int prune_notes_helper(const unsigned char *object_sha1, - const unsigned char *note_sha1, char *note_path, +static int prune_notes_helper(const struct object_id *object_oid, + const struct object_id *note_oid, char *note_path, void *cb_data) { struct note_delete_list **l = (struct note_delete_list **) cb_data; struct note_delete_list *n; - if (has_sha1_file(object_sha1)) + if (has_object_file(object_oid)) return 0; /* nothing to do for this note */ /* failed to find object => prune this note */ n = (struct note_delete_list *) xmalloc(sizeof(*n)); n->next = *l; - n->sha1 = object_sha1; + n->sha1 = object_oid->hash; *l = n; return 0; } diff --git a/notes.h b/notes.h index 5345642cfd..6651673ae1 100644 --- a/notes.h +++ b/notes.h @@ -202,8 +202,8 @@ int copy_note(struct notes_tree *t, * - copy_note() * - free_notes() */ -typedef int each_note_fn(const unsigned char *object_sha1, - const unsigned char *note_sha1, char *note_path, +typedef int each_note_fn(const struct object_id *object_oid, + const struct object_id *note_oid, char *note_path, void *cb_data); int for_each_note(struct notes_tree *t, int flags, each_note_fn fn, void *cb_data); diff --git a/remote-testsvn.c b/remote-testsvn.c index f87bf851ba..793c4ad1d9 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -99,8 +99,8 @@ static int parse_rev_note(const char *msg, struct rev_note *res) return -1; } -static int note2mark_cb(const unsigned char *object_sha1, - const unsigned char *note_sha1, char *note_path, +static int note2mark_cb(const struct object_id *object_oid, + const struct object_id *note_oid, char *note_path, void *cb_data) { FILE *file = (FILE *)cb_data; @@ -109,14 +109,14 @@ static int note2mark_cb(const unsigned char *object_sha1, enum object_type type; struct rev_note note; - if (!(msg = read_sha1_file(note_sha1, &type, &msglen)) || + if (!(msg = read_sha1_file(note_oid->hash, &type, &msglen)) || !msglen || type != OBJ_BLOB) { free(msg); return 1; } if (parse_rev_note(msg, ¬e)) return 2; - if (fprintf(file, ":%d %s\n", note.rev_nr, sha1_to_hex(object_sha1)) < 1) + if (fprintf(file, ":%d %s\n", note.rev_nr, oid_to_hex(object_oid)) < 1) return 3; return 0; } From 9ef7223058a44990dc4650ecb1209c97ceb636b3 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:40 -0700 Subject: [PATCH 425/566] notes: make get_note return pointer to struct object_id Make get_note return a pointer to a const struct object_id. Add a defensive check to ensure we don't accidentally dereference a NULL pointer. Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/notes.c | 22 +++++++++++----------- notes-cache.c | 8 ++++---- notes.c | 18 +++++++++--------- notes.h | 2 +- remote-testsvn.c | 6 +++--- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/builtin/notes.c b/builtin/notes.c index 53fe6d34d4..3d9005b8f4 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -351,7 +351,7 @@ static int list(int argc, const char **argv, const char *prefix) { struct notes_tree *t; unsigned char object[20]; - const unsigned char *note; + const struct object_id *note; int retval = -1; struct option options[] = { OPT_END() @@ -372,7 +372,7 @@ static int list(int argc, const char **argv, const char *prefix) die(_("failed to resolve '%s' as a valid ref."), argv[0]); note = get_note(t, object); if (note) { - puts(sha1_to_hex(note)); + puts(oid_to_hex(note)); retval = 0; } else retval = error(_("no note found for object %s."), @@ -392,7 +392,7 @@ static int add(int argc, const char **argv, const char *prefix) const char *object_ref; struct notes_tree *t; unsigned char object[20], new_note[20]; - const unsigned char *note; + const struct object_id *note; struct note_data d = { 0, 0, NULL, STRBUF_INIT }; struct option options[] = { { OPTION_CALLBACK, 'm', "message", &d, N_("message"), @@ -453,7 +453,7 @@ static int add(int argc, const char **argv, const char *prefix) sha1_to_hex(object)); } - prepare_note_data(object, &d, note); + prepare_note_data(object, &d, note->hash); if (d.buf.len || allow_empty) { write_note_data(&d, new_note); if (add_note(t, object, new_note, combine_notes_overwrite)) @@ -474,7 +474,7 @@ static int add(int argc, const char **argv, const char *prefix) static int copy(int argc, const char **argv, const char *prefix) { int retval = 0, force = 0, from_stdin = 0; - const unsigned char *from_note, *note; + const struct object_id *from_note, *note; const char *object_ref; unsigned char object[20], from_obj[20]; struct notes_tree *t; @@ -539,7 +539,7 @@ static int copy(int argc, const char **argv, const char *prefix) goto out; } - if (add_note(t, object, from_note, combine_notes_overwrite)) + if (add_note(t, object, from_note->hash, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: @@ -553,7 +553,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) const char *object_ref; struct notes_tree *t; unsigned char object[20], new_note[20]; - const unsigned char *note; + const struct object_id *note; char *logmsg; const char * const *usage; struct note_data d = { 0, 0, NULL, STRBUF_INIT }; @@ -598,13 +598,13 @@ static int append_edit(int argc, const char **argv, const char *prefix) t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); note = get_note(t, object); - prepare_note_data(object, &d, edit ? note : NULL); + prepare_note_data(object, &d, edit && note ? note->hash : NULL); if (note && !edit) { /* Append buf to previous note contents */ unsigned long size; enum object_type type; - char *prev_buf = read_sha1_file(note, &type, &size); + char *prev_buf = read_sha1_file(note->hash, &type, &size); strbuf_grow(&d.buf, size + 1); if (d.buf.len && prev_buf && size) @@ -638,7 +638,7 @@ static int show(int argc, const char **argv, const char *prefix) const char *object_ref; struct notes_tree *t; unsigned char object[20]; - const unsigned char *note; + const struct object_id *note; int retval; struct option options[] = { OPT_END() @@ -664,7 +664,7 @@ static int show(int argc, const char **argv, const char *prefix) retval = error(_("no note found for object %s."), sha1_to_hex(object)); else { - const char *show_args[3] = {"show", sha1_to_hex(note), NULL}; + const char *show_args[3] = {"show", oid_to_hex(note), NULL}; retval = execv_git_cmd(show_args); } free_notes(t); diff --git a/notes-cache.c b/notes-cache.c index 2843e98576..6e84a748f0 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -69,15 +69,15 @@ int notes_cache_write(struct notes_cache *c) char *notes_cache_get(struct notes_cache *c, struct object_id *key_oid, size_t *outsize) { - const unsigned char *value_sha1; + const struct object_id *value_oid; enum object_type type; char *value; unsigned long size; - value_sha1 = get_note(&c->tree, key_oid->hash); - if (!value_sha1) + value_oid = get_note(&c->tree, key_oid->hash); + if (!value_oid) return NULL; - value = read_sha1_file(value_sha1, &type, &size); + value = read_sha1_file(value_oid->hash, &type, &size); *outsize = size; return value; diff --git a/notes.c b/notes.c index e881c10ee9..fe4db2c1ec 100644 --- a/notes.c +++ b/notes.c @@ -1119,7 +1119,7 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1) return 0; } -const unsigned char *get_note(struct notes_tree *t, +const struct object_id *get_note(struct notes_tree *t, const unsigned char *object_sha1) { struct leaf_node *found; @@ -1128,7 +1128,7 @@ const unsigned char *get_note(struct notes_tree *t, t = &default_notes_tree; assert(t->initialized); found = note_tree_find(t, t->root, 0, object_sha1); - return found ? found->val_oid.hash : NULL; + return found ? &found->val_oid : NULL; } int for_each_note(struct notes_tree *t, int flags, each_note_fn fn, @@ -1219,7 +1219,7 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1, struct strbuf *sb, const char *output_encoding, int raw) { static const char utf8[] = "utf-8"; - const unsigned char *sha1; + const struct object_id *oid; char *msg, *msg_p; unsigned long linelen, msglen; enum object_type type; @@ -1229,11 +1229,11 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1, if (!t->initialized) init_notes(t, NULL, NULL, 0); - sha1 = get_note(t, object_sha1); - if (!sha1) + oid = get_note(t, object_sha1); + if (!oid) return; - if (!(msg = read_sha1_file(sha1, &type, &msglen)) || type != OBJ_BLOB) { + if (!(msg = read_sha1_file(oid->hash, &type, &msglen)) || type != OBJ_BLOB) { free(msg); return; } @@ -1291,14 +1291,14 @@ int copy_note(struct notes_tree *t, const unsigned char *from_obj, const unsigned char *to_obj, int force, combine_notes_fn combine_notes) { - const unsigned char *note = get_note(t, from_obj); - const unsigned char *existing_note = get_note(t, to_obj); + const struct object_id *note = get_note(t, from_obj); + const struct object_id *existing_note = get_note(t, to_obj); if (!force && existing_note) return 1; if (note) - return add_note(t, to_obj, note, combine_notes); + return add_note(t, to_obj, note->hash, combine_notes); else if (existing_note) return add_note(t, to_obj, null_sha1, combine_notes); diff --git a/notes.h b/notes.h index 6651673ae1..c72bb97106 100644 --- a/notes.h +++ b/notes.h @@ -140,7 +140,7 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1); * * Return NULL if the given object has no notes. */ -const unsigned char *get_note(struct notes_tree *t, +const struct object_id *get_note(struct notes_tree *t, const unsigned char *object_sha1); /* diff --git a/remote-testsvn.c b/remote-testsvn.c index 793c4ad1d9..017af1bd5f 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -53,15 +53,15 @@ static void terminate_batch(void) /* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */ static char *read_ref_note(const unsigned char sha1[20]) { - const unsigned char *note_sha1; + const struct object_id *note_oid; char *msg = NULL; unsigned long msglen; enum object_type type; init_notes(NULL, notes_ref, NULL, 0); - if (!(note_sha1 = get_note(NULL, sha1))) + if (!(note_oid = get_note(NULL, sha1))) return NULL; /* note tree not found */ - if (!(msg = read_sha1_file(note_sha1, &type, &msglen))) + if (!(msg = read_sha1_file(note_oid->hash, &type, &msglen))) error("Empty notes tree. %s", notes_ref); else if (!msglen || type != OBJ_BLOB) { error("Note contains unusable content. " From fb61e4d3abe2349eb0fabe0be8981626b4074fa7 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:41 -0700 Subject: [PATCH 426/566] notes: convert format_display_notes to struct object_id Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- log-tree.c | 2 +- notes.c | 8 ++++---- notes.h | 2 +- revision.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/log-tree.c b/log-tree.c index a4ec11c2bf..9c0c64a2d8 100644 --- a/log-tree.c +++ b/log-tree.c @@ -655,7 +655,7 @@ void show_log(struct rev_info *opt) struct strbuf notebuf = STRBUF_INIT; raw = (opt->commit_format == CMIT_FMT_USERFORMAT); - format_display_notes(commit->object.oid.hash, ¬ebuf, + format_display_notes(&commit->object.oid, ¬ebuf, get_log_output_encoding(), raw); ctx.notes_message = notebuf.len ? strbuf_detach(¬ebuf, NULL) diff --git a/notes.c b/notes.c index fe4db2c1ec..b5cabafde6 100644 --- a/notes.c +++ b/notes.c @@ -1215,7 +1215,7 @@ void free_notes(struct notes_tree *t) * (raw != 0) gives the %N userformat; otherwise, the note message is given * for human consumption. */ -static void format_note(struct notes_tree *t, const unsigned char *object_sha1, +static void format_note(struct notes_tree *t, const struct object_id *object_oid, struct strbuf *sb, const char *output_encoding, int raw) { static const char utf8[] = "utf-8"; @@ -1229,7 +1229,7 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1, if (!t->initialized) init_notes(t, NULL, NULL, 0); - oid = get_note(t, object_sha1); + oid = get_note(t, object_oid->hash); if (!oid) return; @@ -1277,13 +1277,13 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1, free(msg); } -void format_display_notes(const unsigned char *object_sha1, +void format_display_notes(const struct object_id *object_oid, struct strbuf *sb, const char *output_encoding, int raw) { int i; assert(display_notes_trees); for (i = 0; display_notes_trees[i]; i++) - format_note(display_notes_trees[i], object_sha1, sb, + format_note(display_notes_trees[i], object_oid, sb, output_encoding, raw); } diff --git a/notes.h b/notes.h index c72bb97106..a66532103c 100644 --- a/notes.h +++ b/notes.h @@ -277,7 +277,7 @@ void init_display_notes(struct display_notes_opt *opt); * * You *must* call init_display_notes() before using this function. */ -void format_display_notes(const unsigned char *object_sha1, +void format_display_notes(const struct object_id *object_oid, struct strbuf *sb, const char *output_encoding, int raw); /* diff --git a/revision.c b/revision.c index b023945309..475d5b2dc1 100644 --- a/revision.c +++ b/revision.c @@ -2908,7 +2908,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt) if (opt->show_notes) { if (!buf.len) strbuf_addstr(&buf, message); - format_display_notes(commit->object.oid.hash, &buf, encoding, 1); + format_display_notes(&commit->object.oid, &buf, encoding, 1); } /* From bb7e4739712e3f9eee0dfd60088b6d8983067960 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:42 -0700 Subject: [PATCH 427/566] builtin/notes: convert to struct object_id Convert most of the static functions to use struct object_id. In addition, convert copy_notes_for_rewrite and its callers. Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/am.c | 2 +- builtin/commit.c | 2 +- builtin/notes.c | 110 +++++++++++++++++++++++------------------------ notes-utils.c | 4 +- notes-utils.h | 2 +- 5 files changed, 60 insertions(+), 60 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 0f63dcab16..d9fdddac4a 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -563,7 +563,7 @@ static int copy_notes_for_rebase(const struct am_state *state) goto finish; } - if (copy_note_for_rewrite(c, from_obj.hash, to_obj.hash)) + if (copy_note_for_rewrite(c, &from_obj, &to_obj)) ret = error(_("Failed to copy notes from '%s' to '%s'"), oid_to_hex(&from_obj), oid_to_hex(&to_obj)); } diff --git a/builtin/commit.c b/builtin/commit.c index da1ba4c862..7587810045 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1809,7 +1809,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) cfg = init_copy_notes_for_rewrite("amend"); if (cfg) { /* we are amending, so current_head is not NULL */ - copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash); + copy_note_for_rewrite(cfg, ¤t_head->object.oid, &oid); finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'"); } run_rewrite_hook(¤t_head->object.oid, &oid); diff --git a/builtin/notes.c b/builtin/notes.c index 3d9005b8f4..7947a16ede 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -129,10 +129,10 @@ static void copy_obj_to_fd(int fd, const unsigned char *sha1) } } -static void write_commented_object(int fd, const unsigned char *object) +static void write_commented_object(int fd, const struct object_id *object) { const char *show_args[5] = - {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL}; + {"show", "--stat", "--no-notes", oid_to_hex(object), NULL}; struct child_process show = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; struct strbuf cbuf = STRBUF_INIT; @@ -145,7 +145,7 @@ static void write_commented_object(int fd, const unsigned char *object) show.git_cmd = 1; if (start_command(&show)) die(_("unable to start 'show' for object '%s'"), - sha1_to_hex(object)); + oid_to_hex(object)); if (strbuf_read(&buf, show.out, 0) < 0) die_errno(_("could not read 'show' output")); @@ -157,10 +157,10 @@ static void write_commented_object(int fd, const unsigned char *object) if (finish_command(&show)) die(_("failed to finish 'show' for object '%s'"), - sha1_to_hex(object)); + oid_to_hex(object)); } -static void prepare_note_data(const unsigned char *object, struct note_data *d, +static void prepare_note_data(const struct object_id *object, struct note_data *d, const unsigned char *old_note) { if (d->use_editor || !d->given) { @@ -243,16 +243,16 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) { struct note_data *d = opt->value; char *buf; - unsigned char object[20]; + struct object_id object; enum object_type type; unsigned long len; if (d->buf.len) strbuf_addch(&d->buf, '\n'); - if (get_sha1(arg, object)) + if (get_oid(arg, &object)) die(_("failed to resolve '%s' as a valid ref."), arg); - if (!(buf = read_sha1_file(object, &type, &len))) { + if (!(buf = read_sha1_file(object.hash, &type, &len))) { free(buf); die(_("failed to read object '%s'."), arg); } @@ -292,7 +292,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) } while (strbuf_getline_lf(&buf, stdin) != EOF) { - unsigned char from_obj[20], to_obj[20]; + struct object_id from_obj, to_obj; struct strbuf **split; int err; @@ -301,15 +301,15 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) die(_("malformed input line: '%s'."), buf.buf); strbuf_rtrim(split[0]); strbuf_rtrim(split[1]); - if (get_sha1(split[0]->buf, from_obj)) + if (get_oid(split[0]->buf, &from_obj)) die(_("failed to resolve '%s' as a valid ref."), split[0]->buf); - if (get_sha1(split[1]->buf, to_obj)) + if (get_oid(split[1]->buf, &to_obj)) die(_("failed to resolve '%s' as a valid ref."), split[1]->buf); if (rewrite_cmd) - err = copy_note_for_rewrite(c, from_obj, to_obj); + err = copy_note_for_rewrite(c, &from_obj, &to_obj); else - err = copy_note(t, from_obj, to_obj, force, + err = copy_note(t, from_obj.hash, to_obj.hash, force, combine_notes_overwrite); if (err) { @@ -350,7 +350,7 @@ static struct notes_tree *init_notes_check(const char *subcommand, static int list(int argc, const char **argv, const char *prefix) { struct notes_tree *t; - unsigned char object[20]; + struct object_id object; const struct object_id *note; int retval = -1; struct option options[] = { @@ -368,15 +368,15 @@ static int list(int argc, const char **argv, const char *prefix) t = init_notes_check("list", 0); if (argc) { - if (get_sha1(argv[0], object)) + if (get_oid(argv[0], &object)) die(_("failed to resolve '%s' as a valid ref."), argv[0]); - note = get_note(t, object); + note = get_note(t, object.hash); if (note) { puts(oid_to_hex(note)); retval = 0; } else retval = error(_("no note found for object %s."), - sha1_to_hex(object)); + oid_to_hex(&object)); } else retval = for_each_note(t, 0, list_each_note, NULL); @@ -391,7 +391,7 @@ static int add(int argc, const char **argv, const char *prefix) int force = 0, allow_empty = 0; const char *object_ref; struct notes_tree *t; - unsigned char object[20], new_note[20]; + struct object_id object, new_note; const struct object_id *note; struct note_data d = { 0, 0, NULL, STRBUF_INIT }; struct option options[] = { @@ -423,11 +423,11 @@ static int add(int argc, const char **argv, const char *prefix) object_ref = argc > 1 ? argv[1] : "HEAD"; - if (get_sha1(object_ref, object)) + if (get_oid(object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("add", NOTES_INIT_WRITABLE); - note = get_note(t, object); + note = get_note(t, object.hash); if (note) { if (!force) { @@ -437,7 +437,7 @@ static int add(int argc, const char **argv, const char *prefix) return error(_("Cannot add notes. " "Found existing notes for object %s. " "Use '-f' to overwrite existing notes"), - sha1_to_hex(object)); + oid_to_hex(&object)); } /* * Redirect to "edit" subcommand. @@ -450,19 +450,19 @@ static int add(int argc, const char **argv, const char *prefix) return append_edit(argc, argv, prefix); } fprintf(stderr, _("Overwriting existing notes for object %s\n"), - sha1_to_hex(object)); + oid_to_hex(&object)); } - prepare_note_data(object, &d, note->hash); + prepare_note_data(&object, &d, note->hash); if (d.buf.len || allow_empty) { - write_note_data(&d, new_note); - if (add_note(t, object, new_note, combine_notes_overwrite)) + write_note_data(&d, new_note.hash); + if (add_note(t, object.hash, new_note.hash, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes add'"); } else { fprintf(stderr, _("Removing note for object %s\n"), - sha1_to_hex(object)); - remove_note(t, object); + oid_to_hex(&object)); + remove_note(t, object.hash); commit_notes(t, "Notes removed by 'git notes add'"); } @@ -476,7 +476,7 @@ static int copy(int argc, const char **argv, const char *prefix) int retval = 0, force = 0, from_stdin = 0; const struct object_id *from_note, *note; const char *object_ref; - unsigned char object[20], from_obj[20]; + struct object_id object, from_obj; struct notes_tree *t; const char *rewrite_cmd = NULL; struct option options[] = { @@ -509,37 +509,37 @@ static int copy(int argc, const char **argv, const char *prefix) usage_with_options(git_notes_copy_usage, options); } - if (get_sha1(argv[0], from_obj)) + if (get_oid(argv[0], &from_obj)) die(_("failed to resolve '%s' as a valid ref."), argv[0]); object_ref = 1 < argc ? argv[1] : "HEAD"; - if (get_sha1(object_ref, object)) + if (get_oid(object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("copy", NOTES_INIT_WRITABLE); - note = get_note(t, object); + note = get_note(t, object.hash); if (note) { if (!force) { retval = error(_("Cannot copy notes. Found existing " "notes for object %s. Use '-f' to " "overwrite existing notes"), - sha1_to_hex(object)); + oid_to_hex(&object)); goto out; } fprintf(stderr, _("Overwriting existing notes for object %s\n"), - sha1_to_hex(object)); + oid_to_hex(&object)); } - from_note = get_note(t, from_obj); + from_note = get_note(t, from_obj.hash); if (!from_note) { retval = error(_("missing notes on source object %s. Cannot " - "copy."), sha1_to_hex(from_obj)); + "copy."), oid_to_hex(&from_obj)); goto out; } - if (add_note(t, object, from_note->hash, combine_notes_overwrite)) + if (add_note(t, object.hash, from_note->hash, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: @@ -552,7 +552,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) int allow_empty = 0; const char *object_ref; struct notes_tree *t; - unsigned char object[20], new_note[20]; + struct object_id object, new_note; const struct object_id *note; char *logmsg; const char * const *usage; @@ -592,13 +592,13 @@ static int append_edit(int argc, const char **argv, const char *prefix) object_ref = 1 < argc ? argv[1] : "HEAD"; - if (get_sha1(object_ref, object)) + if (get_oid(object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); - note = get_note(t, object); + note = get_note(t, object.hash); - prepare_note_data(object, &d, edit && note ? note->hash : NULL); + prepare_note_data(&object, &d, edit && note ? note->hash : NULL); if (note && !edit) { /* Append buf to previous note contents */ @@ -615,14 +615,14 @@ static int append_edit(int argc, const char **argv, const char *prefix) } if (d.buf.len || allow_empty) { - write_note_data(&d, new_note); - if (add_note(t, object, new_note, combine_notes_overwrite)) + write_note_data(&d, new_note.hash); + if (add_note(t, object.hash, new_note.hash, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]); } else { fprintf(stderr, _("Removing note for object %s\n"), - sha1_to_hex(object)); - remove_note(t, object); + oid_to_hex(&object)); + remove_note(t, object.hash); logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]); } commit_notes(t, logmsg); @@ -637,7 +637,7 @@ static int show(int argc, const char **argv, const char *prefix) { const char *object_ref; struct notes_tree *t; - unsigned char object[20]; + struct object_id object; const struct object_id *note; int retval; struct option options[] = { @@ -654,15 +654,15 @@ static int show(int argc, const char **argv, const char *prefix) object_ref = argc ? argv[0] : "HEAD"; - if (get_sha1(object_ref, object)) + if (get_oid(object_ref, &object)) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("show", 0); - note = get_note(t, object); + note = get_note(t, object.hash); if (!note) retval = error(_("no note found for object %s."), - sha1_to_hex(object)); + oid_to_hex(&object)); else { const char *show_args[3] = {"show", oid_to_hex(note), NULL}; retval = execv_git_cmd(show_args); @@ -760,7 +760,7 @@ static int git_config_get_notes_strategy(const char *key, static int merge(int argc, const char **argv, const char *prefix) { struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; - unsigned char result_sha1[20]; + struct object_id result_oid; struct notes_tree *t; struct notes_merge_options o; int do_merge = 0, do_commit = 0, do_abort = 0; @@ -842,16 +842,16 @@ static int merge(int argc, const char **argv, const char *prefix) remote_ref.buf, default_notes_ref()); strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */ - result = notes_merge(&o, t, result_sha1); + result = notes_merge(&o, t, result_oid.hash); if (result >= 0) /* Merge resulted (trivially) in result_sha1 */ /* Update default notes ref with new commit */ - update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, + update_ref(msg.buf, default_notes_ref(), result_oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); else { /* Merge has unresolved conflicts */ const struct worktree *wt; /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ - update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL, + update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref()); @@ -878,10 +878,10 @@ static int merge(int argc, const char **argv, const char *prefix) static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag) { int status; - unsigned char sha1[20]; - if (get_sha1(name, sha1)) + struct object_id oid; + if (get_oid(name, &oid)) return error(_("Failed to resolve '%s' as a valid ref."), name); - status = remove_note(t, sha1); + status = remove_note(t, oid.hash); if (status) fprintf(stderr, _("Object %s has no note\n"), name); else diff --git a/notes-utils.c b/notes-utils.c index 325ff3daa3..7d7c22b435 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -155,12 +155,12 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd) } int copy_note_for_rewrite(struct notes_rewrite_cfg *c, - const unsigned char *from_obj, const unsigned char *to_obj) + const struct object_id *from_obj, const struct object_id *to_obj) { int ret = 0; int i; for (i = 0; c->trees[i]; i++) - ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret; + ret = copy_note(c->trees[i], from_obj->hash, to_obj->hash, 1, c->combine) || ret; return ret; } diff --git a/notes-utils.h b/notes-utils.h index fa538e1d95..1190578398 100644 --- a/notes-utils.h +++ b/notes-utils.h @@ -40,7 +40,7 @@ struct notes_rewrite_cfg { int parse_notes_merge_strategy(const char *v, enum notes_merge_strategy *s); struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd); int copy_note_for_rewrite(struct notes_rewrite_cfg *c, - const unsigned char *from_obj, const unsigned char *to_obj); + const struct object_id *from_obj, const struct object_id *to_obj); void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c, const char *msg); #endif From 5ee8a954e0191be2a144afdda6e37ef776730246 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 30 May 2017 10:30:43 -0700 Subject: [PATCH 428/566] notes: convert some accessor functions to struct object_id Convert add_note, get_note, and copy_note to take struct object_id. Signed-off-by: brian m. carlson Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/notes.c | 20 ++++++++++---------- notes-cache.c | 4 ++-- notes-merge.c | 18 +++++++++--------- notes-utils.c | 2 +- notes.c | 20 ++++++++++---------- notes.h | 8 ++++---- remote-testsvn.c | 10 +++++----- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/builtin/notes.c b/builtin/notes.c index 7947a16ede..b13fc87894 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -309,7 +309,7 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) if (rewrite_cmd) err = copy_note_for_rewrite(c, &from_obj, &to_obj); else - err = copy_note(t, from_obj.hash, to_obj.hash, force, + err = copy_note(t, &from_obj, &to_obj, force, combine_notes_overwrite); if (err) { @@ -370,7 +370,7 @@ static int list(int argc, const char **argv, const char *prefix) if (argc) { if (get_oid(argv[0], &object)) die(_("failed to resolve '%s' as a valid ref."), argv[0]); - note = get_note(t, object.hash); + note = get_note(t, &object); if (note) { puts(oid_to_hex(note)); retval = 0; @@ -427,7 +427,7 @@ static int add(int argc, const char **argv, const char *prefix) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("add", NOTES_INIT_WRITABLE); - note = get_note(t, object.hash); + note = get_note(t, &object); if (note) { if (!force) { @@ -456,7 +456,7 @@ static int add(int argc, const char **argv, const char *prefix) prepare_note_data(&object, &d, note->hash); if (d.buf.len || allow_empty) { write_note_data(&d, new_note.hash); - if (add_note(t, object.hash, new_note.hash, combine_notes_overwrite)) + if (add_note(t, &object, &new_note, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes add'"); } else { @@ -518,7 +518,7 @@ static int copy(int argc, const char **argv, const char *prefix) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("copy", NOTES_INIT_WRITABLE); - note = get_note(t, object.hash); + note = get_note(t, &object); if (note) { if (!force) { @@ -532,14 +532,14 @@ static int copy(int argc, const char **argv, const char *prefix) oid_to_hex(&object)); } - from_note = get_note(t, from_obj.hash); + from_note = get_note(t, &from_obj); if (!from_note) { retval = error(_("missing notes on source object %s. Cannot " "copy."), oid_to_hex(&from_obj)); goto out; } - if (add_note(t, object.hash, from_note->hash, combine_notes_overwrite)) + if (add_note(t, &object, from_note, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: @@ -596,7 +596,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); - note = get_note(t, object.hash); + note = get_note(t, &object); prepare_note_data(&object, &d, edit && note ? note->hash : NULL); @@ -616,7 +616,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) if (d.buf.len || allow_empty) { write_note_data(&d, new_note.hash); - if (add_note(t, object.hash, new_note.hash, combine_notes_overwrite)) + if (add_note(t, &object, &new_note, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]); } else { @@ -658,7 +658,7 @@ static int show(int argc, const char **argv, const char *prefix) die(_("failed to resolve '%s' as a valid ref."), object_ref); t = init_notes_check("show", 0); - note = get_note(t, object.hash); + note = get_note(t, &object); if (!note) retval = error(_("no note found for object %s."), diff --git a/notes-cache.c b/notes-cache.c index 6e84a748f0..29b4cede5f 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -74,7 +74,7 @@ char *notes_cache_get(struct notes_cache *c, struct object_id *key_oid, char *value; unsigned long size; - value_oid = get_note(&c->tree, key_oid->hash); + value_oid = get_note(&c->tree, key_oid); if (!value_oid) return NULL; value = read_sha1_file(value_oid->hash, &type, &size); @@ -90,5 +90,5 @@ int notes_cache_put(struct notes_cache *c, struct object_id *key_oid, if (write_sha1_file(data, size, "blob", value_oid.hash) < 0) return -1; - return add_note(&c->tree, key_oid->hash, value_oid.hash, NULL); + return add_note(&c->tree, key_oid, &value_oid, NULL); } diff --git a/notes-merge.c b/notes-merge.c index 6244f6af9c..9a1a49506e 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -444,14 +444,14 @@ static int merge_one_change(struct notes_merge_options *o, if (o->verbosity >= 2) printf("Using remote notes for %s\n", oid_to_hex(&p->obj)); - if (add_note(t, p->obj.hash, p->remote.hash, combine_notes_overwrite)) + if (add_note(t, &p->obj, &p->remote, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); return 0; case NOTES_MERGE_RESOLVE_UNION: if (o->verbosity >= 2) printf("Concatenating local and remote notes for %s\n", oid_to_hex(&p->obj)); - if (add_note(t, p->obj.hash, p->remote.hash, combine_notes_concatenate)) + if (add_note(t, &p->obj, &p->remote, combine_notes_concatenate)) die("failed to concatenate notes " "(combine_notes_concatenate)"); return 0; @@ -459,7 +459,7 @@ static int merge_one_change(struct notes_merge_options *o, if (o->verbosity >= 2) printf("Concatenating unique lines in local and remote " "notes for %s\n", oid_to_hex(&p->obj)); - if (add_note(t, p->obj.hash, p->remote.hash, combine_notes_cat_sort_uniq)) + if (add_note(t, &p->obj, &p->remote, combine_notes_cat_sort_uniq)) die("failed to concatenate notes " "(combine_notes_cat_sort_uniq)"); return 0; @@ -491,7 +491,7 @@ static int merge_changes(struct notes_merge_options *o, !oidcmp(&p->local, &p->base)) { /* no local change; adopt remote change */ trace_printf("\t\t\tno local change, adopted remote\n"); - if (add_note(t, p->obj.hash, p->remote.hash, + if (add_note(t, &p->obj, &p->remote, combine_notes_overwrite)) die("BUG: combine_notes_overwrite failed"); } else { @@ -693,12 +693,12 @@ int notes_merge_commit(struct notes_merge_options *o, baselen = path.len; while ((e = readdir(dir)) != NULL) { struct stat st; - unsigned char obj_sha1[20], blob_sha1[20]; + struct object_id obj_oid, blob_oid; if (is_dot_or_dotdot(e->d_name)) continue; - if (strlen(e->d_name) != 40 || get_sha1_hex(e->d_name, obj_sha1)) { + if (get_oid_hex(e->d_name, &obj_oid)) { if (o->verbosity >= 3) printf("Skipping non-SHA1 entry '%s%s'\n", path.buf, e->d_name); @@ -709,14 +709,14 @@ int notes_merge_commit(struct notes_merge_options *o, /* write file as blob, and add to partial_tree */ if (stat(path.buf, &st)) die_errno("Failed to stat '%s'", path.buf); - if (index_path(blob_sha1, path.buf, &st, HASH_WRITE_OBJECT)) + if (index_path(blob_oid.hash, path.buf, &st, HASH_WRITE_OBJECT)) die("Failed to write blob object from '%s'", path.buf); - if (add_note(partial_tree, obj_sha1, blob_sha1, NULL)) + if (add_note(partial_tree, &obj_oid, &blob_oid, NULL)) die("Failed to add resolved note '%s' to notes tree", path.buf); if (o->verbosity >= 4) printf("Added resolved note for object %s: %s\n", - sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1)); + oid_to_hex(&obj_oid), oid_to_hex(&blob_oid)); strbuf_setlen(&path, baselen); } diff --git a/notes-utils.c b/notes-utils.c index 7d7c22b435..b2aada90a2 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -160,7 +160,7 @@ int copy_note_for_rewrite(struct notes_rewrite_cfg *c, int ret = 0; int i; for (i = 0; c->trees[i]; i++) - ret = copy_note(c->trees[i], from_obj->hash, to_obj->hash, 1, c->combine) || ret; + ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret; return ret; } diff --git a/notes.c b/notes.c index b5cabafde6..4b3a1adda5 100644 --- a/notes.c +++ b/notes.c @@ -1086,8 +1086,8 @@ void init_display_notes(struct display_notes_opt *opt) string_list_clear(&display_notes_refs, 0); } -int add_note(struct notes_tree *t, const unsigned char *object_sha1, - const unsigned char *note_sha1, combine_notes_fn combine_notes) +int add_note(struct notes_tree *t, const struct object_id *object_oid, + const struct object_id *note_oid, combine_notes_fn combine_notes) { struct leaf_node *l; @@ -1098,8 +1098,8 @@ int add_note(struct notes_tree *t, const unsigned char *object_sha1, if (!combine_notes) combine_notes = t->combine_notes; l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node)); - hashcpy(l->key_oid.hash, object_sha1); - hashcpy(l->val_oid.hash, note_sha1); + oidcpy(&l->key_oid, object_oid); + oidcpy(&l->val_oid, note_oid); return note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes); } @@ -1120,14 +1120,14 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1) } const struct object_id *get_note(struct notes_tree *t, - const unsigned char *object_sha1) + const struct object_id *oid) { struct leaf_node *found; if (!t) t = &default_notes_tree; assert(t->initialized); - found = note_tree_find(t, t->root, 0, object_sha1); + found = note_tree_find(t, t->root, 0, oid->hash); return found ? &found->val_oid : NULL; } @@ -1229,7 +1229,7 @@ static void format_note(struct notes_tree *t, const struct object_id *object_oid if (!t->initialized) init_notes(t, NULL, NULL, 0); - oid = get_note(t, object_oid->hash); + oid = get_note(t, object_oid); if (!oid) return; @@ -1288,7 +1288,7 @@ void format_display_notes(const struct object_id *object_oid, } int copy_note(struct notes_tree *t, - const unsigned char *from_obj, const unsigned char *to_obj, + const struct object_id *from_obj, const struct object_id *to_obj, int force, combine_notes_fn combine_notes) { const struct object_id *note = get_note(t, from_obj); @@ -1298,9 +1298,9 @@ int copy_note(struct notes_tree *t, return 1; if (note) - return add_note(t, to_obj, note->hash, combine_notes); + return add_note(t, to_obj, note, combine_notes); else if (existing_note) - return add_note(t, to_obj, null_sha1, combine_notes); + return add_note(t, to_obj, &null_oid, combine_notes); return 0; } diff --git a/notes.h b/notes.h index a66532103c..3848c2fb3f 100644 --- a/notes.h +++ b/notes.h @@ -121,8 +121,8 @@ void init_notes(struct notes_tree *t, const char *notes_ref, * are not persistent until a subsequent call to write_notes_tree() returns * zero. */ -int add_note(struct notes_tree *t, const unsigned char *object_sha1, - const unsigned char *note_sha1, combine_notes_fn combine_notes); +int add_note(struct notes_tree *t, const struct object_id *object_oid, + const struct object_id *note_oid, combine_notes_fn combine_notes); /* * Remove the given note object from the given notes_tree structure @@ -141,7 +141,7 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1); * Return NULL if the given object has no notes. */ const struct object_id *get_note(struct notes_tree *t, - const unsigned char *object_sha1); + const struct object_id *object_oid); /* * Copy a note from one object to another in the given notes_tree. @@ -156,7 +156,7 @@ const struct object_id *get_note(struct notes_tree *t, * zero. */ int copy_note(struct notes_tree *t, - const unsigned char *from_obj, const unsigned char *to_obj, + const struct object_id *from_obj, const struct object_id *to_obj, int force, combine_notes_fn combine_notes); /* diff --git a/remote-testsvn.c b/remote-testsvn.c index 017af1bd5f..8e8d5c7947 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -51,7 +51,7 @@ static void terminate_batch(void) } /* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */ -static char *read_ref_note(const unsigned char sha1[20]) +static char *read_ref_note(const struct object_id *oid) { const struct object_id *note_oid; char *msg = NULL; @@ -59,7 +59,7 @@ static char *read_ref_note(const unsigned char sha1[20]) enum object_type type; init_notes(NULL, notes_ref, NULL, 0); - if (!(note_oid = get_note(NULL, sha1))) + if (!(note_oid = get_note(NULL, oid))) return NULL; /* note tree not found */ if (!(msg = read_sha1_file(note_oid->hash, &type, &msglen))) error("Empty notes tree. %s", notes_ref); @@ -174,15 +174,15 @@ static int cmd_import(const char *line) int code; int dumpin_fd; char *note_msg; - unsigned char head_sha1[20]; + struct object_id head_oid; unsigned int startrev; struct child_process svndump_proc = CHILD_PROCESS_INIT; const char *command = "svnrdump"; - if (read_ref(private_ref, head_sha1)) + if (read_ref(private_ref, head_oid.hash)) startrev = 0; else { - note_msg = read_ref_note(head_sha1); + note_msg = read_ref_note(&head_oid); if(note_msg == NULL) { warning("No note found for %s.", private_ref); startrev = 0; From 1c41c82bc4878138a4c58e4d5df200de17cb8565 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:44 -0700 Subject: [PATCH 429/566] grep: convert to struct object_id Convert the remaining parts of grep to use struct object_id. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/grep.c | 22 +++++++++++----------- cache.h | 7 +++++++ grep.c | 17 ++++++++--------- grep.h | 2 +- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/builtin/grep.c b/builtin/grep.c index c6c26e9b9e..623c13a939 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -327,7 +327,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid); + add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid); strbuf_release(&pathbuf); return 0; } else @@ -336,7 +336,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid); + grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); strbuf_release(&pathbuf); hit = grep_source(opt, &gs); @@ -570,7 +570,7 @@ static int grep_submodule_launch(struct grep_opt *opt, * with the object's name: 'tree-name:filename'. In order to * provide uniformity of output we want to pass the name of the * parent project's object name to the submodule so the submodule can - * prefix its output with the parent's name and not its own SHA1. + * prefix its output with the parent's name and not its own OID. */ if (gs->identifier && end_of_base) argv_array_pushf(&cp.args, "--parent-basename=%.*s", @@ -583,12 +583,12 @@ static int grep_submodule_launch(struct grep_opt *opt, * If there is a tree identifier for the submodule, add the * rev after adding the submodule options but before the * pathspecs. To do this we listen for the '--' and insert the - * sha1 before pushing the '--' onto the child process argv + * oid before pushing the '--' onto the child process argv * array. */ if (gs->identifier && !strcmp("--", submodule_options.argv[i])) { - argv_array_push(&cp.args, sha1_to_hex(gs->identifier)); + argv_array_push(&cp.args, oid_to_hex(gs->identifier)); } argv_array_push(&cp.args, submodule_options.argv[i]); @@ -618,11 +618,11 @@ static int grep_submodule_launch(struct grep_opt *opt, /* * Prep grep structures for a submodule grep - * sha1: the sha1 of the submodule or NULL if using the working tree + * oid: the oid of the submodule or NULL if using the working tree * filename: name of the submodule including tree name of parent * path: location of the submodule */ -static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1, +static int grep_submodule(struct grep_opt *opt, const struct object_id *oid, const char *filename, const char *path) { if (!is_submodule_initialized(path)) @@ -632,7 +632,7 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1, * If searching history, check for the presense of the * submodule's gitdir before skipping the submodule. */ - if (sha1) { + if (oid) { const struct submodule *sub = submodule_from_path(null_sha1, path); if (sub) @@ -647,7 +647,7 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1, #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, sha1); + add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, oid); return 0; } else #endif @@ -656,7 +656,7 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1, int hit; grep_source_init(&gs, GREP_SOURCE_SUBMODULE, - filename, path, sha1); + filename, path, oid); hit = grep_submodule_launch(opt, &gs); grep_source_clear(&gs); @@ -775,7 +775,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, check_attr); free(data); } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { - hit |= grep_submodule(opt, entry.oid->hash, base->buf, + hit |= grep_submodule(opt, entry.oid, base->buf, base->buf + tn_len); } diff --git a/cache.h b/cache.h index ae4c45d379..50fd2b3ccf 100644 --- a/cache.h +++ b/cache.h @@ -1026,6 +1026,13 @@ static inline void oidcpy(struct object_id *dst, const struct object_id *src) hashcpy(dst->hash, src->hash); } +static inline struct object_id *oiddup(const struct object_id *src) +{ + struct object_id *dst = xmalloc(sizeof(struct object_id)); + oidcpy(dst, src); + return dst; +} + static inline void hashclr(unsigned char *hash) { memset(hash, 0, GIT_SHA1_RAWSZ); diff --git a/grep.c b/grep.c index 47cee45067..a240b4cdb2 100644 --- a/grep.c +++ b/grep.c @@ -1403,7 +1403,7 @@ static int fill_textconv_grep(struct userdiff_driver *driver, */ df = alloc_filespec(gs->path); switch (gs->type) { - case GREP_SOURCE_SHA1: + case GREP_SOURCE_OID: fill_filespec(df, gs->identifier, 1, 0100644); break; case GREP_SOURCE_FILE: @@ -1747,9 +1747,8 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type, * If the identifier is non-NULL (in the submodule case) it * will be a SHA1 that needs to be copied. */ - case GREP_SOURCE_SHA1: - gs->identifier = xmalloc(20); - hashcpy(gs->identifier, identifier); + case GREP_SOURCE_OID: + gs->identifier = oiddup(identifier); break; case GREP_SOURCE_BUF: gs->identifier = NULL; @@ -1772,7 +1771,7 @@ void grep_source_clear_data(struct grep_source *gs) { switch (gs->type) { case GREP_SOURCE_FILE: - case GREP_SOURCE_SHA1: + case GREP_SOURCE_OID: case GREP_SOURCE_SUBMODULE: free(gs->buf); gs->buf = NULL; @@ -1784,7 +1783,7 @@ void grep_source_clear_data(struct grep_source *gs) } } -static int grep_source_load_sha1(struct grep_source *gs) +static int grep_source_load_oid(struct grep_source *gs) { enum object_type type; @@ -1795,7 +1794,7 @@ static int grep_source_load_sha1(struct grep_source *gs) if (!gs->buf) return error(_("'%s': unable to read %s"), gs->name, - sha1_to_hex(gs->identifier)); + oid_to_hex(gs->identifier)); return 0; } @@ -1841,8 +1840,8 @@ static int grep_source_load(struct grep_source *gs) switch (gs->type) { case GREP_SOURCE_FILE: return grep_source_load_file(gs); - case GREP_SOURCE_SHA1: - return grep_source_load_sha1(gs); + case GREP_SOURCE_OID: + return grep_source_load_oid(gs); case GREP_SOURCE_BUF: return gs->buf ? 0 : -1; case GREP_SOURCE_SUBMODULE: diff --git a/grep.h b/grep.h index 267534ca24..c88b40bdc4 100644 --- a/grep.h +++ b/grep.h @@ -158,7 +158,7 @@ struct grep_source { char *name; enum grep_source_type { - GREP_SOURCE_SHA1, + GREP_SOURCE_OID, GREP_SOURCE_FILE, GREP_SOURCE_BUF, GREP_SOURCE_SUBMODULE, From 362d7659150511cb8ef0f4af690d39881f53822d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:45 -0700 Subject: [PATCH 430/566] diff: convert get_stat_data to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff-lib.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index 2982bf055a..a3bc78162e 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -264,12 +264,12 @@ static void diff_index_show_file(struct rev_info *revs, } static int get_stat_data(const struct cache_entry *ce, - const unsigned char **sha1p, + const struct object_id **oidp, unsigned int *modep, int cached, int match_missing, unsigned *dirty_submodule, struct diff_options *diffopt) { - const unsigned char *sha1 = ce->oid.hash; + const struct object_id *oid = &ce->oid; unsigned int mode = ce->ce_mode; if (!cached && !ce_uptodate(ce)) { @@ -280,7 +280,7 @@ static int get_stat_data(const struct cache_entry *ce, return -1; else if (changed) { if (match_missing) { - *sha1p = sha1; + *oidp = oid; *modep = mode; return 0; } @@ -290,11 +290,11 @@ static int get_stat_data(const struct cache_entry *ce, 0, dirty_submodule); if (changed) { mode = ce_mode_from_stat(ce, st.st_mode); - sha1 = null_sha1; + oid = &null_oid; } } - *sha1p = sha1; + *oidp = oid; *modep = mode; return 0; } @@ -303,7 +303,7 @@ static void show_new_file(struct rev_info *revs, const struct cache_entry *new, int cached, int match_missing) { - const unsigned char *sha1; + const struct object_id *oid; unsigned int mode; unsigned dirty_submodule = 0; @@ -311,11 +311,11 @@ static void show_new_file(struct rev_info *revs, * New file in the index: it might actually be different in * the working tree. */ - if (get_stat_data(new, &sha1, &mode, cached, match_missing, + if (get_stat_data(new, &oid, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) return; - diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule); + diff_index_show_file(revs, "+", new, oid->hash, !is_null_oid(oid), mode, dirty_submodule); } static int show_modified(struct rev_info *revs, @@ -325,10 +325,10 @@ static int show_modified(struct rev_info *revs, int cached, int match_missing) { unsigned int mode, oldmode; - const unsigned char *sha1; + const struct object_id *oid; unsigned dirty_submodule = 0; - if (get_stat_data(new, &sha1, &mode, cached, match_missing, + if (get_stat_data(new, &oid, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) { if (report_missing) diff_index_show_file(revs, "-", old, @@ -338,7 +338,7 @@ static int show_modified(struct rev_info *revs, } if (revs->combine_merges && !cached && - (hashcmp(sha1, old->oid.hash) || oidcmp(&old->oid, &new->oid))) { + (oidcmp(oid, &old->oid) || oidcmp(&old->oid, &new->oid))) { struct combine_diff_path *p; int pathlen = ce_namelen(new); @@ -362,12 +362,12 @@ static int show_modified(struct rev_info *revs, } oldmode = old->ce_mode; - if (mode == oldmode && !hashcmp(sha1, old->oid.hash) && !dirty_submodule && + if (mode == oldmode && !oidcmp(oid, &old->oid) && !dirty_submodule && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) return 0; diff_change(&revs->diffopt, oldmode, mode, - old->oid.hash, sha1, 1, !is_null_sha1(sha1), + old->oid.hash, oid->hash, 1, !is_null_oid(oid), old->name, 0, dirty_submodule); return 0; } From fcf2cfb54b6be34749feb6ffb50fe2bdd7bac8bc Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:46 -0700 Subject: [PATCH 431/566] diff: convert diff_index_show_file to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff-lib.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index a3bc78162e..2c838aaf43 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -255,12 +255,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option) static void diff_index_show_file(struct rev_info *revs, const char *prefix, const struct cache_entry *ce, - const unsigned char *sha1, int sha1_valid, + const struct object_id *oid, int oid_valid, unsigned int mode, unsigned dirty_submodule) { diff_addremove(&revs->diffopt, prefix[0], mode, - sha1, sha1_valid, ce->name, dirty_submodule); + oid->hash, oid_valid, ce->name, dirty_submodule); } static int get_stat_data(const struct cache_entry *ce, @@ -315,7 +315,7 @@ static void show_new_file(struct rev_info *revs, &dirty_submodule, &revs->diffopt) < 0) return; - diff_index_show_file(revs, "+", new, oid->hash, !is_null_oid(oid), mode, dirty_submodule); + diff_index_show_file(revs, "+", new, oid, !is_null_oid(oid), mode, dirty_submodule); } static int show_modified(struct rev_info *revs, @@ -332,7 +332,7 @@ static int show_modified(struct rev_info *revs, &dirty_submodule, &revs->diffopt) < 0) { if (report_missing) diff_index_show_file(revs, "-", old, - old->oid.hash, 1, old->ce_mode, + &old->oid, 1, old->ce_mode, 0); return -1; } @@ -426,7 +426,7 @@ static void do_oneway_diff(struct unpack_trees_options *o, * Something removed from the tree? */ if (!idx) { - diff_index_show_file(revs, "-", tree, tree->oid.hash, 1, + diff_index_show_file(revs, "-", tree, &tree->oid, 1, tree->ce_mode, 0); return; } From c26022ea8f54649ed6b3b545dd3158907abe5d2c Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:47 -0700 Subject: [PATCH 432/566] diff: convert diff_addremove to struct object_id Convert diff_addremove to take a struct object_id. In addtion convert the function pointer type 'add_remove_fn_t' to also take a struct object_id. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff-lib.c | 6 +++--- diff.c | 8 ++++---- diff.h | 8 ++++---- revision.c | 4 ++-- tree-diff.c | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index 2c838aaf43..7984ff962f 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -210,14 +210,14 @@ int run_diff_files(struct rev_info *revs, unsigned int option) continue; } diff_addremove(&revs->diffopt, '-', ce->ce_mode, - ce->oid.hash, + &ce->oid, !is_null_oid(&ce->oid), ce->name, 0); continue; } else if (revs->diffopt.ita_invisible_in_index && ce_intent_to_add(ce)) { diff_addremove(&revs->diffopt, '+', ce->ce_mode, - EMPTY_BLOB_SHA1_BIN, 0, + &empty_tree_oid, 0, ce->name, 0); continue; } @@ -260,7 +260,7 @@ static void diff_index_show_file(struct rev_info *revs, unsigned dirty_submodule) { diff_addremove(&revs->diffopt, prefix[0], mode, - oid->hash, oid_valid, ce->name, dirty_submodule); + oid, oid_valid, ce->name, dirty_submodule); } static int get_stat_data(const struct cache_entry *ce, diff --git a/diff.c b/diff.c index f3546536b4..3fa335f446 100644 --- a/diff.c +++ b/diff.c @@ -5081,8 +5081,8 @@ static int is_submodule_ignored(const char *path, struct diff_options *options) void diff_addremove(struct diff_options *options, int addremove, unsigned mode, - const unsigned char *sha1, - int sha1_valid, + const struct object_id *oid, + int oid_valid, const char *concatpath, unsigned dirty_submodule) { struct diff_filespec *one, *two; @@ -5114,9 +5114,9 @@ void diff_addremove(struct diff_options *options, two = alloc_filespec(concatpath); if (addremove != '+') - fill_filespec(one, sha1, sha1_valid, mode); + fill_filespec(one, oid->hash, oid_valid, mode); if (addremove != '-') { - fill_filespec(two, sha1, sha1_valid, mode); + fill_filespec(two, oid->hash, oid_valid, mode); two->dirty_submodule = dirty_submodule; } diff --git a/diff.h b/diff.h index d75e6d15e2..1086975a5a 100644 --- a/diff.h +++ b/diff.h @@ -31,8 +31,8 @@ typedef void (*change_fn_t)(struct diff_options *options, typedef void (*add_remove_fn_t)(struct diff_options *options, int addremove, unsigned mode, - const unsigned char *sha1, - int sha1_valid, + const struct object_id *oid, + int oid_valid, const char *fullpath, unsigned dirty_submodule); typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, @@ -247,8 +247,8 @@ extern int diff_can_quit_early(struct diff_options *); extern void diff_addremove(struct diff_options *, int addremove, unsigned mode, - const unsigned char *sha1, - int sha1_valid, + const struct object_id *oid, + int oid_valid, const char *fullpath, unsigned dirty_submodule); extern void diff_change(struct diff_options *, diff --git a/revision.c b/revision.c index 475d5b2dc1..71519193ca 100644 --- a/revision.c +++ b/revision.c @@ -401,8 +401,8 @@ static int tree_difference = REV_TREE_SAME; static void file_add_remove(struct diff_options *options, int addremove, unsigned mode, - const unsigned char *sha1, - int sha1_valid, + const struct object_id *oid, + int oid_valid, const char *fullpath, unsigned dirty_submodule) { int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD; diff --git a/tree-diff.c b/tree-diff.c index e164e532b2..f2c747ea50 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -78,21 +78,21 @@ static int emit_diff_first_parent_only(struct diff_options *opt, struct combine_ 1, 1, p->path, 0, 0); } else { - const unsigned char *sha1; + const struct object_id *oid; unsigned int mode; int addremove; if (p->mode) { addremove = '+'; - sha1 = p->oid.hash; + oid = &p->oid; mode = p->mode; } else { addremove = '-'; - sha1 = p0->oid.hash; + oid = &p0->oid; mode = p0->mode; } - opt->add_remove(opt, addremove, mode, sha1, 1, p->path, 0); + opt->add_remove(opt, addremove, mode, oid, 1, p->path, 0); } return 0; /* we are done with p */ From 55497b8c9e0163cb6d02cdf3b3cc3a10330c2b66 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:48 -0700 Subject: [PATCH 433/566] diff: convert run_diff_files to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff-lib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index 7984ff962f..c82b07dc1e 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -101,7 +101,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) struct cache_entry *ce = active_cache[i]; int changed; unsigned dirty_submodule = 0; - const unsigned char *old_sha1, *new_sha1; + const struct object_id *old_oid, *new_oid; if (diff_can_quit_early(&revs->diffopt)) break; @@ -233,12 +233,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option) continue; } oldmode = ce->ce_mode; - old_sha1 = ce->oid.hash; - new_sha1 = changed ? null_sha1 : ce->oid.hash; + old_oid = &ce->oid; + new_oid = changed ? &null_oid : &ce->oid; diff_change(&revs->diffopt, oldmode, newmode, - old_sha1, new_sha1, - !is_null_sha1(old_sha1), - !is_null_sha1(new_sha1), + old_oid->hash, new_oid->hash, + !is_null_oid(old_oid), + !is_null_oid(new_oid), ce->name, 0, dirty_submodule); } From 94a0097a41f09e00322add6d1cf62b3610b6a85c Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:49 -0700 Subject: [PATCH 434/566] diff: convert diff_change to struct object_id Convert diff_change to take a struct object_id. In addition convert the function pointer type 'change_fn_t' to also take a struct object_id. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff-lib.c | 4 ++-- diff.c | 14 +++++++------- diff.h | 13 ++++++------- revision.c | 6 +++--- tree-diff.c | 2 +- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index c82b07dc1e..1e8215df50 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -236,7 +236,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) old_oid = &ce->oid; new_oid = changed ? &null_oid : &ce->oid; diff_change(&revs->diffopt, oldmode, newmode, - old_oid->hash, new_oid->hash, + old_oid, new_oid, !is_null_oid(old_oid), !is_null_oid(new_oid), ce->name, 0, dirty_submodule); @@ -367,7 +367,7 @@ static int show_modified(struct rev_info *revs, return 0; diff_change(&revs->diffopt, oldmode, mode, - old->oid.hash, oid->hash, 1, !is_null_oid(oid), + &old->oid, oid, 1, !is_null_oid(oid), old->name, 0, dirty_submodule); return 0; } diff --git a/diff.c b/diff.c index 3fa335f446..b9bb3f6ca4 100644 --- a/diff.c +++ b/diff.c @@ -5127,9 +5127,9 @@ void diff_addremove(struct diff_options *options, void diff_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, - const unsigned char *old_sha1, - const unsigned char *new_sha1, - int old_sha1_valid, int new_sha1_valid, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, const char *concatpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule) { @@ -5142,8 +5142,8 @@ void diff_change(struct diff_options *options, if (DIFF_OPT_TST(options, REVERSE_DIFF)) { SWAP(old_mode, new_mode); - SWAP(old_sha1, new_sha1); - SWAP(old_sha1_valid, new_sha1_valid); + SWAP(old_oid, new_oid); + SWAP(old_oid_valid, new_oid_valid); SWAP(old_dirty_submodule, new_dirty_submodule); } @@ -5153,8 +5153,8 @@ void diff_change(struct diff_options *options, one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); - fill_filespec(one, old_sha1, old_sha1_valid, old_mode); - fill_filespec(two, new_sha1, new_sha1_valid, new_mode); + fill_filespec(one, old_oid->hash, old_oid_valid, old_mode); + fill_filespec(two, new_oid->hash, new_oid_valid, new_mode); one->dirty_submodule = old_dirty_submodule; two->dirty_submodule = new_dirty_submodule; p = diff_queue(&diff_queued_diff, one, two); diff --git a/diff.h b/diff.h index 1086975a5a..fcf334bb6f 100644 --- a/diff.h +++ b/diff.h @@ -23,9 +23,9 @@ typedef int (*pathchange_fn_t)(struct diff_options *options, typedef void (*change_fn_t)(struct diff_options *options, unsigned old_mode, unsigned new_mode, - const unsigned char *old_sha1, - const unsigned char *new_sha1, - int old_sha1_valid, int new_sha1_valid, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, const char *fullpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule); @@ -253,10 +253,9 @@ extern void diff_addremove(struct diff_options *, extern void diff_change(struct diff_options *, unsigned mode1, unsigned mode2, - const unsigned char *sha1, - const unsigned char *sha2, - int sha1_valid, - int sha2_valid, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, const char *fullpath, unsigned dirty_submodule1, unsigned dirty_submodule2); diff --git a/revision.c b/revision.c index 71519193ca..7637e75561 100644 --- a/revision.c +++ b/revision.c @@ -414,9 +414,9 @@ static void file_add_remove(struct diff_options *options, static void file_change(struct diff_options *options, unsigned old_mode, unsigned new_mode, - const unsigned char *old_sha1, - const unsigned char *new_sha1, - int old_sha1_valid, int new_sha1_valid, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, const char *fullpath, unsigned old_dirty_submodule, unsigned new_dirty_submodule) { diff --git a/tree-diff.c b/tree-diff.c index f2c747ea50..7ae1f10b2e 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -74,7 +74,7 @@ static int emit_diff_first_parent_only(struct diff_options *opt, struct combine_ { struct combine_diff_parent *p0 = &p->parent[0]; if (p->mode && p0->mode) { - opt->change(opt, p0->mode, p->mode, p0->oid.hash, p->oid.hash, + opt->change(opt, p0->mode, p->mode, &p0->oid, &p->oid, 1, 1, p->path, 0, 0); } else { From f9704c2d823f437aeed50e591415f24aebdda71d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:50 -0700 Subject: [PATCH 435/566] diff: convert fill_filespec to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/blame.c | 2 +- builtin/diff.c | 4 ++-- combine-diff.c | 4 ++-- diff-lib.c | 2 +- diff-no-index.c | 2 +- diff.c | 16 ++++++++-------- diffcore-rename.c | 2 +- diffcore.h | 2 +- grep.c | 2 +- line-log.c | 6 +++--- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 1043e5376f..5ad435380f 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -163,7 +163,7 @@ int textconv_object(const char *path, struct userdiff_driver *textconv; df = alloc_filespec(path); - fill_filespec(df, oid->hash, oid_valid, mode); + fill_filespec(df, oid, oid_valid, mode); textconv = get_textconv(df); if (!textconv) { free_filespec(df); diff --git a/builtin/diff.c b/builtin/diff.c index 8c03ddaf58..b2d7c32cdb 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -57,8 +57,8 @@ static void stuff_change(struct diff_options *opt, one = alloc_filespec(old_name); two = alloc_filespec(new_name); - fill_filespec(one, old_oid->hash, old_oid_valid, old_mode); - fill_filespec(two, new_oid->hash, new_oid_valid, new_mode); + fill_filespec(one, old_oid, old_oid_valid, old_mode); + fill_filespec(two, new_oid, new_oid_valid, new_mode); diff_queue(&diff_queued_diff, one, two); } diff --git a/combine-diff.c b/combine-diff.c index 2848034fe9..ad063ecb13 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -302,7 +302,7 @@ static char *grab_blob(const struct object_id *oid, unsigned int mode, return xcalloc(1, 1); } else if (textconv) { struct diff_filespec *df = alloc_filespec(path); - fill_filespec(df, oid->hash, 1, mode); + fill_filespec(df, oid, 1, mode); *size = fill_textconv(textconv, df, &blob); free_filespec(df); } else { @@ -1022,7 +1022,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, &result_size, NULL, NULL); } else if (textconv) { struct diff_filespec *df = alloc_filespec(elem->path); - fill_filespec(df, null_sha1, 0, st.st_mode); + fill_filespec(df, &null_oid, 0, st.st_mode); result_size = fill_textconv(textconv, df, &result); free_filespec(df); } else if (0 <= (fd = open(elem->path, O_RDONLY))) { diff --git a/diff-lib.c b/diff-lib.c index 1e8215df50..9e076a4886 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -409,7 +409,7 @@ static void do_oneway_diff(struct unpack_trees_options *o, struct diff_filepair *pair; pair = diff_unmerge(&revs->diffopt, idx->name); if (tree) - fill_filespec(pair->one, tree->oid.hash, 1, + fill_filespec(pair->one, &tree->oid, 1, tree->ce_mode); return; } diff --git a/diff-no-index.c b/diff-no-index.c index 79229382b0..80ff17d460 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -82,7 +82,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode) if (!name) name = "/dev/null"; s = alloc_filespec(name); - fill_filespec(s, null_sha1, 0, mode); + fill_filespec(s, &null_oid, 0, mode); if (name == file_from_standard_input) populate_from_stdin(s); return s; diff --git a/diff.c b/diff.c index b9bb3f6ca4..e0c179f5f2 100644 --- a/diff.c +++ b/diff.c @@ -2702,13 +2702,13 @@ void free_filespec(struct diff_filespec *spec) } } -void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, - int sha1_valid, unsigned short mode) +void fill_filespec(struct diff_filespec *spec, const struct object_id *oid, + int oid_valid, unsigned short mode) { if (mode) { spec->mode = canon_mode(mode); - hashcpy(spec->oid.hash, sha1); - spec->oid_valid = sha1_valid; + oidcpy(&spec->oid, oid); + spec->oid_valid = oid_valid; } } @@ -5114,9 +5114,9 @@ void diff_addremove(struct diff_options *options, two = alloc_filespec(concatpath); if (addremove != '+') - fill_filespec(one, oid->hash, oid_valid, mode); + fill_filespec(one, oid, oid_valid, mode); if (addremove != '-') { - fill_filespec(two, oid->hash, oid_valid, mode); + fill_filespec(two, oid, oid_valid, mode); two->dirty_submodule = dirty_submodule; } @@ -5153,8 +5153,8 @@ void diff_change(struct diff_options *options, one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); - fill_filespec(one, old_oid->hash, old_oid_valid, old_mode); - fill_filespec(two, new_oid->hash, new_oid_valid, new_mode); + fill_filespec(one, old_oid, old_oid_valid, old_mode); + fill_filespec(two, new_oid, new_oid_valid, new_mode); one->dirty_submodule = old_dirty_submodule; two->dirty_submodule = new_dirty_submodule; p = diff_queue(&diff_queued_diff, one, two); diff --git a/diffcore-rename.c b/diffcore-rename.c index f7444c86bd..3d9719dadc 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -60,7 +60,7 @@ static int add_rename_dst(struct diff_filespec *two) memmove(rename_dst + first + 1, rename_dst + first, (rename_dst_nr - first - 1) * sizeof(*rename_dst)); rename_dst[first].two = alloc_filespec(two->path); - fill_filespec(rename_dst[first].two, two->oid.hash, two->oid_valid, + fill_filespec(rename_dst[first].two, &two->oid, two->oid_valid, two->mode); rename_dst[first].pair = NULL; return 0; diff --git a/diffcore.h b/diffcore.h index 6230241354..a30da161da 100644 --- a/diffcore.h +++ b/diffcore.h @@ -52,7 +52,7 @@ struct diff_filespec { extern struct diff_filespec *alloc_filespec(const char *); extern void free_filespec(struct diff_filespec *); -extern void fill_filespec(struct diff_filespec *, const unsigned char *, +extern void fill_filespec(struct diff_filespec *, const struct object_id *, int, unsigned short); #define CHECK_SIZE_ONLY 1 diff --git a/grep.c b/grep.c index a240b4cdb2..3e21c92b9d 100644 --- a/grep.c +++ b/grep.c @@ -1407,7 +1407,7 @@ static int fill_textconv_grep(struct userdiff_driver *driver, fill_filespec(df, gs->identifier, 1, 0100644); break; case GREP_SOURCE_FILE: - fill_filespec(df, null_sha1, 0, 0100644); + fill_filespec(df, &null_oid, 0, 0100644); break; default: die("BUG: attempt to textconv something without a path?"); diff --git a/line-log.c b/line-log.c index b9087814b8..a3bd2f2d5f 100644 --- a/line-log.c +++ b/line-log.c @@ -500,12 +500,12 @@ static struct commit *check_single_commit(struct rev_info *revs) static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec) { unsigned mode; - unsigned char sha1[20]; + struct object_id oid; if (get_tree_entry(commit->object.oid.hash, spec->path, - sha1, &mode)) + oid.hash, &mode)) die("There is no path %s in the commit", spec->path); - fill_filespec(spec, sha1, 1, mode); + fill_filespec(spec, &oid, 1, mode); return; } From fb4a1c0dc89db00989b357fd3c1bee4b5ede6ed3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:51 -0700 Subject: [PATCH 436/566] diff: convert reuse_worktree_file to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/diff.c b/diff.c index e0c179f5f2..084c8b2d0a 100644 --- a/diff.c +++ b/diff.c @@ -2717,7 +2717,7 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid, * the work tree has that object contents, return true, so that * prepare_temp_file() does not have to inflate and extract. */ -static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file) +static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file) { const struct cache_entry *ce; struct stat st; @@ -2748,7 +2748,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int * objects however would tend to be slower as they need * to be individually opened and inflated. */ - if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1)) + if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(oid->hash)) return 0; /* @@ -2768,7 +2768,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int * This is not the sha1 we are looking for, or * unreusable because it is not a regular file. */ - if (hashcmp(sha1, ce->oid.hash) || !S_ISREG(ce->ce_mode)) + if (oidcmp(oid, &ce->oid) || !S_ISREG(ce->ce_mode)) return 0; /* @@ -2842,7 +2842,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) return diff_populate_gitlink(s, size_only); if (!s->oid_valid || - reuse_worktree_file(s->path, s->oid.hash, 0)) { + reuse_worktree_file(s->path, &s->oid, 0)) { struct strbuf buf = STRBUF_INIT; struct stat st; int fd; @@ -3008,7 +3008,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, if (!S_ISGITLINK(one->mode) && (!one->oid_valid || - reuse_worktree_file(name, one->oid.hash, 1))) { + reuse_worktree_file(name, &one->oid, 1))) { struct stat st; if (lstat(name, &st) < 0) { if (errno == ENOENT) From 74014152be5d4280b1f8541cbae09b441d2c5328 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:52 -0700 Subject: [PATCH 437/566] diff: finish conversion for prepare_temp_file to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diff.c b/diff.c index 084c8b2d0a..a8ceeb024c 100644 --- a/diff.c +++ b/diff.c @@ -3030,13 +3030,13 @@ static struct diff_tempfile *prepare_temp_file(const char *name, /* we can borrow from the file in the work tree */ temp->name = name; if (!one->oid_valid) - sha1_to_hex_r(temp->hex, null_sha1); + oid_to_hex_r(temp->hex, &null_oid); else oid_to_hex_r(temp->hex, &one->oid); /* Even though we may sometimes borrow the * contents from the work tree, we always want * one->mode. mode is trustworthy even when - * !(one->sha1_valid), as long as + * !(one->oid_valid), as long as * DIFF_FILE_VALID(one). */ xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode); From 34f3c0ebfbe3c8075587e5b06d0fd280ea57f053 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:53 -0700 Subject: [PATCH 438/566] patch-ids: convert to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/log.c | 2 +- patch-ids.c | 20 ++++++++++---------- patch-ids.h | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index a440601efe..6bdba34446 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1354,7 +1354,7 @@ static void prepare_bases(struct base_tree_info *bases, struct object_id *patch_id; if (commit->util) continue; - if (commit_patch_id(commit, &diffopt, oid.hash, 0)) + if (commit_patch_id(commit, &diffopt, &oid, 0)) die(_("cannot get patch id")); ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id); patch_id = bases->patch_id + bases->nr_patch_id; diff --git a/patch-ids.c b/patch-ids.c index 92eba7a059..aab26cbbb9 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -11,7 +11,7 @@ static int patch_id_defined(struct commit *commit) } int commit_patch_id(struct commit *commit, struct diff_options *options, - unsigned char *sha1, int diff_header_only) + struct object_id *oid, int diff_header_only) { if (!patch_id_defined(commit)) return -1; @@ -22,7 +22,7 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, else diff_root_tree_sha1(commit->object.oid.hash, "", options); diffcore_std(options); - return diff_flush_patch_id(options, sha1, diff_header_only); + return diff_flush_patch_id(options, oid->hash, diff_header_only); } /* @@ -39,15 +39,15 @@ static int patch_id_cmp(struct patch_id *a, struct patch_id *b, struct diff_options *opt) { - if (is_null_sha1(a->patch_id) && - commit_patch_id(a->commit, opt, a->patch_id, 0)) + if (is_null_oid(&a->patch_id) && + commit_patch_id(a->commit, opt, &a->patch_id, 0)) return error("Could not get patch ID for %s", oid_to_hex(&a->commit->object.oid)); - if (is_null_sha1(b->patch_id) && - commit_patch_id(b->commit, opt, b->patch_id, 0)) + if (is_null_oid(&b->patch_id) && + commit_patch_id(b->commit, opt, &b->patch_id, 0)) return error("Could not get patch ID for %s", oid_to_hex(&b->commit->object.oid)); - return hashcmp(a->patch_id, b->patch_id); + return oidcmp(&a->patch_id, &b->patch_id); } int init_patch_ids(struct patch_ids *ids) @@ -71,13 +71,13 @@ static int init_patch_id_entry(struct patch_id *patch, struct commit *commit, struct patch_ids *ids) { - unsigned char header_only_patch_id[GIT_MAX_RAWSZ]; + struct object_id header_only_patch_id; patch->commit = commit; - if (commit_patch_id(commit, &ids->diffopts, header_only_patch_id, 1)) + if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1)) return -1; - hashmap_entry_init(patch, sha1hash(header_only_patch_id)); + hashmap_entry_init(patch, sha1hash(header_only_patch_id.hash)); return 0; } diff --git a/patch-ids.h b/patch-ids.h index b9e5751f8e..bec0f727a7 100644 --- a/patch-ids.h +++ b/patch-ids.h @@ -3,7 +3,7 @@ struct patch_id { struct hashmap_entry ent; - unsigned char patch_id[GIT_MAX_RAWSZ]; + struct object_id patch_id; struct commit *commit; }; @@ -13,7 +13,7 @@ struct patch_ids { }; int commit_patch_id(struct commit *commit, struct diff_options *options, - unsigned char *sha1, int); + struct object_id *oid, int); int init_patch_ids(struct patch_ids *); int free_patch_ids(struct patch_ids *); struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *); From bd25f288767aa26f42ac02d2d36695c8df9134dd Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:54 -0700 Subject: [PATCH 439/566] diff: convert diff_flush_patch_id to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff.c | 12 ++++++------ diff.h | 2 +- patch-ids.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/diff.c b/diff.c index a8ceeb024c..dd325e6160 100644 --- a/diff.c +++ b/diff.c @@ -4584,7 +4584,7 @@ static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode) } /* returns 0 upon success, and writes result into sha1 */ -static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only) +static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only) { struct diff_queue_struct *q = &diff_queued_diff; int i; @@ -4656,9 +4656,9 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, if (diff_filespec_is_binary(p->one) || diff_filespec_is_binary(p->two)) { git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid), - 40); + GIT_SHA1_HEXSZ); git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid), - 40); + GIT_SHA1_HEXSZ); continue; } @@ -4671,15 +4671,15 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, p->one->path); } - git_SHA1_Final(sha1, &ctx); + git_SHA1_Final(oid->hash, &ctx); return 0; } -int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only) +int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only) { struct diff_queue_struct *q = &diff_queued_diff; int i; - int result = diff_get_patch_id(options, sha1, diff_header_only); + int result = diff_get_patch_id(options, oid, diff_header_only); for (i = 0; i < q->nr; i++) diff_free_filepair(q->queue[i]); diff --git a/diff.h b/diff.h index fcf334bb6f..150f1c5a1c 100644 --- a/diff.h +++ b/diff.h @@ -354,7 +354,7 @@ extern int run_diff_files(struct rev_info *revs, unsigned int option); extern int run_diff_index(struct rev_info *revs, int cached); extern int do_diff_cache(const struct object_id *, struct diff_options *); -extern int diff_flush_patch_id(struct diff_options *, unsigned char *, int); +extern int diff_flush_patch_id(struct diff_options *, struct object_id *, int); extern int diff_result_code(struct diff_options *, int); diff --git a/patch-ids.c b/patch-ids.c index aab26cbbb9..a70a291d82 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -22,7 +22,7 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, else diff_root_tree_sha1(commit->object.oid.hash, "", options); diffcore_std(options); - return diff_flush_patch_id(options, oid->hash, diff_header_only); + return diff_flush_patch_id(options, oid, diff_header_only); } /* From b9acf54dbd2906249abb2a406d466b47da44678e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:55 -0700 Subject: [PATCH 440/566] combine-diff: convert diff_tree_combined to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/diff.c | 2 +- combine-diff.c | 10 +++++----- diff.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/diff.c b/builtin/diff.c index b2d7c32cdb..73b4ff3db2 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -194,7 +194,7 @@ static int builtin_diff_combined(struct rev_info *revs, revs->dense_combined_merges = revs->combine_merges = 1; for (i = 1; i < ents; i++) oid_array_append(&parents, &ent[i].item->oid); - diff_tree_combined(ent[0].item->oid.hash, &parents, + diff_tree_combined(&ent[0].item->oid, &parents, revs->dense_combined_merges, revs); oid_array_clear(&parents); return 0; diff --git a/combine-diff.c b/combine-diff.c index ad063ecb13..84981df752 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1384,7 +1384,7 @@ static struct combine_diff_path *find_paths_multitree( } -void diff_tree_combined(const unsigned char *sha1, +void diff_tree_combined(const struct object_id *oid, const struct oid_array *parents, int dense, struct rev_info *rev) @@ -1448,11 +1448,11 @@ void diff_tree_combined(const unsigned char *sha1, * diff(sha1,parent_i) for all i to do the job, specifically * for parent0. */ - paths = find_paths_generic(sha1, parents, &diffopts); + paths = find_paths_generic(oid->hash, parents, &diffopts); } else { int stat_opt; - paths = find_paths_multitree(sha1, parents, &diffopts); + paths = find_paths_multitree(oid->hash, parents, &diffopts); /* * show stat against the first parent even @@ -1463,7 +1463,7 @@ void diff_tree_combined(const unsigned char *sha1, if (stat_opt) { diffopts.output_format = stat_opt; - diff_tree_sha1(parents->oid[0].hash, sha1, "", &diffopts); + diff_tree_sha1(parents->oid[0].hash, oid->hash, "", &diffopts); diffcore_std(&diffopts); if (opt->orderfile) diffcore_order(opt->orderfile); @@ -1539,6 +1539,6 @@ void diff_tree_combined_merge(const struct commit *commit, int dense, oid_array_append(&parents, &parent->item->object.oid); parent = parent->next; } - diff_tree_combined(commit->object.oid.hash, &parents, dense, rev); + diff_tree_combined(&commit->object.oid, &parents, dense, rev); oid_array_clear(&parents); } diff --git a/diff.h b/diff.h index 150f1c5a1c..6aeeda035e 100644 --- a/diff.h +++ b/diff.h @@ -236,7 +236,7 @@ struct combine_diff_path { extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *); -extern void diff_tree_combined(const unsigned char *sha1, const struct oid_array *parents, int dense, struct rev_info *rev); +extern void diff_tree_combined(const struct object_id *oid, const struct oid_array *parents, int dense, struct rev_info *rev); extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev); From 09fae19aa8db639d2384a98f8ebd2f9ec66f6e28 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:56 -0700 Subject: [PATCH 441/566] combine-diff: convert find_paths_* to struct object_id Convert find_paths_generic and find_paths_multitree to use struct object_id. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- combine-diff.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/combine-diff.c b/combine-diff.c index 84981df752..c823645106 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1311,7 +1311,7 @@ static const char *path_path(void *obj) /* find set of paths that every parent touches */ -static struct combine_diff_path *find_paths_generic(const unsigned char *sha1, +static struct combine_diff_path *find_paths_generic(const struct object_id *oid, const struct oid_array *parents, struct diff_options *opt) { struct combine_diff_path *paths = NULL; @@ -1336,7 +1336,7 @@ static struct combine_diff_path *find_paths_generic(const unsigned char *sha1, opt->output_format = stat_opt; else opt->output_format = DIFF_FORMAT_NO_OUTPUT; - diff_tree_sha1(parents->oid[i].hash, sha1, "", opt); + diff_tree_sha1(parents->oid[i].hash, oid->hash, "", opt); diffcore_std(opt); paths = intersect_paths(paths, i, num_parent); @@ -1360,7 +1360,7 @@ static struct combine_diff_path *find_paths_generic(const unsigned char *sha1, * rename/copy detection, etc, comparing all trees simultaneously (= faster). */ static struct combine_diff_path *find_paths_multitree( - const unsigned char *sha1, const struct oid_array *parents, + const struct object_id *oid, const struct oid_array *parents, struct diff_options *opt) { int i, nparent = parents->nr; @@ -1376,7 +1376,7 @@ static struct combine_diff_path *find_paths_multitree( paths_head.next = NULL; strbuf_init(&base, PATH_MAX); - diff_tree_paths(&paths_head, sha1, parents_sha1, nparent, &base, opt); + diff_tree_paths(&paths_head, oid->hash, parents_sha1, nparent, &base, opt); strbuf_release(&base); free(parents_sha1); @@ -1448,11 +1448,11 @@ void diff_tree_combined(const struct object_id *oid, * diff(sha1,parent_i) for all i to do the job, specifically * for parent0. */ - paths = find_paths_generic(oid->hash, parents, &diffopts); + paths = find_paths_generic(oid, parents, &diffopts); } else { int stat_opt; - paths = find_paths_multitree(oid->hash, parents, &diffopts); + paths = find_paths_multitree(oid, parents, &diffopts); /* * show stat against the first parent even From 7b8dea0c7570a2028f204498c82c8ca7ec6950e3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:57 -0700 Subject: [PATCH 442/566] tree-diff: convert diff_root_tree_sha1 to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/fast-export.c | 4 ++-- diff.h | 4 ++-- log-tree.c | 2 +- patch-ids.c | 2 +- tree-diff.c | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 24e29ad7ea..e242726f08 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -566,8 +566,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) commit->tree->object.oid.hash, "", &rev->diffopt); } else - diff_root_tree_sha1(commit->tree->object.oid.hash, - "", &rev->diffopt); + diff_root_tree_oid(&commit->tree->object.oid, + "", &rev->diffopt); /* Export the referenced blobs, and remember the marks. */ for (i = 0; i < diff_queued_diff.nr; i++) diff --git a/diff.h b/diff.h index 6aeeda035e..8d46a67099 100644 --- a/diff.h +++ b/diff.h @@ -215,8 +215,8 @@ extern struct combine_diff_path *diff_tree_paths( struct strbuf *base, struct diff_options *opt); extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt); -extern int diff_root_tree_sha1(const unsigned char *new, const char *base, - struct diff_options *opt); +extern int diff_root_tree_oid(const struct object_id *new_oid, const char *base, + struct diff_options *opt); struct combine_diff_path { struct combine_diff_path *next; diff --git a/log-tree.c b/log-tree.c index 9c0c64a2d8..b40242534b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -803,7 +803,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log parents = get_saved_parents(opt, commit); if (!parents) { if (opt->show_root_diff) { - diff_root_tree_sha1(oid->hash, "", &opt->diffopt); + diff_root_tree_oid(oid, "", &opt->diffopt); log_tree_diff_flush(opt); } return !opt->loginfo; diff --git a/patch-ids.c b/patch-ids.c index a70a291d82..aaf462c030 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -20,7 +20,7 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, diff_tree_sha1(commit->parents->item->object.oid.hash, commit->object.oid.hash, "", options); else - diff_root_tree_sha1(commit->object.oid.hash, "", options); + diff_root_tree_oid(&commit->object.oid, "", options); diffcore_std(options); return diff_flush_patch_id(options, oid, diff_header_only); } diff --git a/tree-diff.c b/tree-diff.c index 7ae1f10b2e..f9bbaf3c47 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -711,7 +711,7 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha return retval; } -int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_options *opt) +int diff_root_tree_oid(const struct object_id *new_oid, const char *base, struct diff_options *opt) { - return diff_tree_sha1(NULL, new, base, opt); + return diff_tree_sha1(NULL, new_oid->hash, base, opt); } From 13b57da83384e9523943ea9d8ecf3ec7993ce56a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 29 May 2017 22:25:25 +0200 Subject: [PATCH 443/566] mingw: verify that paths are not mistaken for remote nicknames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This added test case simply verifies that users will not be bothered with bogus complaints à la warning: unable to access '.git/remotes/D:\repo': Invalid argument when fetching from a Windows path (in this case, D:\repo). [j6t: mark the new test as test_expect_failure] Signed-off-by: Johannes Schindelin Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t5580-clone-push-unc.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh index b195f71ea9..944730cddc 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-clone-push-unc.sh @@ -1,10 +1,10 @@ #!/bin/sh -test_description='various UNC path tests (Windows-only)' +test_description='various Windows-only path tests' . ./test-lib.sh if ! test_have_prereq MINGW; then - skip_all='skipping UNC path tests, requires Windows' + skip_all='skipping Windows-only path tests' test_done fi @@ -45,4 +45,10 @@ test_expect_success push ' test "$rev" = "$(git rev-parse --verify refs/heads/to-push)" ' +test_expect_failure 'remote nick cannot contain backslashes' ' + BACKSLASHED="$(pwd | tr / \\\\)" && + git ls-remote "$BACKSLASHED" >out 2>err && + test_i18ngrep ! "unable to access" err +' + test_done From e5b313442ab7c700d0851e9dbe7d2b029e3893e5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 29 May 2017 22:27:35 +0200 Subject: [PATCH 444/566] mingw_fopen: report ENOENT for invalid file names On Windows, certain characters are prohibited in file names, most prominently the colon. When fopen() is called with such an invalid file name, the underlying Windows API actually reports a particular error, but since there is no suitable errno value, this error is translated to EINVAL. Detect the case and report ENOENT instead. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- compat/mingw.c | 2 ++ t/t5580-clone-push-unc.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compat/mingw.c b/compat/mingw.c index 3fbfda5978..b604a827e8 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -423,6 +423,8 @@ FILE *mingw_fopen (const char *filename, const char *otype) return NULL; } file = _wfopen(wfilename, wotype); + if (!file && GetLastError() == ERROR_INVALID_NAME) + errno = ENOENT; if (file && hide && set_hidden_flag(wfilename, 1)) warning("could not mark '%s' as hidden.", filename); return file; diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh index 944730cddc..b322c2f722 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-clone-push-unc.sh @@ -45,7 +45,7 @@ test_expect_success push ' test "$rev" = "$(git rev-parse --verify refs/heads/to-push)" ' -test_expect_failure 'remote nick cannot contain backslashes' ' +test_expect_success 'remote nick cannot contain backslashes' ' BACKSLASHED="$(pwd | tr / \\\\)" && git ls-remote "$BACKSLASHED" >out 2>err && test_i18ngrep ! "unable to access" err From 177409e5897988f03e0c8111c94db6ea0466b138 Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Thu, 1 Jun 2017 16:50:55 -0700 Subject: [PATCH 445/566] send-email: check for repo before invoking hook Unless --no-validate is passed, send-email will invoke $repo->repo_path() in its search for a validate hook regardless of whether a Git repo is actually present. Teach send-email to first check for repo existence. Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano --- git-send-email.perl | 30 ++++++++++++++++-------------- t/t9001-send-email.sh | 8 ++++++++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index c314cc2b51..fa559cca3f 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1739,21 +1739,23 @@ sub unique_email_list { sub validate_patch { my $fn = shift; - my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'), - 'sendemail-validate'); - my $hook_error; - if (-x $validate_hook) { - my $target = abs_path($fn); - # The hook needs a correct cwd and GIT_DIR. - my $cwd_save = cwd(); - chdir($repo->wc_path() or $repo->repo_path()) - or die("chdir: $!"); - local $ENV{"GIT_DIR"} = $repo->repo_path(); - $hook_error = "rejected by sendemail-validate hook" - if system($validate_hook, $target); - chdir($cwd_save) or die("chdir: $!"); + if ($repo) { + my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'), + 'sendemail-validate'); + my $hook_error; + if (-x $validate_hook) { + my $target = abs_path($fn); + # The hook needs a correct cwd and GIT_DIR. + my $cwd_save = cwd(); + chdir($repo->wc_path() or $repo->repo_path()) + or die("chdir: $!"); + local $ENV{"GIT_DIR"} = $repo->repo_path(); + $hook_error = "rejected by sendemail-validate hook" + if system($validate_hook, $target); + chdir($cwd_save) or die("chdir: $!"); + } + return $hook_error if $hook_error; } - return $hook_error if $hook_error; open(my $fh, '<', $fn) or die sprintf(__("unable to open %s: %s\n"), $fn, $!); diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 15128c755a..d1e4e8ad19 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -1953,4 +1953,12 @@ test_expect_success $PREREQ 'invoke hook' ' ) ' +test_expect_success $PREREQ 'test that send-email works outside a repo' ' + nongit git send-email \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + "$(pwd)/0001-add-master.patch" +' + test_done From 194280427d2f2f4ab564aae4c7aa7fa9f7e78f26 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 May 2017 08:25:47 +0200 Subject: [PATCH 446/566] completion: add git config gc completions Add missing completion for git config gc options: * gc.aggressiveDepth * gc.autoDetach * gc.logExpiry * gc.worktreePruneExpire Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 535a19a70c..94ed7a93b9 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2464,15 +2464,19 @@ _git_config () format.thread format.to gc. + gc.aggressiveDepth gc.aggressiveWindow gc.auto + gc.autoDetach gc.autopacklimit + gc.logExpiry gc.packrefs gc.pruneexpire gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved gc.rerereunresolved + gc.worktreePruneExpire gitcvs.allbinary gitcvs.commitmsgannotation gitcvs.dbTableNamePrefix From f254eab2e024edec8b8d4a342252efba74901fdb Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 May 2017 08:25:48 +0200 Subject: [PATCH 447/566] completion: add git config core completions Add missing completions for git config core: * core.checkStat * core.commentChar * core.hideDotFiles * core.hooksPath * core.packedRefsTimeout * core.precomposeUnicode * core.protectHFS * core.protectNTFS * core.splitIndex * core.sshCommand Note that some configs are only used for some platforms (hideDotFiles on Windows and precomposeUnicode on Mac). Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 94ed7a93b9..3e6ddaa814 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2403,6 +2403,8 @@ _git_config () core.autocrlf core.bare core.bigFileThreshold + core.checkStat + core.commentChar core.compression core.createObject core.deltaBaseCacheLimit @@ -2412,6 +2414,8 @@ _git_config () core.fileMode core.fsyncobjectfiles core.gitProxy + core.hideDotFiles + core.hooksPath core.ignoreStat core.ignorecase core.logAllRefUpdates @@ -2419,14 +2423,20 @@ _git_config () core.notesRef core.packedGitLimit core.packedGitWindowSize + core.packedRefsTimeout core.pager + core.precomposeUnicode core.preferSymlinkRefs core.preloadindex + core.protectHFS + core.protectNTFS core.quotepath core.repositoryFormatVersion core.safecrlf core.sharedRepository core.sparseCheckout + core.splitIndex + core.sshCommand core.symlinks core.trustctime core.untrackedCache From e8dec567705ebeaaff70bb29bd9f724944c5ae90 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 May 2017 08:25:49 +0200 Subject: [PATCH 448/566] completion: add git config am.threeWay completion Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3e6ddaa814..6150790387 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2343,6 +2343,7 @@ _git_config () advice.statusHints alias. am.keepcr + am.threeWay apply.ignorewhitespace apply.whitespace branch.autosetupmerge From fd1552d59d2f181019a8cf18582e89167be51e01 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 May 2017 08:25:50 +0200 Subject: [PATCH 449/566] completion: add git config advice completions Add missing completions for git config advice: * advice.amWorkDir * advice.pushAlreadyExists * advice.pushFetchFirst * advice.pushNeedsForce * advice.pushNonFFCurrent * advice.pushNonFFMatching * advice.pushUpdateRejected * advice.rmHints * advice.statusUoption Remove completion for git config advice.pushNonFastForward, since it was renamed to pushUpdateRejected in 1184564eac8e. The config still works, but is no longer part of the documentation. Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6150790387..087ea945bd 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2335,12 +2335,20 @@ _git_config () esac __gitcomp " add.ignoreErrors + advice.amWorkDir advice.commitBeforeMerge advice.detachedHead advice.implicitIdentity - advice.pushNonFastForward + advice.pushAlreadyExists + advice.pushFetchFirst + advice.pushNeedsForce + advice.pushNonFFCurrent + advice.pushNonFFMatching + advice.pushUpdateRejected advice.resolveConflict + advice.rmHints advice.statusHints + advice.statusUoption alias. am.keepcr am.threeWay From 6ecef7379ce428ee3da215eaca86b518fce80d68 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 May 2017 08:25:51 +0200 Subject: [PATCH 450/566] completion: add git config credential completions Add missing completions for git config credential: * credential.helper * credential.useHttpPath * credential.username Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 087ea945bd..98617d30cd 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2452,6 +2452,9 @@ _git_config () core.warnAmbiguousRefs core.whitespace core.worktree + credential.helper + credential.useHttpPath + credential.username diff.autorefreshindex diff.external diff.ignoreSubmodules From d78d237bba5f4f65832e8f75a147cb81c59885ce Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Sat, 27 May 2017 08:25:52 +0200 Subject: [PATCH 451/566] completion: add git config credentialCache.ignoreSIGHUP Signed-off-by: Rikard Falkeborn Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 98617d30cd..d18d30b777 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2455,6 +2455,7 @@ _git_config () credential.helper credential.useHttpPath credential.username + credentialCache.ignoreSIGHUP diff.autorefreshindex diff.external diff.ignoreSubmodules From 8ba74bfd7c7e11454a15ed6360777e702c438846 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Jun 2017 11:34:15 +0900 Subject: [PATCH 452/566] diff-tree: update stale in-code comments Signed-off-by: Junio C Hamano --- builtin/diff-tree.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 12b683d021..094e8de71f 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -127,9 +127,11 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) } /* - * NOTE! We expect "a ^b" to be equal to "a..b", so we - * reverse the order of the objects if the second one - * is marked UNINTERESTING. + * NOTE! We expect "a..b" to expand to "^a b" but it is + * perfectly valid for revision range parser to yield "b ^a", + * which means the same thing. If we get the latter, i.e. the + * second one is marked UNINTERESTING, we recover the original + * order the user gave, i.e. "a..b", by swapping the trees. */ nr_sha1 = opt->pending.nr; switch (nr_sha1) { From 0c79cee697f7f4e304c53108ee895b706df1322e Mon Sep 17 00:00:00 2001 From: Adam Dinwoodie Date: Thu, 1 Jun 2017 11:37:03 +0100 Subject: [PATCH 453/566] docs: fix formatting and grammar When compiling the documentation, asciidoc thinks a backtick surrounded by whitespace shouldn't be interpreted as marking the start or end of a literal. In most cases, that's useful behaviour, but in the git-pull documentation it means asciidoc is failing to correctly detect which text should be monospaced and which shouldn't. To avoid this, remove the extraneous spaces from the text to be monospaced. It would also be possible to fix the formatting by switching to asciidoc's ++ monospace format markers and still have the space characters included in the monospace text, but the spaces aren't necessary and not having them keeps the markup simpler. Also include a minor grammar fix suggested by Jeff while we're changing these lines. Signed-off-by: Adam Dinwoodie Helped-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-pull.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 942af8e0f7..e414185f5a 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -159,15 +159,15 @@ present while on branch ``, that value is used instead of In order to determine what URL to use to fetch from, the value of the configuration `remote..url` is consulted -and if there is not any such variable, the value on `URL: ` line -in `$GIT_DIR/remotes/` file is used. +and if there is not any such variable, the value on the `URL:` line +in `$GIT_DIR/remotes/` is used. In order to determine what remote branches to fetch (and optionally store in the remote-tracking branches) when the command is run without any refspec parameters on the command line, values of the configuration variable `remote..fetch` are consulted, and if there aren't any, `$GIT_DIR/remotes/` -file is consulted and its `Pull: ` lines are used. +is consulted and its `Pull:` lines are used. In addition to the refspec formats described in the OPTIONS section, you can have a globbing refspec that looks like this: From 826c06412e429a091d32d0e1b26e77da33fc1ce9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Jun 2017 15:07:36 +0900 Subject: [PATCH 454/566] Fifth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index d0f7ef5590..7ee326e1dc 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -110,6 +110,9 @@ Performance, Internal Implementation, Development Support etc. operations now, which would speed up "git status" (as long as the cache is properly invalidated). + * The internal implementation of "git grep" has seen some clean-up. + (merge 8df4c2953f ab/grep-preparatory-cleanup later to maint). + Also contains various documentation updates and code clean-ups. @@ -263,6 +266,23 @@ notes for details). when needed. Recent versions of Net::SMTP can do TLS natively. (merge 0ead000c3a dk/send-email-avoid-net-smtp-ssl-when-able later to maint). + * "foo\bar\baz" in "git fetch foo\bar\baz", even though there is no + slashes in it, cannot be a nickname for a remote on Windows, as + that is likely to be a pathname on a local filesystem. + (merge d9244ecf4f js/bs-is-a-dir-sep-on-windows later to maint). + + * "git clean -d" used to clean directories that has ignored files, + even though the command should not lose ignored ones without "-x". + "git status --ignored" did not list ignored and untracked files + without "-uall". These have been corrected. + (merge 6b1db43109 sl/clean-d-ignored-fix later to maint). + + * The result from "git diff" that compares two blobs, e.g. "git diff + $commit1:$path $commit2:$path", used to be shown with the full + object name as given on the command line, but it is more natural to + use the $path in the output and use it to look up .gitattributes. + (merge 30d005c020 jk/diff-blob later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 515360f9e9 jn/credential-doc-on-clear later to maint). (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). @@ -272,3 +292,7 @@ notes for details). (merge 449456ad47 sg/core-filemode-doc-typofix later to maint). (merge ba4dce784e km/log-showsignature-doc later to maint). (merge c5a9157393 jh/memihash-opt later to maint). + (merge 80f4cd8046 ab/ref-filter-no-contains later to maint). + (merge b275da816c ah/doc-interpret-trailers-ifexists later to maint). + (merge fc7a5edb55 ah/doc-pretty-format-fix later to maint). + (merge 7e95fcb4b5 sb/t5531-update-desc later to maint). From f164c1bf6533ee0205c545e850f733269839f62f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 4 Jun 2017 09:58:01 +0900 Subject: [PATCH 455/566] Sixth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index 7ee326e1dc..1bb29b6e9d 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -113,6 +113,11 @@ Performance, Internal Implementation, Development Support etc. * The internal implementation of "git grep" has seen some clean-up. (merge 8df4c2953f ab/grep-preparatory-cleanup later to maint). + * Update the C style recommendation for notes for translators, as + recent versions of gettext tools can work with our style of + multi-line comments. + (merge 66f5f6dca9 ab/c-translators-comment-style later to maint). + Also contains various documentation updates and code clean-ups. @@ -283,6 +288,19 @@ notes for details). use the $path in the output and use it to look up .gitattributes. (merge 30d005c020 jk/diff-blob later to maint). + * The "collision detecting" SHA-1 implementation shipped with 2.13 + was quite broken on some big-endian platforms and/or platforms that + do not like unaligned fetches. Update to the upstream code which + has already fixed these issues. + (merge a0103914c2 ab/sha1dc-maint later to maint). + + * "git am -h" triggered a BUG(). + (merge f3a2fffe06 jk/unbreak-am-h later to maint). + + * The interaction of "url.*.insteadOf" and custom URL scheme's + whitelisting is now documented better. + (merge 2c9a2ae285 jk/url-insteadof-config later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 515360f9e9 jn/credential-doc-on-clear later to maint). (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). @@ -296,3 +314,8 @@ notes for details). (merge b275da816c ah/doc-interpret-trailers-ifexists later to maint). (merge fc7a5edb55 ah/doc-pretty-format-fix later to maint). (merge 7e95fcb4b5 sb/t5531-update-desc later to maint). + (merge b8f354f294 sd/t3200-typofix later to maint). + (merge ba746ff9c9 ah/doc-filter-branch-export-env later to maint). + (merge 44e2ff09ce ab/t3070-test-dedup later to maint). + (merge 9ee4aa95db rf/completion-config-commit later to maint). + (merge fb87327aee ah/doc-rev-parse-short-default later to maint). From e215bd91bb3fb3ced90c28b06018ef9d955c5dc2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 4 Jun 2017 10:25:42 +0900 Subject: [PATCH 456/566] Prepare for 2.13.1; more topics to follow Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.13.1.txt | 69 +++++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes/2.13.1.txt diff --git a/Documentation/RelNotes/2.13.1.txt b/Documentation/RelNotes/2.13.1.txt new file mode 100644 index 0000000000..04c752c490 --- /dev/null +++ b/Documentation/RelNotes/2.13.1.txt @@ -0,0 +1,69 @@ +Git v2.13.1 Release Notes +========================= + +Fixes since v2.13 +----------------- + + * The Web interface to gmane news archive is long gone, even though + the articles are still accessible via NTTP. Replace the links with + ones to public-inbox.org. Because their message identification is + based on the actual message-id, it is likely that it will be easier + to migrate away from it if/when necessary. + + * Update tests to pass under GETTEXT_POISON (a mechanism to ensure + that output strings that should not be translated are not + translated by mistake), and tell TravisCI to run them. + + * Setting "log.decorate=false" in the configuration file did not take + effect in v2.13, which has been corrected. + + * An earlier update to test 7400 needed to be skipped on CYGWIN. + + * Git sometimes gives an advice in a rhetorical question that does + not require an answer, which can confuse new users and non native + speakers. Attempt to rephrase them. + + * "git read-tree -m" (no tree-ish) gave a nonsense suggestion "use + --empty if you want to clear the index". With "-m", such a request + will still fail anyway, as you'd need to name at least one tree-ish + to be merged. + + * The codepath in "git am" that is used when running "git rebase" + leaked memory held for the log message of the commits being rebased. + + * "pack-objects" can stream a slice of an existing packfile out when + the pack bitmap can tell that the reachable objects are all needed + in the output, without inspecting individual objects. This + strategy however would not work well when "--local" and other + options are in use, and need to be disabled. + + * Clarify documentation for include.path and includeIf..path + configuration variables. + + * Tag objects, which are not reachable from any ref, that point at + missing objects were mishandled by "git gc" and friends (they + should silently be ignored instead) + + * A few http:// links that are redirected to https:// in the + documentation have been updated to https:// links. + + * Make sure our tests would pass when the sources are checked out + with "platform native" line ending convention by default on + Windows. Some "text" files out tests use and the test scripts + themselves that are meant to be run with /bin/sh, ought to be + checked out with eol=LF even on Windows. + + * Fix memory leaks pointed out by Coverity (and people). + + * The receive-pack program now makes sure that the push certificate + records the same set of push options used for pushing. + + * "git cherry-pick" and other uses of the sequencer machinery + mishandled a trailer block whose last line is an incomplete line. + This has been fixed so that an additional sign-off etc. are added + after completing the existing incomplete line. + + * The shell completion script (in contrib/) learned "git stash" has + a new "push" subcommand. + +Also contains various documentation updates and code clean-ups. diff --git a/RelNotes b/RelNotes index 125bf78f3b..52c8e104fa 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes/2.13.0.txt \ No newline at end of file +Documentation/RelNotes/2.13.1.txt \ No newline at end of file From 68602c01fde24d3b5cbcc4817bb85ab309684def Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 2 Jun 2017 12:28:10 -0700 Subject: [PATCH 457/566] Documentation/git-rm: correct submodule description Since 3ccd681c2a (Merge branch 'sb/submodule-rm-absorb', 2017-01-18) git-rm tries to absorb any submodules git dir before deleting the submodule. Correct the documentation to say so. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- Documentation/git-rm.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index f1efc116eb..8c87e8cdd7 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -140,10 +140,11 @@ Only submodules using a gitfile (which means they were cloned with a Git version 1.7.8 or newer) will be removed from the work tree, as their repository lives inside the .git directory of the superproject. If a submodule (or one of those nested inside it) -still uses a .git directory, `git rm` will fail - no matter if forced -or not - to protect the submodule's history. If it exists the -submodule. section in the linkgit:gitmodules[5] file will also -be removed and that file will be staged (unless --cached or -n are used). +still uses a .git directory, `git rm` will move the submodules +git directory into the superprojects git directory to protect +the submodule's history. If it exists the submodule. section +in the linkgit:gitmodules[5] file will also be removed and that file +will be staged (unless --cached or -n are used). A submodule is considered up-to-date when the HEAD is the same as recorded in the index, no tracked files are modified and no untracked From 2c04f6340579518c55a554fcac9fe21c01b3d3ea Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 5 Jun 2017 09:05:38 +0900 Subject: [PATCH 458/566] Git 2.13.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.13.1.txt | 45 +++ Documentation/git.txt | 510 ------------------------------ GIT-VERSION-GEN | 2 +- 3 files changed, 46 insertions(+), 511 deletions(-) diff --git a/Documentation/RelNotes/2.13.1.txt b/Documentation/RelNotes/2.13.1.txt index 04c752c490..ed7cd976d9 100644 --- a/Documentation/RelNotes/2.13.1.txt +++ b/Documentation/RelNotes/2.13.1.txt @@ -66,4 +66,49 @@ Fixes since v2.13 * The shell completion script (in contrib/) learned "git stash" has a new "push" subcommand. + * Travis CI gained a task to format the documentation with both + AsciiDoc and AsciiDoctor. + + * Update the C style recommendation for notes for translators, as + recent versions of gettext tools can work with our style of + multi-line comments. + + * "git clone --config var=val" is a way to populate the + per-repository configuration file of the new repository, but it did + not work well when val is an empty string. This has been fixed. + + * A few codepaths in "checkout" and "am" working on an unborn branch + tried to access an uninitialized piece of memory. + + * "git for-each-ref --format=..." with %(HEAD) in the format used to + resolve the HEAD symref as many times as it had processed refs, + which was wasteful, and "git branch" shared the same problem. + + * "git interpret-trailers", when used as GIT_EDITOR for "git commit + -v", looked for and appended to a trailer block at the very end, + i.e. at the end of the "diff" output. The command has been + corrected to pay attention to the cut-mark line "commit -v" adds to + the buffer---the real trailer block should appear just before it. + + * A test allowed both "git push" and "git receive-pack" on the other + end write their traces into the same file. This is OK on platforms + that allows atomically appending to a file opened with O_APPEND, + but on other platforms led to a mangled output, causing + intermittent test failures. This has been fixed by disabling + traces from "receive-pack" in the test. + + * "foo\bar\baz" in "git fetch foo\bar\baz", even though there is no + slashes in it, cannot be a nickname for a remote on Windows, as + that is likely to be a pathname on a local filesystem. + + * The "collision detecting" SHA-1 implementation shipped with 2.13 + was quite broken on some big-endian platforms and/or platforms that + do not like unaligned fetches. Update to the upstream code which + has already fixed these issues. + + * "git am -h" triggered a BUG(). + + * The interaction of "url.*.insteadOf" and custom URL scheme's + whitelisting is now documented better. + Also contains various documentation updates and code clean-ups. diff --git a/Documentation/git.txt b/Documentation/git.txt index fb10314c1d..7dd5e03280 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -35,516 +35,6 @@ manual page gives you an overview of the command-line command syntax. A formatted and hyperlinked copy of the latest Git documentation can be viewed at `https://git.github.io/htmldocs/git.html`. -ifdef::stalenotes[] -[NOTE] -============ - -You are reading the documentation for the latest (possibly -unreleased) version of Git, that is available from the 'master' -branch of the `git.git` repository. -Documentation for older releases are available here: - -* link:v2.13.0/git.html[documentation for release 2.13] - -* release notes for - link:RelNotes/2.13.0.txt[2.13]. - - -* link:v2.12.3/git.html[documentation for release 2.12.3] - -* release notes for - link:RelNotes/2.12.3.txt[2.12.3], - link:RelNotes/2.12.2.txt[2.12.2], - link:RelNotes/2.12.1.txt[2.12.1], - link:RelNotes/2.12.0.txt[2.12]. - -* link:v2.11.1/git.html[documentation for release 2.11.1] - -* release notes for - link:RelNotes/2.11.2.txt[2.11.2], - link:RelNotes/2.11.1.txt[2.11.1], - link:RelNotes/2.11.0.txt[2.11]. - -* link:v2.10.3/git.html[documentation for release 2.10.3] - -* release notes for - link:RelNotes/2.10.3.txt[2.10.3], - link:RelNotes/2.10.2.txt[2.10.2], - link:RelNotes/2.10.1.txt[2.10.1], - link:RelNotes/2.10.0.txt[2.10]. - -* link:v2.9.4/git.html[documentation for release 2.9.4] - -* release notes for - link:RelNotes/2.9.4.txt[2.9.4], - link:RelNotes/2.9.3.txt[2.9.3], - link:RelNotes/2.9.2.txt[2.9.2], - link:RelNotes/2.9.1.txt[2.9.1], - link:RelNotes/2.9.0.txt[2.9]. - -* link:v2.8.5/git.html[documentation for release 2.8.5] - -* release notes for - link:RelNotes/2.8.5.txt[2.8.5], - link:RelNotes/2.8.4.txt[2.8.4], - link:RelNotes/2.8.3.txt[2.8.3], - link:RelNotes/2.8.2.txt[2.8.2], - link:RelNotes/2.8.1.txt[2.8.1], - link:RelNotes/2.8.0.txt[2.8]. - -* link:v2.7.5/git.html[documentation for release 2.7.5] - -* release notes for - link:RelNotes/2.7.5.txt[2.7.5], - link:RelNotes/2.7.4.txt[2.7.4], - link:RelNotes/2.7.3.txt[2.7.3], - link:RelNotes/2.7.2.txt[2.7.2], - link:RelNotes/2.7.1.txt[2.7.1], - link:RelNotes/2.7.0.txt[2.7]. - -* link:v2.6.7/git.html[documentation for release 2.6.7] - -* release notes for - link:RelNotes/2.6.7.txt[2.6.7], - link:RelNotes/2.6.6.txt[2.6.6], - link:RelNotes/2.6.5.txt[2.6.5], - link:RelNotes/2.6.4.txt[2.6.4], - link:RelNotes/2.6.3.txt[2.6.3], - link:RelNotes/2.6.2.txt[2.6.2], - link:RelNotes/2.6.1.txt[2.6.1], - link:RelNotes/2.6.0.txt[2.6]. - -* link:v2.5.6/git.html[documentation for release 2.5.6] - -* release notes for - link:RelNotes/2.5.6.txt[2.5.6], - link:RelNotes/2.5.5.txt[2.5.5], - link:RelNotes/2.5.4.txt[2.5.4], - link:RelNotes/2.5.3.txt[2.5.3], - link:RelNotes/2.5.2.txt[2.5.2], - link:RelNotes/2.5.1.txt[2.5.1], - link:RelNotes/2.5.0.txt[2.5]. - -* link:v2.4.12/git.html[documentation for release 2.4.12] - -* release notes for - link:RelNotes/2.4.12.txt[2.4.12], - link:RelNotes/2.4.11.txt[2.4.11], - link:RelNotes/2.4.10.txt[2.4.10], - link:RelNotes/2.4.9.txt[2.4.9], - link:RelNotes/2.4.8.txt[2.4.8], - link:RelNotes/2.4.7.txt[2.4.7], - link:RelNotes/2.4.6.txt[2.4.6], - link:RelNotes/2.4.5.txt[2.4.5], - link:RelNotes/2.4.4.txt[2.4.4], - link:RelNotes/2.4.3.txt[2.4.3], - link:RelNotes/2.4.2.txt[2.4.2], - link:RelNotes/2.4.1.txt[2.4.1], - link:RelNotes/2.4.0.txt[2.4]. - -* link:v2.3.10/git.html[documentation for release 2.3.10] - -* release notes for - link:RelNotes/2.3.10.txt[2.3.10], - link:RelNotes/2.3.9.txt[2.3.9], - link:RelNotes/2.3.8.txt[2.3.8], - link:RelNotes/2.3.7.txt[2.3.7], - link:RelNotes/2.3.6.txt[2.3.6], - link:RelNotes/2.3.5.txt[2.3.5], - link:RelNotes/2.3.4.txt[2.3.4], - link:RelNotes/2.3.3.txt[2.3.3], - link:RelNotes/2.3.2.txt[2.3.2], - link:RelNotes/2.3.1.txt[2.3.1], - link:RelNotes/2.3.0.txt[2.3]. - -* link:v2.2.3/git.html[documentation for release 2.2.3] - -* release notes for - link:RelNotes/2.2.3.txt[2.2.3], - link:RelNotes/2.2.2.txt[2.2.2], - link:RelNotes/2.2.1.txt[2.2.1], - link:RelNotes/2.2.0.txt[2.2]. - -* link:v2.1.4/git.html[documentation for release 2.1.4] - -* release notes for - link:RelNotes/2.1.4.txt[2.1.4], - link:RelNotes/2.1.3.txt[2.1.3], - link:RelNotes/2.1.2.txt[2.1.2], - link:RelNotes/2.1.1.txt[2.1.1], - link:RelNotes/2.1.0.txt[2.1]. - -* link:v2.0.5/git.html[documentation for release 2.0.5] - -* release notes for - link:RelNotes/2.0.5.txt[2.0.5], - link:RelNotes/2.0.4.txt[2.0.4], - link:RelNotes/2.0.3.txt[2.0.3], - link:RelNotes/2.0.2.txt[2.0.2], - link:RelNotes/2.0.1.txt[2.0.1], - link:RelNotes/2.0.0.txt[2.0.0]. - -* link:v1.9.5/git.html[documentation for release 1.9.5] - -* release notes for - link:RelNotes/1.9.5.txt[1.9.5], - link:RelNotes/1.9.4.txt[1.9.4], - link:RelNotes/1.9.3.txt[1.9.3], - link:RelNotes/1.9.2.txt[1.9.2], - link:RelNotes/1.9.1.txt[1.9.1], - link:RelNotes/1.9.0.txt[1.9.0]. - -* link:v1.8.5.6/git.html[documentation for release 1.8.5.6] - -* release notes for - link:RelNotes/1.8.5.6.txt[1.8.5.6], - link:RelNotes/1.8.5.5.txt[1.8.5.5], - link:RelNotes/1.8.5.4.txt[1.8.5.4], - link:RelNotes/1.8.5.3.txt[1.8.5.3], - link:RelNotes/1.8.5.2.txt[1.8.5.2], - link:RelNotes/1.8.5.1.txt[1.8.5.1], - link:RelNotes/1.8.5.txt[1.8.5]. - -* link:v1.8.4.5/git.html[documentation for release 1.8.4.5] - -* release notes for - link:RelNotes/1.8.4.5.txt[1.8.4.5], - link:RelNotes/1.8.4.4.txt[1.8.4.4], - link:RelNotes/1.8.4.3.txt[1.8.4.3], - link:RelNotes/1.8.4.2.txt[1.8.4.2], - link:RelNotes/1.8.4.1.txt[1.8.4.1], - link:RelNotes/1.8.4.txt[1.8.4]. - -* link:v1.8.3.4/git.html[documentation for release 1.8.3.4] - -* release notes for - link:RelNotes/1.8.3.4.txt[1.8.3.4], - link:RelNotes/1.8.3.3.txt[1.8.3.3], - link:RelNotes/1.8.3.2.txt[1.8.3.2], - link:RelNotes/1.8.3.1.txt[1.8.3.1], - link:RelNotes/1.8.3.txt[1.8.3]. - -* link:v1.8.2.3/git.html[documentation for release 1.8.2.3] - -* release notes for - link:RelNotes/1.8.2.3.txt[1.8.2.3], - link:RelNotes/1.8.2.2.txt[1.8.2.2], - link:RelNotes/1.8.2.1.txt[1.8.2.1], - link:RelNotes/1.8.2.txt[1.8.2]. - -* link:v1.8.1.6/git.html[documentation for release 1.8.1.6] - -* release notes for - link:RelNotes/1.8.1.6.txt[1.8.1.6], - link:RelNotes/1.8.1.5.txt[1.8.1.5], - link:RelNotes/1.8.1.4.txt[1.8.1.4], - link:RelNotes/1.8.1.3.txt[1.8.1.3], - link:RelNotes/1.8.1.2.txt[1.8.1.2], - link:RelNotes/1.8.1.1.txt[1.8.1.1], - link:RelNotes/1.8.1.txt[1.8.1]. - -* link:v1.8.0.3/git.html[documentation for release 1.8.0.3] - -* release notes for - link:RelNotes/1.8.0.3.txt[1.8.0.3], - link:RelNotes/1.8.0.2.txt[1.8.0.2], - link:RelNotes/1.8.0.1.txt[1.8.0.1], - link:RelNotes/1.8.0.txt[1.8.0]. - -* link:v1.7.12.4/git.html[documentation for release 1.7.12.4] - -* release notes for - link:RelNotes/1.7.12.4.txt[1.7.12.4], - link:RelNotes/1.7.12.3.txt[1.7.12.3], - link:RelNotes/1.7.12.2.txt[1.7.12.2], - link:RelNotes/1.7.12.1.txt[1.7.12.1], - link:RelNotes/1.7.12.txt[1.7.12]. - -* link:v1.7.11.7/git.html[documentation for release 1.7.11.7] - -* release notes for - link:RelNotes/1.7.11.7.txt[1.7.11.7], - link:RelNotes/1.7.11.6.txt[1.7.11.6], - link:RelNotes/1.7.11.5.txt[1.7.11.5], - link:RelNotes/1.7.11.4.txt[1.7.11.4], - link:RelNotes/1.7.11.3.txt[1.7.11.3], - link:RelNotes/1.7.11.2.txt[1.7.11.2], - link:RelNotes/1.7.11.1.txt[1.7.11.1], - link:RelNotes/1.7.11.txt[1.7.11]. - -* link:v1.7.10.5/git.html[documentation for release 1.7.10.5] - -* release notes for - link:RelNotes/1.7.10.5.txt[1.7.10.5], - link:RelNotes/1.7.10.4.txt[1.7.10.4], - link:RelNotes/1.7.10.3.txt[1.7.10.3], - link:RelNotes/1.7.10.2.txt[1.7.10.2], - link:RelNotes/1.7.10.1.txt[1.7.10.1], - link:RelNotes/1.7.10.txt[1.7.10]. - -* link:v1.7.9.7/git.html[documentation for release 1.7.9.7] - -* release notes for - link:RelNotes/1.7.9.7.txt[1.7.9.7], - link:RelNotes/1.7.9.6.txt[1.7.9.6], - link:RelNotes/1.7.9.5.txt[1.7.9.5], - link:RelNotes/1.7.9.4.txt[1.7.9.4], - link:RelNotes/1.7.9.3.txt[1.7.9.3], - link:RelNotes/1.7.9.2.txt[1.7.9.2], - link:RelNotes/1.7.9.1.txt[1.7.9.1], - link:RelNotes/1.7.9.txt[1.7.9]. - -* link:v1.7.8.6/git.html[documentation for release 1.7.8.6] - -* release notes for - link:RelNotes/1.7.8.6.txt[1.7.8.6], - link:RelNotes/1.7.8.5.txt[1.7.8.5], - link:RelNotes/1.7.8.4.txt[1.7.8.4], - link:RelNotes/1.7.8.3.txt[1.7.8.3], - link:RelNotes/1.7.8.2.txt[1.7.8.2], - link:RelNotes/1.7.8.1.txt[1.7.8.1], - link:RelNotes/1.7.8.txt[1.7.8]. - -* link:v1.7.7.7/git.html[documentation for release 1.7.7.7] - -* release notes for - link:RelNotes/1.7.7.7.txt[1.7.7.7], - link:RelNotes/1.7.7.6.txt[1.7.7.6], - link:RelNotes/1.7.7.5.txt[1.7.7.5], - link:RelNotes/1.7.7.4.txt[1.7.7.4], - link:RelNotes/1.7.7.3.txt[1.7.7.3], - link:RelNotes/1.7.7.2.txt[1.7.7.2], - link:RelNotes/1.7.7.1.txt[1.7.7.1], - link:RelNotes/1.7.7.txt[1.7.7]. - -* link:v1.7.6.6/git.html[documentation for release 1.7.6.6] - -* release notes for - link:RelNotes/1.7.6.6.txt[1.7.6.6], - link:RelNotes/1.7.6.5.txt[1.7.6.5], - link:RelNotes/1.7.6.4.txt[1.7.6.4], - link:RelNotes/1.7.6.3.txt[1.7.6.3], - link:RelNotes/1.7.6.2.txt[1.7.6.2], - link:RelNotes/1.7.6.1.txt[1.7.6.1], - link:RelNotes/1.7.6.txt[1.7.6]. - -* link:v1.7.5.4/git.html[documentation for release 1.7.5.4] - -* release notes for - link:RelNotes/1.7.5.4.txt[1.7.5.4], - link:RelNotes/1.7.5.3.txt[1.7.5.3], - link:RelNotes/1.7.5.2.txt[1.7.5.2], - link:RelNotes/1.7.5.1.txt[1.7.5.1], - link:RelNotes/1.7.5.txt[1.7.5]. - -* link:v1.7.4.5/git.html[documentation for release 1.7.4.5] - -* release notes for - link:RelNotes/1.7.4.5.txt[1.7.4.5], - link:RelNotes/1.7.4.4.txt[1.7.4.4], - link:RelNotes/1.7.4.3.txt[1.7.4.3], - link:RelNotes/1.7.4.2.txt[1.7.4.2], - link:RelNotes/1.7.4.1.txt[1.7.4.1], - link:RelNotes/1.7.4.txt[1.7.4]. - -* link:v1.7.3.5/git.html[documentation for release 1.7.3.5] - -* release notes for - link:RelNotes/1.7.3.5.txt[1.7.3.5], - link:RelNotes/1.7.3.4.txt[1.7.3.4], - link:RelNotes/1.7.3.3.txt[1.7.3.3], - link:RelNotes/1.7.3.2.txt[1.7.3.2], - link:RelNotes/1.7.3.1.txt[1.7.3.1], - link:RelNotes/1.7.3.txt[1.7.3]. - -* link:v1.7.2.5/git.html[documentation for release 1.7.2.5] - -* release notes for - link:RelNotes/1.7.2.5.txt[1.7.2.5], - link:RelNotes/1.7.2.4.txt[1.7.2.4], - link:RelNotes/1.7.2.3.txt[1.7.2.3], - link:RelNotes/1.7.2.2.txt[1.7.2.2], - link:RelNotes/1.7.2.1.txt[1.7.2.1], - link:RelNotes/1.7.2.txt[1.7.2]. - -* link:v1.7.1.4/git.html[documentation for release 1.7.1.4] - -* release notes for - link:RelNotes/1.7.1.4.txt[1.7.1.4], - link:RelNotes/1.7.1.3.txt[1.7.1.3], - link:RelNotes/1.7.1.2.txt[1.7.1.2], - link:RelNotes/1.7.1.1.txt[1.7.1.1], - link:RelNotes/1.7.1.txt[1.7.1]. - -* link:v1.7.0.9/git.html[documentation for release 1.7.0.9] - -* release notes for - link:RelNotes/1.7.0.9.txt[1.7.0.9], - link:RelNotes/1.7.0.8.txt[1.7.0.8], - link:RelNotes/1.7.0.7.txt[1.7.0.7], - link:RelNotes/1.7.0.6.txt[1.7.0.6], - link:RelNotes/1.7.0.5.txt[1.7.0.5], - link:RelNotes/1.7.0.4.txt[1.7.0.4], - link:RelNotes/1.7.0.3.txt[1.7.0.3], - link:RelNotes/1.7.0.2.txt[1.7.0.2], - link:RelNotes/1.7.0.1.txt[1.7.0.1], - link:RelNotes/1.7.0.txt[1.7.0]. - -* link:v1.6.6.3/git.html[documentation for release 1.6.6.3] - -* release notes for - link:RelNotes/1.6.6.3.txt[1.6.6.3], - link:RelNotes/1.6.6.2.txt[1.6.6.2], - link:RelNotes/1.6.6.1.txt[1.6.6.1], - link:RelNotes/1.6.6.txt[1.6.6]. - -* link:v1.6.5.9/git.html[documentation for release 1.6.5.9] - -* release notes for - link:RelNotes/1.6.5.9.txt[1.6.5.9], - link:RelNotes/1.6.5.8.txt[1.6.5.8], - link:RelNotes/1.6.5.7.txt[1.6.5.7], - link:RelNotes/1.6.5.6.txt[1.6.5.6], - link:RelNotes/1.6.5.5.txt[1.6.5.5], - link:RelNotes/1.6.5.4.txt[1.6.5.4], - link:RelNotes/1.6.5.3.txt[1.6.5.3], - link:RelNotes/1.6.5.2.txt[1.6.5.2], - link:RelNotes/1.6.5.1.txt[1.6.5.1], - link:RelNotes/1.6.5.txt[1.6.5]. - -* link:v1.6.4.5/git.html[documentation for release 1.6.4.5] - -* release notes for - link:RelNotes/1.6.4.5.txt[1.6.4.5], - link:RelNotes/1.6.4.4.txt[1.6.4.4], - link:RelNotes/1.6.4.3.txt[1.6.4.3], - link:RelNotes/1.6.4.2.txt[1.6.4.2], - link:RelNotes/1.6.4.1.txt[1.6.4.1], - link:RelNotes/1.6.4.txt[1.6.4]. - -* link:v1.6.3.4/git.html[documentation for release 1.6.3.4] - -* release notes for - link:RelNotes/1.6.3.4.txt[1.6.3.4], - link:RelNotes/1.6.3.3.txt[1.6.3.3], - link:RelNotes/1.6.3.2.txt[1.6.3.2], - link:RelNotes/1.6.3.1.txt[1.6.3.1], - link:RelNotes/1.6.3.txt[1.6.3]. - -* release notes for - link:RelNotes/1.6.2.5.txt[1.6.2.5], - link:RelNotes/1.6.2.4.txt[1.6.2.4], - link:RelNotes/1.6.2.3.txt[1.6.2.3], - link:RelNotes/1.6.2.2.txt[1.6.2.2], - link:RelNotes/1.6.2.1.txt[1.6.2.1], - link:RelNotes/1.6.2.txt[1.6.2]. - -* link:v1.6.1.3/git.html[documentation for release 1.6.1.3] - -* release notes for - link:RelNotes/1.6.1.3.txt[1.6.1.3], - link:RelNotes/1.6.1.2.txt[1.6.1.2], - link:RelNotes/1.6.1.1.txt[1.6.1.1], - link:RelNotes/1.6.1.txt[1.6.1]. - -* link:v1.6.0.6/git.html[documentation for release 1.6.0.6] - -* release notes for - link:RelNotes/1.6.0.6.txt[1.6.0.6], - link:RelNotes/1.6.0.5.txt[1.6.0.5], - link:RelNotes/1.6.0.4.txt[1.6.0.4], - link:RelNotes/1.6.0.3.txt[1.6.0.3], - link:RelNotes/1.6.0.2.txt[1.6.0.2], - link:RelNotes/1.6.0.1.txt[1.6.0.1], - link:RelNotes/1.6.0.txt[1.6.0]. - -* link:v1.5.6.6/git.html[documentation for release 1.5.6.6] - -* release notes for - link:RelNotes/1.5.6.6.txt[1.5.6.6], - link:RelNotes/1.5.6.5.txt[1.5.6.5], - link:RelNotes/1.5.6.4.txt[1.5.6.4], - link:RelNotes/1.5.6.3.txt[1.5.6.3], - link:RelNotes/1.5.6.2.txt[1.5.6.2], - link:RelNotes/1.5.6.1.txt[1.5.6.1], - link:RelNotes/1.5.6.txt[1.5.6]. - -* link:v1.5.5.6/git.html[documentation for release 1.5.5.6] - -* release notes for - link:RelNotes/1.5.5.6.txt[1.5.5.6], - link:RelNotes/1.5.5.5.txt[1.5.5.5], - link:RelNotes/1.5.5.4.txt[1.5.5.4], - link:RelNotes/1.5.5.3.txt[1.5.5.3], - link:RelNotes/1.5.5.2.txt[1.5.5.2], - link:RelNotes/1.5.5.1.txt[1.5.5.1], - link:RelNotes/1.5.5.txt[1.5.5]. - -* link:v1.5.4.7/git.html[documentation for release 1.5.4.7] - -* release notes for - link:RelNotes/1.5.4.7.txt[1.5.4.7], - link:RelNotes/1.5.4.6.txt[1.5.4.6], - link:RelNotes/1.5.4.5.txt[1.5.4.5], - link:RelNotes/1.5.4.4.txt[1.5.4.4], - link:RelNotes/1.5.4.3.txt[1.5.4.3], - link:RelNotes/1.5.4.2.txt[1.5.4.2], - link:RelNotes/1.5.4.1.txt[1.5.4.1], - link:RelNotes/1.5.4.txt[1.5.4]. - -* link:v1.5.3.8/git.html[documentation for release 1.5.3.8] - -* release notes for - link:RelNotes/1.5.3.8.txt[1.5.3.8], - link:RelNotes/1.5.3.7.txt[1.5.3.7], - link:RelNotes/1.5.3.6.txt[1.5.3.6], - link:RelNotes/1.5.3.5.txt[1.5.3.5], - link:RelNotes/1.5.3.4.txt[1.5.3.4], - link:RelNotes/1.5.3.3.txt[1.5.3.3], - link:RelNotes/1.5.3.2.txt[1.5.3.2], - link:RelNotes/1.5.3.1.txt[1.5.3.1], - link:RelNotes/1.5.3.txt[1.5.3]. - -* link:v1.5.2.5/git.html[documentation for release 1.5.2.5] - -* release notes for - link:RelNotes/1.5.2.5.txt[1.5.2.5], - link:RelNotes/1.5.2.4.txt[1.5.2.4], - link:RelNotes/1.5.2.3.txt[1.5.2.3], - link:RelNotes/1.5.2.2.txt[1.5.2.2], - link:RelNotes/1.5.2.1.txt[1.5.2.1], - link:RelNotes/1.5.2.txt[1.5.2]. - -* link:v1.5.1.6/git.html[documentation for release 1.5.1.6] - -* release notes for - link:RelNotes/1.5.1.6.txt[1.5.1.6], - link:RelNotes/1.5.1.5.txt[1.5.1.5], - link:RelNotes/1.5.1.4.txt[1.5.1.4], - link:RelNotes/1.5.1.3.txt[1.5.1.3], - link:RelNotes/1.5.1.2.txt[1.5.1.2], - link:RelNotes/1.5.1.1.txt[1.5.1.1], - link:RelNotes/1.5.1.txt[1.5.1]. - -* link:v1.5.0.7/git.html[documentation for release 1.5.0.7] - -* release notes for - link:RelNotes/1.5.0.7.txt[1.5.0.7], - link:RelNotes/1.5.0.6.txt[1.5.0.6], - link:RelNotes/1.5.0.5.txt[1.5.0.5], - link:RelNotes/1.5.0.3.txt[1.5.0.3], - link:RelNotes/1.5.0.2.txt[1.5.0.2], - link:RelNotes/1.5.0.1.txt[1.5.0.1], - link:RelNotes/1.5.0.txt[1.5.0]. - -* documentation for release link:v1.4.4.4/git.html[1.4.4.4], - link:v1.3.3/git.html[1.3.3], - link:v1.2.6/git.html[1.2.6], - link:v1.0.13/git.html[1.0.13]. - -============ - -endif::stalenotes[] OPTIONS ------- diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index afcf9e9abf..5b6722840e 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.13.0 +DEF_VER=v2.13.1 LF=' ' From b3a847d1dba7688435058d8d4161b3456bc1103d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 5 Jun 2017 09:32:25 +0900 Subject: [PATCH 459/566] Seventh batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 71 ++++++++++++------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index 1bb29b6e9d..e4ca72e50e 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -18,6 +18,10 @@ Backward compatibility notes. might still be cases that need to be addressed--bug reports are greatly appreciated. + * The experiment to improve the hunk-boundary selection of textual + diff output has finished, and the "indent heuristics" has now + become the default. + Updates since v2.13 ------------------- @@ -46,7 +50,7 @@ UI, Workflows & Features * "git send-email" learned to run sendemail-validate hook to inspect and reject a message before sending it out. - (merge 6489660b4b jt/send-email-validate-hook later to maint). + (merge 177409e589 jt/send-email-validate-hook later to maint). * There is no good reason why "git fetch $there $sha1" should fail when the $sha1 names an object at the tip of an advertised ref, @@ -60,6 +64,9 @@ UI, Workflows & Features current repository to determine if the contents from the named path should be included. + * Make the "indent" heuristics the default in "diff" and diff.indentHeuristics + configuration variable an escape hatch for those who do no want it. + Performance, Internal Implementation, Development Support etc. @@ -76,7 +83,6 @@ Performance, Internal Implementation, Development Support etc. * Travis CI gained a task to format the documentation with both AsciiDoc and AsciiDoctor. - (merge 505ad91304 ls/travis-doc-asciidoctor later to maint). * Some platforms have ulong that is smaller than time_t, and our historical use of ulong for timestamp would mean they cannot @@ -116,7 +122,12 @@ Performance, Internal Implementation, Development Support etc. * Update the C style recommendation for notes for translators, as recent versions of gettext tools can work with our style of multi-line comments. - (merge 66f5f6dca9 ab/c-translators-comment-style later to maint). + + * The implementation of "ref" API around the "packed refs" have been + cleaned up, in preparation for further changes. + + * The internal logic used in "git blame" has been libified to make it + easier to use by cgit. Also contains various documentation updates and code clean-ups. @@ -136,40 +147,32 @@ notes for details). mishandled a trailer block whose last line is an incomplete line. This has been fixed so that an additional sign-off etc. are added after completing the existing incomplete line. - (merge 44dc738a39 jt/use-trailer-api-in-commands later to maint). * The codepath in "git am" that is used when running "git rebase" leaked memory held for the log message of the commits being rebased. - (merge 721f5f1e35 jk/am-leakfix later to maint). * "git clone --config var=val" is a way to populate the per-repository configuration file of the new repository, but it did not work well when val is an empty string. This has been fixed. - (merge db4eca1fea jn/clone-add-empty-config-from-command-line later to maint). * Setting "log.decorate=false" in the configuration file did not take effect in v2.13, which has been corrected. - (merge c74271aae7 ah/log-decorate-default-to-auto later to maint). * A few codepaths in "checkout" and "am" working on an unborn branch tried to access an uninitialized piece of memory. - (merge 57e0ef0e0e rs/checkout-am-fix-unborn later to maint). * The Web interface to gmane news archive is long gone, even though the articles are still accessible via NTTP. Replace the links with ones to public-inbox.org. Because their message identification is based on the actual message-id, it is likely that it will be easier to migrate away from it if/when necessary. - (merge 5840eb9d14 ab/doc-replace-gmane-links later to maint). * The receive-pack program now makes sure that the push certificate records the same set of push options used for pushing. - (merge cbaf82cc6b jt/push-options-doc later to maint). * Tests have been updated to pass under GETTEXT_POISON (a mechanism to ensure that output strings that should not be translated are not translated by mistake), and TravisCI is told to run them. - (merge b8e188f6f5 ab/fix-poison-tests later to maint). * "git checkout --recurse-submodules" did not quite work with a submodule that itself has submodules. @@ -185,58 +188,47 @@ notes for details). in the output, without inspecting individual objects. This strategy however would not work well when "--local" and other options are in use, and need to be disabled. - (merge da5a1f8100 jk/disable-pack-reuse-when-broken later to maint). * Fix memory leaks pointed out by Coverity (and people). - (merge 443a12f37b js/plug-leaks later to maint). * "git read-tree -m" (no tree-ish) gave a nonsense suggestion "use --empty if you want to clear the index". With "-m", such a request will still fail anyway, as you'd need to name at least one tree-ish to be merged. - (merge b9b10d3681 jc/read-tree-empty-with-m later to maint). * Make sure our tests would pass when the sources are checked out with "platform native" line ending convention by default on Windows. Some "text" files out tests use and the test scripts themselves that are meant to be run with /bin/sh, ought to be checked out with eol=LF even on Windows. - (merge 2779f66505 js/eol-on-ourselves later to maint). * Introduce the BUG() macro to improve die("BUG: ..."). (merge 3d7dd2d3b6 jk/bug-to-abort later to maint). * Clarify documentation for include.path and includeIf..path configuration variables. - (merge ce933ebd5a jk/doc-config-include later to maint). * Git sometimes gives an advice in a rhetorical question that does not require an answer, which can confuse new users and non native speakers. Attempt to rephrase them. - (merge 6963893943 ja/do-not-ask-needless-questions later to maint). * A few http:// links that are redirected to https:// in the documentation have been updated to https:// links. - (merge 5e68729fd9 jk/update-links-in-docs later to maint). * "git for-each-ref --format=..." with %(HEAD) in the format used to resolve the HEAD symref as many times as it had processed refs, which was wasteful, and "git branch" shared the same problem. - (merge 613a0e52ea kn/ref-filter-branch-list later to maint). * Regression fix to topic recently merged to 'master'. - (merge d096d7f1ef pw/rebase-i-regression-fix later to maint). * The shell completion script (in contrib/) learned "git stash" has a new "push" subcommand. - (merge 3851e4483f tg/stash-push-fixup later to maint). * "git interpret-trailers", when used as GIT_EDITOR for "git commit -v", looked for and appended to a trailer block at the very end, i.e. at the end of the "diff" output. The command has been corrected to pay attention to the cut-mark line "commit -v" adds to the buffer---the real trailer block should appear just before it. - (merge d76650b8d1 bm/interpret-trailers-cut-line-is-eom later to maint). * A test allowed both "git push" and "git receive-pack" on the other end write their traces into the same file. This is OK on platforms @@ -244,12 +236,10 @@ notes for details). but on other platforms led to a mangled output, causing intermittent test failures. This has been fixed by disabling traces from "receive-pack" in the test. - (merge 71406ed4d6 jk/alternate-ref-optim later to maint). * Tag objects, which are not reachable from any ref, that point at missing objects were mishandled by "git gc" and friends (they should silently be ignored instead) - (merge a3ba6bf10a jk/ignore-broken-tags-when-ignoring-missing-links later to maint). * "git describe --contains" penalized light-weight tags so much that they were almost never considered. Instead, give them about the @@ -269,12 +259,11 @@ notes for details). * "git send-email" now uses Net::SMTP::SSL, which is obsolete, only when needed. Recent versions of Net::SMTP can do TLS natively. - (merge 0ead000c3a dk/send-email-avoid-net-smtp-ssl-when-able later to maint). + (merge bfbfc9a953 dk/send-email-avoid-net-smtp-ssl-when-able later to maint). * "foo\bar\baz" in "git fetch foo\bar\baz", even though there is no slashes in it, cannot be a nickname for a remote on Windows, as that is likely to be a pathname on a local filesystem. - (merge d9244ecf4f js/bs-is-a-dir-sep-on-windows later to maint). * "git clean -d" used to clean directories that has ignored files, even though the command should not lose ignored ones without "-x". @@ -292,30 +281,26 @@ notes for details). was quite broken on some big-endian platforms and/or platforms that do not like unaligned fetches. Update to the upstream code which has already fixed these issues. - (merge a0103914c2 ab/sha1dc-maint later to maint). * "git am -h" triggered a BUG(). - (merge f3a2fffe06 jk/unbreak-am-h later to maint). * The interaction of "url.*.insteadOf" and custom URL scheme's whitelisting is now documented better. - (merge 2c9a2ae285 jk/url-insteadof-config later to maint). + + * The timestamp of the index file is now taken after the file is + closed, to help Windows, on which a stale timestamp is reported by + fstat() on a file that is opened for writing and data was written + but not yet closed. + (merge 9f41c7a6b3 jh/close-index-before-stat later to maint). + + * "git pull --rebase --autostash" didn't auto-stash when the local history + fast-forwards to the upstream. + (merge f15e7cf5cc tb/pull-ff-rebase-autostash later to maint). * Other minor doc, test and build updates and code cleanups. - (merge 515360f9e9 jn/credential-doc-on-clear later to maint). - (merge 0e6d899fee ab/aix-needs-compat-regex later to maint). - (merge e294e8959f jc/apply-fix-mismerge later to maint). - (merge 7f1b225153 bw/submodule-with-bs-path later to maint). - (merge c8f7c8b704 tb/dedup-crlf-tests later to maint). - (merge 449456ad47 sg/core-filemode-doc-typofix later to maint). - (merge ba4dce784e km/log-showsignature-doc later to maint). (merge c5a9157393 jh/memihash-opt later to maint). - (merge 80f4cd8046 ab/ref-filter-no-contains later to maint). - (merge b275da816c ah/doc-interpret-trailers-ifexists later to maint). - (merge fc7a5edb55 ah/doc-pretty-format-fix later to maint). - (merge 7e95fcb4b5 sb/t5531-update-desc later to maint). - (merge b8f354f294 sd/t3200-typofix later to maint). - (merge ba746ff9c9 ah/doc-filter-branch-export-env later to maint). (merge 44e2ff09ce ab/t3070-test-dedup later to maint). (merge 9ee4aa95db rf/completion-config-commit later to maint). - (merge fb87327aee ah/doc-rev-parse-short-default later to maint). + (merge ef4fe5617e jk/connect-symref-info-leak-fix later to maint). + (merge a56eea28c4 jk/drop-free-refspecs later to maint). + (merge 0c79cee697 ad/pull-remote-doc later to maint). From 154ffeecc6b13082b06fab96e5de698d606aa790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Fri, 2 Jun 2017 10:33:30 +0000 Subject: [PATCH 460/566] perf: work around the tested repo having an index.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the tested repo has an index.lock file it should be removed. This file may be present if e.g. git-status previously crashed in that repo, and it will make a lot of git commands fail. Let's try harder and remove the lock. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/perf/perf-lib.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index b6fc880395..b50211b259 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -108,7 +108,14 @@ test_perf_create_repo_from () { cd "$repo" && "$MODERN_GIT" init -q && test_perf_do_repo_symlink_config_ && - mv .git/hooks .git/hooks-disabled 2>/dev/null + mv .git/hooks .git/hooks-disabled 2>/dev/null && + if test -f .git/index.lock + then + # We may be copying a repo that can't run "git + # status" due to a locked index. Since we have + # a copy it's fine to remove the lock. + rm .git/index.lock + fi ) || error "failed to copy repository '$source' to '$repo'" } From 5237e0eb599c208d1c3f6da997c06ee18a1dc201 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:58 -0700 Subject: [PATCH 461/566] notes-merge: convert notes_merge* to struct object_id Convert notes_merge and notes_merge_commit to use struct object_id. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/notes.c | 6 ++--- notes-merge.c | 58 ++++++++++++++++++++++++------------------------- notes-merge.h | 20 ++++++++--------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/builtin/notes.c b/builtin/notes.c index b13fc87894..2ebc2b7c43 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -724,7 +724,7 @@ static int merge_commit(struct notes_merge_options *o) if (!o->local_ref) die(_("failed to resolve NOTES_MERGE_REF")); - if (notes_merge_commit(o, t, partial, oid.hash)) + if (notes_merge_commit(o, t, partial, &oid)) die(_("failed to finalize notes merge")); /* Reuse existing commit message in reflog message */ @@ -842,9 +842,9 @@ static int merge(int argc, const char **argv, const char *prefix) remote_ref.buf, default_notes_ref()); strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */ - result = notes_merge(&o, t, result_oid.hash); + result = notes_merge(&o, t, &result_oid); - if (result >= 0) /* Merge resulted (trivially) in result_sha1 */ + if (result >= 0) /* Merge resulted (trivially) in result_oid */ /* Update default notes ref with new commit */ update_ref(msg.buf, default_notes_ref(), result_oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); diff --git a/notes-merge.c b/notes-merge.c index 9a1a49506e..9dbf7f6a31 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -533,17 +533,17 @@ static int merge_from_diffs(struct notes_merge_options *o, int notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, - unsigned char *result_sha1) + struct object_id *result_oid) { struct object_id local_oid, remote_oid; struct commit *local, *remote; struct commit_list *bases = NULL; - const unsigned char *base_sha1, *base_tree_sha1; + const struct object_id *base_oid, *base_tree_oid; int result = 0; assert(o->local_ref && o->remote_ref); assert(!strcmp(o->local_ref, local_tree->ref)); - hashclr(result_sha1); + oidclr(result_oid); trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n", o->local_ref, o->remote_ref); @@ -553,16 +553,16 @@ int notes_merge(struct notes_merge_options *o, die("Failed to resolve local notes ref '%s'", o->local_ref); else if (!check_refname_format(o->local_ref, 0) && is_null_oid(&local_oid)) - local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ + local = NULL; /* local_oid == null_oid indicates unborn ref */ else if (!(local = lookup_commit_reference(&local_oid))) die("Could not parse local commit %s (%s)", oid_to_hex(&local_oid), o->local_ref); trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid)); - /* Dereference o->remote_ref into remote_sha1 */ + /* Dereference o->remote_ref into remote_oid */ if (get_oid(o->remote_ref, &remote_oid)) { /* - * Failed to get remote_sha1. If o->remote_ref looks like an + * Failed to get remote_oid. If o->remote_ref looks like an * unborn ref, perform the merge using an empty notes tree. */ if (!check_refname_format(o->remote_ref, 0)) { @@ -583,12 +583,12 @@ int notes_merge(struct notes_merge_options *o, "(%s)", o->remote_ref, o->local_ref); if (!local) { /* result == remote commit */ - hashcpy(result_sha1, remote_oid.hash); + oidcpy(result_oid, &remote_oid); goto found_result; } if (!remote) { /* result == local commit */ - hashcpy(result_sha1, local_oid.hash); + oidcpy(result_oid, &local_oid); goto found_result; } assert(local && remote); @@ -596,47 +596,47 @@ int notes_merge(struct notes_merge_options *o, /* Find merge bases */ bases = get_merge_bases(local, remote); if (!bases) { - base_sha1 = null_sha1; - base_tree_sha1 = EMPTY_TREE_SHA1_BIN; + base_oid = &null_oid; + base_tree_oid = &empty_tree_oid; if (o->verbosity >= 4) printf("No merge base found; doing history-less merge\n"); } else if (!bases->next) { - base_sha1 = bases->item->object.oid.hash; - base_tree_sha1 = bases->item->tree->object.oid.hash; + base_oid = &bases->item->object.oid; + base_tree_oid = &bases->item->tree->object.oid; if (o->verbosity >= 4) printf("One merge base found (%.7s)\n", - sha1_to_hex(base_sha1)); + oid_to_hex(base_oid)); } else { /* TODO: How to handle multiple merge-bases? */ - base_sha1 = bases->item->object.oid.hash; - base_tree_sha1 = bases->item->tree->object.oid.hash; + base_oid = &bases->item->object.oid; + base_tree_oid = &bases->item->tree->object.oid; if (o->verbosity >= 3) printf("Multiple merge bases found. Using the first " - "(%.7s)\n", sha1_to_hex(base_sha1)); + "(%.7s)\n", oid_to_hex(base_oid)); } if (o->verbosity >= 4) printf("Merging remote commit %.7s into local commit %.7s with " "merge-base %.7s\n", oid_to_hex(&remote->object.oid), oid_to_hex(&local->object.oid), - sha1_to_hex(base_sha1)); + oid_to_hex(base_oid)); - if (!hashcmp(remote->object.oid.hash, base_sha1)) { + if (!oidcmp(&remote->object.oid, base_oid)) { /* Already merged; result == local commit */ if (o->verbosity >= 2) printf("Already up-to-date!\n"); - hashcpy(result_sha1, local->object.oid.hash); + oidcpy(result_oid, &local->object.oid); goto found_result; } - if (!hashcmp(local->object.oid.hash, base_sha1)) { + if (!oidcmp(&local->object.oid, base_oid)) { /* Fast-forward; result == remote commit */ if (o->verbosity >= 2) printf("Fast-forward\n"); - hashcpy(result_sha1, remote->object.oid.hash); + oidcpy(result_oid, &remote->object.oid); goto found_result; } - result = merge_from_diffs(o, base_tree_sha1, local->tree->object.oid.hash, + result = merge_from_diffs(o, base_tree_oid->hash, local->tree->object.oid.hash, remote->tree->object.oid.hash, local_tree); if (result != 0) { /* non-trivial merge (with or without conflicts) */ @@ -646,28 +646,28 @@ int notes_merge(struct notes_merge_options *o, commit_list_insert(local, &parents); create_notes_commit(local_tree, parents, o->commit_msg.buf, o->commit_msg.len, - result_sha1); + result_oid->hash); } found_result: free_commit_list(bases); strbuf_release(&(o->commit_msg)); - trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n", - result, sha1_to_hex(result_sha1)); + trace_printf("notes_merge(): result = %i, result_oid = %.7s\n", + result, oid_to_hex(result_oid)); return result; } int notes_merge_commit(struct notes_merge_options *o, struct notes_tree *partial_tree, struct commit *partial_commit, - unsigned char *result_sha1) + struct object_id *result_oid) { /* * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all * found notes to 'partial_tree'. Write the updated notes tree to * the DB, and commit the resulting tree object while reusing the * commit message and parents from 'partial_commit'. - * Finally store the new commit object SHA1 into 'result_sha1'. + * Finally store the new commit object OID into 'result_oid'. */ DIR *dir; struct dirent *e; @@ -721,11 +721,11 @@ int notes_merge_commit(struct notes_merge_options *o, } create_notes_commit(partial_tree, partial_commit->parents, - msg, strlen(msg), result_sha1); + msg, strlen(msg), result_oid->hash); unuse_commit_buffer(partial_commit, buffer); if (o->verbosity >= 4) printf("Finalized notes merge commit: %s\n", - sha1_to_hex(result_sha1)); + oid_to_hex(result_oid)); strbuf_release(&path); closedir(dir); return 0; diff --git a/notes-merge.h b/notes-merge.h index 0d890563b5..f815f23451 100644 --- a/notes-merge.h +++ b/notes-merge.h @@ -32,16 +32,16 @@ void init_notes_merge_options(struct notes_merge_options *o); * outcomes: * * 1. The merge trivially results in an existing commit (e.g. fast-forward or - * already-up-to-date). 'local_tree' is untouched, the SHA1 of the result - * is written into 'result_sha1' and 0 is returned. + * already-up-to-date). 'local_tree' is untouched, the OID of the result + * is written into 'result_oid' and 0 is returned. * 2. The merge successfully completes, producing a merge commit. local_tree - * contains the updated notes tree, the SHA1 of the resulting commit is - * written into 'result_sha1', and 1 is returned. + * contains the updated notes tree, the OID of the resulting commit is + * written into 'result_oid', and 1 is returned. * 3. The merge results in conflicts. This is similar to #2 in that the * partial merge result (i.e. merge result minus the unmerged entries) - * are stored in 'local_tree', and the SHA1 or the resulting commit + * are stored in 'local_tree', and the OID or the resulting commit * (to be amended when the conflicts have been resolved) is written into - * 'result_sha1'. The unmerged entries are written into the + * 'result_oid'. The unmerged entries are written into the * .git/NOTES_MERGE_WORKTREE directory with conflict markers. * -1 is returned. * @@ -52,7 +52,7 @@ void init_notes_merge_options(struct notes_merge_options *o); */ int notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, - unsigned char *result_sha1); + struct object_id *result_oid); /* * Finalize conflict resolution from an earlier notes_merge() @@ -62,13 +62,13 @@ int notes_merge(struct notes_merge_options *o, * call to notes_merge(). * * This function will add the (now resolved) notes in .git/NOTES_MERGE_WORKTREE - * to 'partial_tree', and create a final notes merge commit, the SHA1 of which - * will be stored in 'result_sha1'. + * to 'partial_tree', and create a final notes merge commit, the OID of which + * will be stored in 'result_oid'. */ int notes_merge_commit(struct notes_merge_options *o, struct notes_tree *partial_tree, struct commit *partial_commit, - unsigned char *result_sha1); + struct object_id *result_oid); /* * Abort conflict resolution from an earlier notes_merge() From 9d6babb2f9115892b7ddc58a9a3c44ae52da1712 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:30:59 -0700 Subject: [PATCH 462/566] notes-merge: convert merge_from_diffs to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- notes-merge.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/notes-merge.c b/notes-merge.c index 9dbf7f6a31..be78f19549 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -114,8 +114,8 @@ static struct object_id uninitialized = { }; static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, - const unsigned char *base, - const unsigned char *remote, + const struct object_id *base, + const struct object_id *remote, int *num_changes) { struct diff_options opt; @@ -123,13 +123,13 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, int i, len = 0; trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n", - sha1_to_hex(base), sha1_to_hex(remote)); + oid_to_hex(base), oid_to_hex(remote)); diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opt); - diff_tree_sha1(base, remote, "", &opt); + diff_tree_sha1(base->hash, remote->hash, "", &opt); diffcore_std(&opt); changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair)); @@ -179,20 +179,20 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, static void diff_tree_local(struct notes_merge_options *o, struct notes_merge_pair *changes, int len, - const unsigned char *base, - const unsigned char *local) + const struct object_id *base, + const struct object_id *local) { struct diff_options opt; int i; trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n", - len, sha1_to_hex(base), sha1_to_hex(local)); + len, oid_to_hex(base), oid_to_hex(local)); diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opt); - diff_tree_sha1(base, local, "", &opt); + diff_tree_sha1(base->hash, local->hash, "", &opt); diffcore_std(&opt); for (i = 0; i < diff_queued_diff.nr; i++) { @@ -505,16 +505,17 @@ static int merge_changes(struct notes_merge_options *o, } static int merge_from_diffs(struct notes_merge_options *o, - const unsigned char *base, - const unsigned char *local, - const unsigned char *remote, struct notes_tree *t) + const struct object_id *base, + const struct object_id *local, + const struct object_id *remote, + struct notes_tree *t) { struct notes_merge_pair *changes; int num_changes, conflicts; trace_printf("\tmerge_from_diffs(base = %.7s, local = %.7s, " - "remote = %.7s)\n", sha1_to_hex(base), sha1_to_hex(local), - sha1_to_hex(remote)); + "remote = %.7s)\n", oid_to_hex(base), oid_to_hex(local), + oid_to_hex(remote)); changes = diff_tree_remote(o, base, remote, &num_changes); diff_tree_local(o, changes, num_changes, base, local); @@ -636,8 +637,8 @@ int notes_merge(struct notes_merge_options *o, goto found_result; } - result = merge_from_diffs(o, base_tree_oid->hash, local->tree->object.oid.hash, - remote->tree->object.oid.hash, local_tree); + result = merge_from_diffs(o, base_tree_oid, &local->tree->object.oid, + &remote->tree->object.oid, local_tree); if (result != 0) { /* non-trivial merge (with or without conflicts) */ /* Commit (partial) result */ From d7a7c708dac5e93b715d1d1f5897be2b0c9bcf7d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:00 -0700 Subject: [PATCH 463/566] notes-merge: convert find_notes_merge_pair_ps to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- notes-merge.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/notes-merge.c b/notes-merge.c index be78f19549..55dbb3659e 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -58,7 +58,7 @@ static int verify_notes_filepair(struct diff_filepair *p, unsigned char *sha1) } static struct notes_merge_pair *find_notes_merge_pair_pos( - struct notes_merge_pair *list, int len, unsigned char *obj, + struct notes_merge_pair *list, int len, struct object_id *obj, int insert_new, int *occupied) { /* @@ -75,7 +75,7 @@ static struct notes_merge_pair *find_notes_merge_pair_pos( int i = last_index < len ? last_index : len - 1; int prev_cmp = 0, cmp = -1; while (i >= 0 && i < len) { - cmp = hashcmp(obj, list[i].obj.hash); + cmp = oidcmp(obj, &list[i].obj); if (!cmp) /* obj belongs @ i */ break; else if (cmp < 0 && prev_cmp <= 0) /* obj belongs < i */ @@ -138,19 +138,19 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int occupied; - unsigned char obj[20]; + struct object_id obj; - if (verify_notes_filepair(p, obj)) { + if (verify_notes_filepair(p, obj.hash)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, oid_to_hex(&p->one->oid), oid_to_hex(&p->two->oid)); continue; } - mp = find_notes_merge_pair_pos(changes, len, obj, 1, &occupied); + mp = find_notes_merge_pair_pos(changes, len, &obj, 1, &occupied); if (occupied) { /* We've found an addition/deletion pair */ - assert(!hashcmp(mp->obj.hash, obj)); + assert(!oidcmp(&mp->obj, &obj)); if (is_null_oid(&p->one->oid)) { /* addition */ assert(is_null_oid(&mp->remote)); oidcpy(&mp->remote, &p->two->oid); @@ -160,7 +160,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, } else assert(!"Invalid existing change recorded"); } else { - hashcpy(mp->obj.hash, obj); + oidcpy(&mp->obj, &obj); oidcpy(&mp->base, &p->one->oid); oidcpy(&mp->local, &uninitialized); oidcpy(&mp->remote, &p->two->oid); @@ -199,25 +199,25 @@ static void diff_tree_local(struct notes_merge_options *o, struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int match; - unsigned char obj[20]; + struct object_id obj; - if (verify_notes_filepair(p, obj)) { + if (verify_notes_filepair(p, obj.hash)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, oid_to_hex(&p->one->oid), oid_to_hex(&p->two->oid)); continue; } - mp = find_notes_merge_pair_pos(changes, len, obj, 0, &match); + mp = find_notes_merge_pair_pos(changes, len, &obj, 0, &match); if (!match) { trace_printf("\t\tIgnoring local-only change for %s: " - "%.7s -> %.7s\n", sha1_to_hex(obj), + "%.7s -> %.7s\n", oid_to_hex(&obj), oid_to_hex(&p->one->oid), oid_to_hex(&p->two->oid)); continue; } - assert(!hashcmp(mp->obj.hash, obj)); + assert(!oidcmp(&mp->obj, &obj)); if (is_null_oid(&p->two->oid)) { /* deletion */ /* * Either this is a true deletion (1), or it is part From 4d77896eeba6727e9a9fac9d78a8d7695737b772 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:01 -0700 Subject: [PATCH 464/566] notes-merge: convert verify_notes_filepair to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- notes-merge.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/notes-merge.c b/notes-merge.c index 55dbb3659e..962e9b1bca 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -22,21 +22,21 @@ void init_notes_merge_options(struct notes_merge_options *o) o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT; } -static int path_to_sha1(const char *path, unsigned char *sha1) +static int path_to_oid(const char *path, struct object_id *oid) { - char hex_sha1[40]; + char hex_oid[GIT_SHA1_HEXSZ]; int i = 0; - while (*path && i < 40) { + while (*path && i < GIT_SHA1_HEXSZ) { if (*path != '/') - hex_sha1[i++] = *path; + hex_oid[i++] = *path; path++; } - if (*path || i != 40) + if (*path || i != GIT_SHA1_HEXSZ) return -1; - return get_sha1_hex(hex_sha1, sha1); + return get_oid_hex(hex_oid, oid); } -static int verify_notes_filepair(struct diff_filepair *p, unsigned char *sha1) +static int verify_notes_filepair(struct diff_filepair *p, struct object_id *oid) { switch (p->status) { case DIFF_STATUS_MODIFIED: @@ -54,7 +54,7 @@ static int verify_notes_filepair(struct diff_filepair *p, unsigned char *sha1) return -1; } assert(!strcmp(p->one->path, p->two->path)); - return path_to_sha1(p->one->path, sha1); + return path_to_oid(p->one->path, oid); } static struct notes_merge_pair *find_notes_merge_pair_pos( @@ -140,7 +140,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, int occupied; struct object_id obj; - if (verify_notes_filepair(p, obj.hash)) { + if (verify_notes_filepair(p, &obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, oid_to_hex(&p->one->oid), @@ -201,7 +201,7 @@ static void diff_tree_local(struct notes_merge_options *o, int match; struct object_id obj; - if (verify_notes_filepair(p, obj.hash)) { + if (verify_notes_filepair(p, &obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, oid_to_hex(&p->one->oid), From 9e5e0c289a900186b8943741cc632ea3bb6e1510 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:02 -0700 Subject: [PATCH 465/566] notes-merge: convert write_note_to_worktree to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- notes-merge.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/notes-merge.c b/notes-merge.c index 962e9b1bca..7d88857a80 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -292,11 +292,11 @@ static void check_notes_merge_worktree(struct notes_merge_options *o) git_path(NOTES_MERGE_WORKTREE)); } -static void write_buf_to_worktree(const unsigned char *obj, +static void write_buf_to_worktree(const struct object_id *obj, const char *buf, unsigned long size) { int fd; - char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj)); + char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj)); if (safe_create_leading_directories_const(path)) die_errno("unable to create directory for '%s'", path); @@ -320,19 +320,19 @@ static void write_buf_to_worktree(const unsigned char *obj, free(path); } -static void write_note_to_worktree(const unsigned char *obj, - const unsigned char *note) +static void write_note_to_worktree(const struct object_id *obj, + const struct object_id *note) { enum object_type type; unsigned long size; - void *buf = read_sha1_file(note, &type, &size); + void *buf = read_sha1_file(note->hash, &type, &size); if (!buf) die("cannot read note %s for object %s", - sha1_to_hex(note), sha1_to_hex(obj)); + oid_to_hex(note), oid_to_hex(obj)); if (type != OBJ_BLOB) die("blob expected in note %s for object %s", - sha1_to_hex(note), sha1_to_hex(obj)); + oid_to_hex(note), oid_to_hex(obj)); write_buf_to_worktree(obj, buf, size); free(buf); } @@ -358,7 +358,7 @@ static int ll_merge_in_worktree(struct notes_merge_options *o, if ((status < 0) || !result_buf.ptr) die("Failed to execute internal merge"); - write_buf_to_worktree(p->obj.hash, result_buf.ptr, result_buf.size); + write_buf_to_worktree(&p->obj, result_buf.ptr, result_buf.size); free(result_buf.ptr); return status; @@ -393,7 +393,7 @@ static int merge_one_change_manual(struct notes_merge_options *o, "deleted in %s and modified in %s. Version from %s " "left in tree.\n", oid_to_hex(&p->obj), lref, rref, rref); - write_note_to_worktree(p->obj.hash, p->remote.hash); + write_note_to_worktree(&p->obj, &p->remote); } else if (is_null_oid(&p->remote)) { /* D/F conflict, checkout p->local */ assert(!is_null_oid(&p->local)); @@ -402,7 +402,7 @@ static int merge_one_change_manual(struct notes_merge_options *o, "deleted in %s and modified in %s. Version from %s " "left in tree.\n", oid_to_hex(&p->obj), rref, lref, lref); - write_note_to_worktree(p->obj.hash, p->local.hash); + write_note_to_worktree(&p->obj, &p->local); } else { /* "regular" conflict, checkout result of ll_merge() */ const char *reason = "content"; From 66f414f885aa6f44ae9e764bdd3e3cd7bd80c5a3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:03 -0700 Subject: [PATCH 466/566] diff-tree: convert diff_tree_sha1 to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/blame.c | 20 ++++++++++---------- builtin/diff-tree.c | 8 +++----- builtin/diff.c | 2 +- builtin/fast-export.c | 4 ++-- builtin/log.c | 6 +++--- builtin/merge.c | 2 +- combine-diff.c | 4 ++-- diff.h | 5 +++-- line-log.c | 4 ++-- log-tree.c | 8 ++++---- merge-recursive.c | 2 +- notes-merge.c | 4 ++-- patch-ids.c | 4 ++-- revision.c | 4 ++-- sequencer.c | 4 ++-- tree-diff.c | 12 +++++++----- 16 files changed, 47 insertions(+), 46 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 5ad435380f..7645aa991d 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -565,9 +565,9 @@ static struct origin *find_origin(struct scoreboard *sb, if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else - diff_tree_sha1(parent->tree->object.oid.hash, - origin->commit->tree->object.oid.hash, - "", &diff_opts); + diff_tree_oid(&parent->tree->object.oid, + &origin->commit->tree->object.oid, + "", &diff_opts); diffcore_std(&diff_opts); if (!diff_queued_diff.nr) { @@ -635,9 +635,9 @@ static struct origin *find_rename(struct scoreboard *sb, if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else - diff_tree_sha1(parent->tree->object.oid.hash, - origin->commit->tree->object.oid.hash, - "", &diff_opts); + diff_tree_oid(&parent->tree->object.oid, + &origin->commit->tree->object.oid, + "", &diff_opts); diffcore_std(&diff_opts); for (i = 0; i < diff_queued_diff.nr; i++) { @@ -1262,7 +1262,7 @@ static void find_copy_in_parent(struct scoreboard *sb, /* Try "find copies harder" on new path if requested; * we do not want to use diffcore_rename() actually to * match things up; find_copies_harder is set only to - * force diff_tree_sha1() to feed all filepairs to diff_queue, + * force diff_tree_oid() to feed all filepairs to diff_queue, * and this code needs to be after diff_setup_done(), which * usually makes find-copies-harder imply copy detection. */ @@ -1274,9 +1274,9 @@ static void find_copy_in_parent(struct scoreboard *sb, if (is_null_oid(&target->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else - diff_tree_sha1(parent->tree->object.oid.hash, - target->commit->tree->object.oid.hash, - "", &diff_opts); + diff_tree_oid(&parent->tree->object.oid, + &target->commit->tree->object.oid, + "", &diff_opts); if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER)) diffcore_std(&diff_opts); diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 5ea1c14317..aef1676198 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -49,8 +49,8 @@ static int stdin_diff_trees(struct tree *tree1, const char *p) return -1; printf("%s %s\n", oid_to_hex(&tree1->object.oid), oid_to_hex(&tree2->object.oid)); - diff_tree_sha1(tree1->object.oid.hash, tree2->object.oid.hash, - "", &log_tree_opt.diffopt); + diff_tree_oid(&tree1->object.oid, &tree2->object.oid, + "", &log_tree_opt.diffopt); log_tree_diff_flush(&log_tree_opt); return 0; } @@ -148,9 +148,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) if (tree2->flags & UNINTERESTING) { SWAP(tree2, tree1); } - diff_tree_sha1(tree1->oid.hash, - tree2->oid.hash, - "", &opt->diffopt); + diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt); log_tree_diff_flush(opt); break; } diff --git a/builtin/diff.c b/builtin/diff.c index 73b4ff3db2..4c6a1a962f 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -174,7 +174,7 @@ static int builtin_diff_tree(struct rev_info *revs, swap = 1; oid[swap] = &ent0->item->oid; oid[1 - swap] = &ent1->item->oid; - diff_tree_sha1(oid[0]->hash, oid[1]->hash, "", &revs->diffopt); + diff_tree_oid(oid[0], oid[1], "", &revs->diffopt); log_tree_diff_flush(revs); return 0; } diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e242726f08..d57f36c438 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -562,8 +562,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) get_object_mark(&commit->parents->item->object) != 0 && !full_tree) { parse_commit_or_die(commit->parents->item); - diff_tree_sha1(commit->parents->item->tree->object.oid.hash, - commit->tree->object.oid.hash, "", &rev->diffopt); + diff_tree_oid(&commit->parents->item->tree->object.oid, + &commit->tree->object.oid, "", &rev->diffopt); } else diff_root_tree_oid(&commit->tree->object.oid, diff --git a/builtin/log.c b/builtin/log.c index 6bdba34446..4ef522ee50 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1043,9 +1043,9 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, diff_setup_done(&opts); - diff_tree_sha1(origin->tree->object.oid.hash, - head->tree->object.oid.hash, - "", &opts); + diff_tree_oid(&origin->tree->object.oid, + &head->tree->object.oid, + "", &opts); diffcore_std(&opts); diff_flush(&opts); diff --git a/builtin/merge.c b/builtin/merge.c index a4a098f40f..afaed6a2c2 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -415,7 +415,7 @@ static void finish(struct commit *head_commit, DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff_setup_done(&opts); - diff_tree_sha1(head->hash, new_head->hash, "", &opts); + diff_tree_oid(head, new_head, "", &opts); diffcore_std(&opts); diff_flush(&opts); } diff --git a/combine-diff.c b/combine-diff.c index c823645106..04c4ae8564 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1336,7 +1336,7 @@ static struct combine_diff_path *find_paths_generic(const struct object_id *oid, opt->output_format = stat_opt; else opt->output_format = DIFF_FORMAT_NO_OUTPUT; - diff_tree_sha1(parents->oid[i].hash, oid->hash, "", opt); + diff_tree_oid(&parents->oid[i], oid, "", opt); diffcore_std(opt); paths = intersect_paths(paths, i, num_parent); @@ -1463,7 +1463,7 @@ void diff_tree_combined(const struct object_id *oid, if (stat_opt) { diffopts.output_format = stat_opt; - diff_tree_sha1(parents->oid[0].hash, oid->hash, "", &diffopts); + diff_tree_oid(&parents->oid[0], oid, "", &diffopts); diffcore_std(&diffopts); if (opt->orderfile) diffcore_order(opt->orderfile); diff --git a/diff.h b/diff.h index 8d46a67099..e0b5034603 100644 --- a/diff.h +++ b/diff.h @@ -213,8 +213,9 @@ extern struct combine_diff_path *diff_tree_paths( struct combine_diff_path *p, const unsigned char *sha1, const unsigned char **parent_sha1, int nparent, struct strbuf *base, struct diff_options *opt); -extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, - const char *base, struct diff_options *opt); +extern int diff_tree_oid(const struct object_id *old_oid, + const struct object_id *new_oid, + const char *base, struct diff_options *opt); extern int diff_root_tree_oid(const struct object_id *new_oid, const char *base, struct diff_options *opt); diff --git a/line-log.c b/line-log.c index a3bd2f2d5f..2588ce0767 100644 --- a/line-log.c +++ b/line-log.c @@ -819,8 +819,8 @@ static void queue_diffs(struct line_log_data *range, assert(commit); DIFF_QUEUE_CLEAR(&diff_queued_diff); - diff_tree_sha1(parent ? parent->tree->object.oid.hash : NULL, - commit->tree->object.oid.hash, "", opt); + diff_tree_oid(parent ? &parent->tree->object.oid : NULL, + &commit->tree->object.oid, "", opt); if (opt->detect_rename) { filter_diffs_for_paths(range, 1); if (diff_might_be_rename()) diff --git a/log-tree.c b/log-tree.c index b40242534b..2903874ecf 100644 --- a/log-tree.c +++ b/log-tree.c @@ -822,8 +822,8 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log * we merged _in_. */ parse_commit_or_die(parents->item); - diff_tree_sha1(parents->item->tree->object.oid.hash, - oid->hash, "", &opt->diffopt); + diff_tree_oid(&parents->item->tree->object.oid, + oid, "", &opt->diffopt); log_tree_diff_flush(opt); return !opt->loginfo; } @@ -837,8 +837,8 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log struct commit *parent = parents->item; parse_commit_or_die(parent); - diff_tree_sha1(parent->tree->object.oid.hash, - oid->hash, "", &opt->diffopt); + diff_tree_oid(&parent->tree->object.oid, + oid, "", &opt->diffopt); log_tree_diff_flush(opt); showed_log |= !opt->loginfo; diff --git a/merge-recursive.c b/merge-recursive.c index ae5238d82c..5cc86df2d1 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -528,7 +528,7 @@ static struct string_list *get_renames(struct merge_options *o, opts.show_rename_progress = o->show_rename_progress; opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opts); - diff_tree_sha1(o_tree->object.oid.hash, tree->object.oid.hash, "", &opts); + diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts); diffcore_std(&opts); if (opts.needed_rename_limit > o->needed_rename_limit) o->needed_rename_limit = opts.needed_rename_limit; diff --git a/notes-merge.c b/notes-merge.c index 7d88857a80..70e3fbeefb 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -129,7 +129,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opt); - diff_tree_sha1(base->hash, remote->hash, "", &opt); + diff_tree_oid(base, remote, "", &opt); diffcore_std(&opt); changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair)); @@ -192,7 +192,7 @@ static void diff_tree_local(struct notes_merge_options *o, DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opt); - diff_tree_sha1(base->hash, local->hash, "", &opt); + diff_tree_oid(base, local, "", &opt); diffcore_std(&opt); for (i = 0; i < diff_queued_diff.nr; i++) { diff --git a/patch-ids.c b/patch-ids.c index aaf462c030..9c0ab9e67a 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -17,8 +17,8 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, return -1; if (commit->parents) - diff_tree_sha1(commit->parents->item->object.oid.hash, - commit->object.oid.hash, "", options); + diff_tree_oid(&commit->parents->item->object.oid, + &commit->object.oid, "", options); else diff_root_tree_oid(&commit->object.oid, "", options); diffcore_std(options); diff --git a/revision.c b/revision.c index 7637e75561..3030f33eed 100644 --- a/revision.c +++ b/revision.c @@ -455,7 +455,7 @@ static int rev_compare_tree(struct rev_info *revs, tree_difference = REV_TREE_SAME; DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); - if (diff_tree_sha1(t1->object.oid.hash, t2->object.oid.hash, "", + if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "", &revs->pruning) < 0) return REV_TREE_DIFFERENT; return tree_difference; @@ -471,7 +471,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit) tree_difference = REV_TREE_SAME; DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); - retval = diff_tree_sha1(NULL, t1->object.oid.hash, "", &revs->pruning); + retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning); return retval >= 0 && (tree_difference == REV_TREE_SAME); } diff --git a/sequencer.c b/sequencer.c index a23b948ac1..7a114def84 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2129,8 +2129,8 @@ cleanup_head_ref: if (read_oneliner(&buf, rebase_path_orig_head(), 0) && !get_sha1(buf.buf, orig.hash) && !get_sha1("HEAD", head.hash)) { - diff_tree_sha1(orig.hash, head.hash, - "", &log_tree_opt.diffopt); + diff_tree_oid(&orig, &head, "", + &log_tree_opt.diffopt); log_tree_diff_flush(&log_tree_opt); } } diff --git a/tree-diff.c b/tree-diff.c index f9bbaf3c47..fc020d76dc 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -419,7 +419,7 @@ static struct combine_diff_path *ll_diff_tree_paths( * load parents first, as they are probably already cached. * * ( log_tree_diff() parses commit->parent before calling here via - * diff_tree_sha1(parent, commit) ) + * diff_tree_oid(parent, commit) ) */ for (i = 0; i < nparent; ++i) tptree[i] = fill_tree_descriptor(&tp[i], parents_sha1[i]); @@ -694,7 +694,9 @@ static int ll_diff_tree_sha1(const unsigned char *old, const unsigned char *new, return 0; } -int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base_str, struct diff_options *opt) +int diff_tree_oid(const struct object_id *old_oid, + const struct object_id *new_oid, + const char *base_str, struct diff_options *opt) { struct strbuf base; int retval; @@ -702,9 +704,9 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha strbuf_init(&base, PATH_MAX); strbuf_addstr(&base, base_str); - retval = ll_diff_tree_sha1(old, new, &base, opt); + retval = ll_diff_tree_sha1(old_oid->hash, new_oid->hash, &base, opt); if (!*base_str && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) - try_to_follow_renames(old, new, &base, opt); + try_to_follow_renames(old_oid->hash, new_oid->hash, &base, opt); strbuf_release(&base); @@ -713,5 +715,5 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha int diff_root_tree_oid(const struct object_id *new_oid, const char *base, struct diff_options *opt) { - return diff_tree_sha1(NULL, new_oid->hash, base, opt); + return diff_tree_oid(NULL, new_oid, base, opt); } From 315f49f20bfbe65cecc2adfe6131588647c57ed4 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:04 -0700 Subject: [PATCH 467/566] builtin/diff-tree: cleanup references to sha1 Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/diff-tree.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index aef1676198..8b26a66a95 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -7,7 +7,7 @@ static struct rev_info log_tree_opt; -static int diff_tree_commit_sha1(const struct object_id *oid) +static int diff_tree_commit_oid(const struct object_id *oid) { struct commit *commit = lookup_commit_reference(oid); if (!commit) @@ -98,7 +98,6 @@ static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt int cmd_diff_tree(int argc, const char **argv, const char *prefix) { - int nr_sha1; char line[1000]; struct object *tree1, *tree2; static struct rev_info *opt = &log_tree_opt; @@ -132,15 +131,14 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) * reverse the order of the objects if the second one * is marked UNINTERESTING. */ - nr_sha1 = opt->pending.nr; - switch (nr_sha1) { + switch (opt->pending.nr) { case 0: if (!read_stdin) usage(diff_tree_usage); break; case 1: tree1 = opt->pending.objects[0].item; - diff_tree_commit_sha1(&tree1->oid); + diff_tree_commit_oid(&tree1->oid); break; case 2: tree1 = opt->pending.objects[0].item; From 128be8767d9a20de82f032d5a422fa2a0dadb9bb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:05 -0700 Subject: [PATCH 468/566] tree-diff: convert try_to_follow_renames to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- tree-diff.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tree-diff.c b/tree-diff.c index fc020d76dc..29e3f6144b 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -577,7 +577,9 @@ static inline int diff_might_be_rename(void) !DIFF_FILE_VALID(diff_queued_diff.queue[0]->one); } -static void try_to_follow_renames(const unsigned char *old, const unsigned char *new, struct strbuf *base, struct diff_options *opt) +static void try_to_follow_renames(const struct object_id *old_oid, + const struct object_id *new_oid, + struct strbuf *base, struct diff_options *opt) { struct diff_options diff_opts; struct diff_queue_struct *q = &diff_queued_diff; @@ -615,7 +617,7 @@ static void try_to_follow_renames(const unsigned char *old, const unsigned char diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; diff_setup_done(&diff_opts); - ll_diff_tree_sha1(old, new, base, &diff_opts); + ll_diff_tree_sha1(old_oid->hash, new_oid->hash, base, &diff_opts); diffcore_std(&diff_opts); clear_pathspec(&diff_opts.pathspec); @@ -706,7 +708,7 @@ int diff_tree_oid(const struct object_id *old_oid, retval = ll_diff_tree_sha1(old_oid->hash, new_oid->hash, &base, opt); if (!*base_str && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) - try_to_follow_renames(old_oid->hash, new_oid->hash, &base, opt); + try_to_follow_renames(old_oid, new_oid, &base, opt); strbuf_release(&base); From fda94b416ed07988e645cec22bf61742d4dd5c95 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:06 -0700 Subject: [PATCH 469/566] tree-diff: convert diff_tree_paths to struct object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- combine-diff.c | 10 ++++---- diff.h | 4 ++-- tree-diff.c | 63 +++++++++++++++++++++++++------------------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/combine-diff.c b/combine-diff.c index 04c4ae8564..ec9d930440 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1364,22 +1364,22 @@ static struct combine_diff_path *find_paths_multitree( struct diff_options *opt) { int i, nparent = parents->nr; - const unsigned char **parents_sha1; + const struct object_id **parents_oid; struct combine_diff_path paths_head; struct strbuf base; - ALLOC_ARRAY(parents_sha1, nparent); + ALLOC_ARRAY(parents_oid, nparent); for (i = 0; i < nparent; i++) - parents_sha1[i] = parents->oid[i].hash; + parents_oid[i] = &parents->oid[i]; /* fake list head, so worker can assume it is non-NULL */ paths_head.next = NULL; strbuf_init(&base, PATH_MAX); - diff_tree_paths(&paths_head, oid->hash, parents_sha1, nparent, &base, opt); + diff_tree_paths(&paths_head, oid, parents_oid, nparent, &base, opt); strbuf_release(&base); - free(parents_sha1); + free(parents_oid); return paths_head.next; } diff --git a/diff.h b/diff.h index e0b5034603..0d0c14f285 100644 --- a/diff.h +++ b/diff.h @@ -210,8 +210,8 @@ const char *diff_line_prefix(struct diff_options *); extern const char mime_boundary_leader[]; extern struct combine_diff_path *diff_tree_paths( - struct combine_diff_path *p, const unsigned char *sha1, - const unsigned char **parent_sha1, int nparent, + struct combine_diff_path *p, const struct object_id *oid, + const struct object_id **parents_oid, int nparent, struct strbuf *base, struct diff_options *opt); extern int diff_tree_oid(const struct object_id *old_oid, const struct object_id *new_oid, diff --git a/tree-diff.c b/tree-diff.c index 29e3f6144b..6a960f569c 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -26,11 +26,12 @@ } while(0) static struct combine_diff_path *ll_diff_tree_paths( - struct combine_diff_path *p, const unsigned char *sha1, - const unsigned char **parents_sha1, int nparent, + struct combine_diff_path *p, const struct object_id *oid, + const struct object_id **parents_oid, int nparent, struct strbuf *base, struct diff_options *opt); -static int ll_diff_tree_sha1(const unsigned char *old, const unsigned char *new, - struct strbuf *base, struct diff_options *opt); +static int ll_diff_tree_oid(const struct object_id *old_oid, + const struct object_id *new_oid, + struct strbuf *base, struct diff_options *opt); /* * Compare two tree entries, taking into account only path/S_ISDIR(mode), @@ -183,7 +184,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, { unsigned mode; const char *path; - const unsigned char *sha1; + const struct object_id *oid; int pathlen; int old_baselen = base->len; int i, isdir, recurse = 0, emitthis = 1; @@ -193,7 +194,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, if (t) { /* path present in resulting tree */ - sha1 = tree_entry_extract(t, &path, &mode)->hash; + oid = tree_entry_extract(t, &path, &mode); pathlen = tree_entry_len(&t->entry); isdir = S_ISDIR(mode); } else { @@ -208,7 +209,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, pathlen = tree_entry_len(&tp[imin].entry); isdir = S_ISDIR(mode); - sha1 = NULL; + oid = NULL; mode = 0; } @@ -220,7 +221,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, if (emitthis) { int keep; struct combine_diff_path *pprev = p; - p = path_appendnew(p, nparent, base, path, pathlen, mode, sha1); + p = path_appendnew(p, nparent, base, path, pathlen, mode, oid ? oid->hash : NULL); for (i = 0; i < nparent; ++i) { /* @@ -229,7 +230,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, */ int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ); - const unsigned char *sha1_i; + const struct object_id *oid_i; unsigned mode_i; p->parent[i].status = @@ -239,16 +240,16 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, DIFF_STATUS_ADDED; if (tpi_valid) { - sha1_i = tp[i].entry.oid->hash; + oid_i = tp[i].entry.oid; mode_i = tp[i].entry.mode; } else { - sha1_i = NULL; + oid_i = &null_oid; mode_i = 0; } p->parent[i].mode = mode_i; - hashcpy(p->parent[i].oid.hash, sha1_i ? sha1_i : null_sha1); + oidcpy(&p->parent[i].oid, oid_i); } keep = 1; @@ -273,21 +274,20 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, } if (recurse) { - const unsigned char **parents_sha1; + const struct object_id **parents_oid; - FAST_ARRAY_ALLOC(parents_sha1, nparent); + FAST_ARRAY_ALLOC(parents_oid, nparent); for (i = 0; i < nparent; ++i) { /* same rule as in emitthis */ int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ); - parents_sha1[i] = tpi_valid ? tp[i].entry.oid->hash - : NULL; + parents_oid[i] = tpi_valid ? tp[i].entry.oid : NULL; } strbuf_add(base, path, pathlen); strbuf_addch(base, '/'); - p = ll_diff_tree_paths(p, sha1, parents_sha1, nparent, base, opt); - FAST_ARRAY_FREE(parents_sha1, nparent); + p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt); + FAST_ARRAY_FREE(parents_oid, nparent); } strbuf_setlen(base, old_baselen); @@ -312,7 +312,7 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base, /* - * generate paths for combined diff D(sha1,parents_sha1[]) + * generate paths for combined diff D(sha1,parents_oid[]) * * Resulting paths are appended to combine_diff_path linked list, and also, are * emitted on the go via opt->pathchange() callback, so it is possible to @@ -404,8 +404,8 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent) } static struct combine_diff_path *ll_diff_tree_paths( - struct combine_diff_path *p, const unsigned char *sha1, - const unsigned char **parents_sha1, int nparent, + struct combine_diff_path *p, const struct object_id *oid, + const struct object_id **parents_oid, int nparent, struct strbuf *base, struct diff_options *opt) { struct tree_desc t, *tp; @@ -422,8 +422,8 @@ static struct combine_diff_path *ll_diff_tree_paths( * diff_tree_oid(parent, commit) ) */ for (i = 0; i < nparent; ++i) - tptree[i] = fill_tree_descriptor(&tp[i], parents_sha1[i]); - ttree = fill_tree_descriptor(&t, sha1); + tptree[i] = fill_tree_descriptor(&tp[i], parents_oid[i]->hash); + ttree = fill_tree_descriptor(&t, oid->hash); /* Enable recursion indefinitely */ opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE); @@ -548,11 +548,11 @@ static struct combine_diff_path *ll_diff_tree_paths( } struct combine_diff_path *diff_tree_paths( - struct combine_diff_path *p, const unsigned char *sha1, - const unsigned char **parents_sha1, int nparent, + struct combine_diff_path *p, const struct object_id *oid, + const struct object_id **parents_oid, int nparent, struct strbuf *base, struct diff_options *opt) { - p = ll_diff_tree_paths(p, sha1, parents_sha1, nparent, base, opt); + p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt); /* * free pre-allocated last element, if any @@ -617,7 +617,7 @@ static void try_to_follow_renames(const struct object_id *old_oid, diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; diff_setup_done(&diff_opts); - ll_diff_tree_sha1(old_oid->hash, new_oid->hash, base, &diff_opts); + ll_diff_tree_oid(old_oid, new_oid, base, &diff_opts); diffcore_std(&diff_opts); clear_pathspec(&diff_opts.pathspec); @@ -676,15 +676,16 @@ static void try_to_follow_renames(const struct object_id *old_oid, q->nr = 1; } -static int ll_diff_tree_sha1(const unsigned char *old, const unsigned char *new, - struct strbuf *base, struct diff_options *opt) +static int ll_diff_tree_oid(const struct object_id *old_oid, + const struct object_id *new_oid, + struct strbuf *base, struct diff_options *opt) { struct combine_diff_path phead, *p; pathchange_fn_t pathchange_old = opt->pathchange; phead.next = NULL; opt->pathchange = emit_diff_first_parent_only; - diff_tree_paths(&phead, new, &old, 1, base, opt); + diff_tree_paths(&phead, new_oid, &old_oid, 1, base, opt); for (p = phead.next; p;) { struct combine_diff_path *pprev = p; @@ -706,7 +707,7 @@ int diff_tree_oid(const struct object_id *old_oid, strbuf_init(&base, PATH_MAX); strbuf_addstr(&base, base_str); - retval = ll_diff_tree_sha1(old_oid->hash, new_oid->hash, &base, opt); + retval = ll_diff_tree_oid(old_oid, new_oid, &base, opt); if (!*base_str && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) try_to_follow_renames(old_oid, new_oid, &base, opt); From 0e72462fb403110461faa1b06369b5c989ded381 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:07 -0700 Subject: [PATCH 470/566] tree-diff: convert path_appendnew to object_id Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- tree-diff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tree-diff.c b/tree-diff.c index 6a960f569c..467e381724 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -132,7 +132,7 @@ static int emit_diff_first_parent_only(struct diff_options *opt, struct combine_ */ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last, int nparent, const struct strbuf *base, const char *path, int pathlen, - unsigned mode, const unsigned char *sha1) + unsigned mode, const struct object_id *oid) { struct combine_diff_path *p; size_t len = st_add(base->len, pathlen); @@ -162,7 +162,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last, memcpy(p->path + base->len, path, pathlen); p->path[len] = 0; p->mode = mode; - hashcpy(p->oid.hash, sha1 ? sha1 : null_sha1); + oidcpy(&p->oid, oid ? oid : &null_oid); return p; } @@ -221,7 +221,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, if (emitthis) { int keep; struct combine_diff_path *pprev = p; - p = path_appendnew(p, nparent, base, path, pathlen, mode, oid ? oid->hash : NULL); + p = path_appendnew(p, nparent, base, path, pathlen, mode, oid); for (i = 0; i < nparent; ++i) { /* From 02491b67f3e4218176f3e6fe25961f1a8afade50 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:08 -0700 Subject: [PATCH 471/566] diffcore-rename: use is_empty_blob_oid Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diffcore-rename.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index 3d9719dadc..03d1e8d40b 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -464,7 +464,7 @@ void diffcore_rename(struct diff_options *options) strcmp(options->single_follow, p->two->path)) continue; /* not interested */ else if (!DIFF_OPT_TST(options, RENAME_EMPTY) && - is_empty_blob_sha1(p->two->oid.hash)) + is_empty_blob_oid(&p->two->oid)) continue; else if (add_rename_dst(p->two) < 0) { warning("skipping rename detection, detected" @@ -474,7 +474,7 @@ void diffcore_rename(struct diff_options *options) } } else if (!DIFF_OPT_TST(options, RENAME_EMPTY) && - is_empty_blob_sha1(p->one->oid.hash)) + is_empty_blob_oid(&p->one->oid)) continue; else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) { /* From 94e327e973d320abf91c22307b87292911643c3b Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 May 2017 10:31:09 -0700 Subject: [PATCH 472/566] diff: rename diff_fill_sha1_info to diff_fill_oid_info Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- diff.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/diff.c b/diff.c index dd325e6160..c758a0d73e 100644 --- a/diff.c +++ b/diff.c @@ -3239,7 +3239,7 @@ static void run_diff_cmd(const char *pgm, fprintf(o->file, "* Unmerged path %s\n", name); } -static void diff_fill_sha1_info(struct diff_filespec *one) +static void diff_fill_oid_info(struct diff_filespec *one) { if (DIFF_FILE_VALID(one)) { if (!one->oid_valid) { @@ -3298,8 +3298,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) return; } - diff_fill_sha1_info(one); - diff_fill_sha1_info(two); + diff_fill_oid_info(one); + diff_fill_oid_info(two); if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && @@ -3344,8 +3344,8 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, if (o->prefix_length) strip_prefix(o->prefix_length, &name, &other); - diff_fill_sha1_info(p->one); - diff_fill_sha1_info(p->two); + diff_fill_oid_info(p->one); + diff_fill_oid_info(p->two); builtin_diffstat(name, other, p->one, p->two, diffstat, o, p); } @@ -3368,8 +3368,8 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) if (o->prefix_length) strip_prefix(o->prefix_length, &name, &other); - diff_fill_sha1_info(p->one); - diff_fill_sha1_info(p->two); + diff_fill_oid_info(p->one); + diff_fill_oid_info(p->two); builtin_checkdiff(name, other, attr_path, p->one, p->two, o); } @@ -4616,8 +4616,8 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid if (DIFF_PAIR_UNMERGED(p)) continue; - diff_fill_sha1_info(p->one); - diff_fill_sha1_info(p->two); + diff_fill_oid_info(p->one); + diff_fill_oid_info(p->two); len1 = remove_space(p->one->path, strlen(p->one->path)); len2 = remove_space(p->two->path, strlen(p->two->path)); From 5a88f97cff0f269f8b2069b17c9da05ee66c98bb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 1 Jun 2017 13:38:16 +0900 Subject: [PATCH 473/566] diff- and log- family: handle "git cmd -h" early "git $builtin -h" bypasses the usual repository setup and calls the cmd_$builtin() function, expecting it to show the help text. Unfortunately the commands in the log- and the diff- family want to call into the revisions machinery, which by definition needs to have a repository already discovered. Strictly speaking, they may not need a repository only for parsing "-h", but it is a good discipline to future-proof codepath to ensure that setup_revisions() is called after we know that a repository is there. Handle the "git $builtin -h" special case very early in these commands to work around potential issues. Signed-off-by: Junio C Hamano --- builtin/diff-files.c | 3 +++ builtin/diff-index.c | 3 +++ builtin/diff-tree.c | 3 +++ builtin/rev-list.c | 3 +++ 4 files changed, 12 insertions(+) diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 15c61fd8d1..6be1df684a 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -20,6 +20,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) int result; unsigned options = 0; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage(diff_files_usage); + init_revisions(&rev, prefix); gitmodules_config(); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 1af373d002..02dd35ba45 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -17,6 +17,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) int i; int result; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage(diff_cache_usage); + init_revisions(&rev, prefix); gitmodules_config(); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 326f88b657..773cc254b5 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -105,6 +105,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) struct setup_revision_opt s_r_opt; int read_stdin = 0; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage(diff_tree_usage); + init_revisions(opt, prefix); gitmodules_config(); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ diff --git a/builtin/rev-list.c b/builtin/rev-list.c index bcf77f0b8a..2951e0efd7 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -277,6 +277,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int use_bitmap_index = 0; const char *show_progress = NULL; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage(rev_list_usage); + git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = DEFAULT_ABBREV; From b48cbfc5e6112952bc3be4dea0208bc5e1f331eb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:17:42 -0400 Subject: [PATCH 474/566] version: convert to parse-options The "git version" command didn't traditionally accept any options, and in fact ignores any you give it. When we added simple option parsing for "--build-options" in 6b9c38e14, we didn't improve this; we just loop over the arguments and pick out the one we recognize. Instead, let's move to a real parsing loop, complain about nonsense options, and recognize conventions like "-h". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- help.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/help.c b/help.c index bc6cd19cf3..1064363cd5 100644 --- a/help.c +++ b/help.c @@ -8,6 +8,7 @@ #include "column.h" #include "version.h" #include "refs.h" +#include "parse-options.h" void add_cmdname(struct cmdnames *cmds, const char *name, int len) { @@ -424,16 +425,30 @@ const char *help_unknown_cmd(const char *cmd) int cmd_version(int argc, const char **argv, const char *prefix) { + int build_options = 0; + const char * const usage[] = { + N_("git version []"), + NULL + }; + struct option options[] = { + OPT_BOOL(0, "build-options", &build_options, + "also print build options"), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, usage, 0); + /* * The format of this string should be kept stable for compatibility * with external projects that rely on the output of "git version". + * + * Always show the version, even if other options are given. */ printf("git version %s\n", git_version_string); - while (*++argv) { - if (!strcmp(*argv, "--build-options")) { - printf("sizeof-long: %d\n", (int)sizeof(long)); - /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */ - } + + if (build_options) { + printf("sizeof-long: %d\n", (int)sizeof(long)); + /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */ } return 0; } From 8893fd95b66cbd6566136a289dd05fcf4e547281 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:18:43 -0400 Subject: [PATCH 475/566] git: add hidden --list-builtins option It can be useful in the test suite to be able to iterate over the list of builtins. We could do this with some Makefile magic. But since the authoritative list is in the commands array inside git.c, and since this could also be handy for debugging, let's add a hidden command-line option to dump that list. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/git.c b/git.c index 8ff44f081d..1b8b7f51a6 100644 --- a/git.c +++ b/git.c @@ -26,6 +26,8 @@ static const char *env_names[] = { static char *orig_env[4]; static int save_restore_env_balance; +static void list_builtins(void); + static void save_env_before_alias(void) { int i; @@ -232,6 +234,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) } (*argv)++; (*argc)--; + } else if (!strcmp(cmd, "--list-builtins")) { + list_builtins(); + exit(0); } else { fprintf(stderr, "Unknown option: %s\n", cmd); usage(git_usage_string); @@ -529,6 +534,13 @@ int is_builtin(const char *s) return !!get_builtin(s); } +static void list_builtins(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(commands); i++) + printf("%s\n", commands[i].cmd); +} + #ifdef STRIP_EXTENSION static void strip_extension(const char **argv) { From d691551192ac845747694258ccae9ffeeb6bdd58 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 30 May 2017 01:19:30 -0400 Subject: [PATCH 476/566] t0012: test "-h" with builtins Since commit 99caeed05 (Let 'git -h' show usage without a git dir, 2009-11-09), the git wrapper handles "-h" specially, skipping any repository setup but still calling the builtin's cmd_foo() function. This means that every cmd_foo() must be ready to handle this case, but we don't have any systematic tests. This led to "git am -h" being broken for some time without anybody noticing. This patch just tests that "git foo -h" works for every builtin, where we see a 129 exit code (the normal code for our usage() helper), and that the word "usage" appears in the output. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t0012-help.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/t/t0012-help.sh b/t/t0012-help.sh index 8faba2e8bc..487b92a5de 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -49,4 +49,16 @@ test_expect_success "--help does not work for guides" " test_i18ncmp expect actual " +test_expect_success 'generate builtin list' ' + git --list-builtins >builtins +' + +while read builtin +do + test_expect_success "$builtin can handle -h" ' + test_expect_code 129 git $builtin -h >output 2>&1 && + test_i18ngrep usage output + ' +done Date: Mon, 5 Jun 2017 15:15:25 -0400 Subject: [PATCH 477/566] t5313: make extended-table test more deterministic Commit a1283866b (t5313: test bounds-checks of corrupted/malicious pack/idx files, 2016-02-25) added a test that requires our corrupted pack index to have two objects. The entry for the first one remains untouched, but we corrupt the entry for second one. Since the index stores the entries in sha1-sorted order, this means that the test must make sure that the sha1 of the object we expect to be corrupted ("$object") sorts after the other placeholder object. That commit used the HEAD commit as the placeholder, but the script never calls test_tick. That means that the commit object (and thus its sha1) depends on the timestamp when the test script is run. This usually works in practice, because the sha1 of $object starts with "fff". The commit object will sort after that only 1 in 4096 times, but when it does the test will fail. One obvious solution is to add the test_tick call to get a deterministic commit sha1. But since we're relying on the sort order for the test to function, let's make that very explicit by just generating a second blob with a known sha1. Reported-by: Lars Schneider Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t5313-pack-bounds-checks.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh index a8a587abc3..9372508c99 100755 --- a/t/t5313-pack-bounds-checks.sh +++ b/t/t5313-pack-bounds-checks.sh @@ -139,7 +139,13 @@ test_expect_success 'bogus offset into v2 extended table' ' test_expect_success 'bogus offset inside v2 extended table' ' # We need two objects here, so we can plausibly require # an extended table (if the first object were larger than 2^31). - do_pack "$object $(git rev-parse HEAD)" --index-version=2 && + # + # Note that the value is important here. We want $object as + # the second entry in sorted-sha1 order. The sha1 of 1485 starts + # with "000", which sorts before that of $object (which starts + # with "fff"). + second=$(echo 1485 | git hash-object -w --stdin) && + do_pack "$object $second" --index-version=2 && # We have to make extra room for the table, so we cannot # just munge in place as usual. From 5d184f468e59cd9c5b51b5c139540015c8e41b18 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 6 Jun 2017 15:01:11 -0400 Subject: [PATCH 478/566] sha1dc: ignore indent-with-non-tab whitespace violations The upstream sha1dc code indents some lines with spaces. While this doesn't match Git's coding guidelines, it's better to leave this imported code untouched than to try to make it match our style. However, we can use .gitattributes to tell "diff --check" and "git am" not to bother us about it. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- sha1dc/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 sha1dc/.gitattributes diff --git a/sha1dc/.gitattributes b/sha1dc/.gitattributes new file mode 100644 index 0000000000..da53f40548 --- /dev/null +++ b/sha1dc/.gitattributes @@ -0,0 +1 @@ +* whitespace=-indent-with-non-tab From 6b851e536b05e0c8c61f77b9e4c3e7cedea39ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Tue, 6 Jun 2017 15:12:29 +0000 Subject: [PATCH 479/566] sha1dc: update from upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update sha1dc from the latest version by the upstream maintainer[1]. See commit a0103914c2 ("sha1dc: update from upstream", 2017-05-20) for the latest update. That update was done sans some whitespace changes by upstream, which is why the diff here isn't the same as the upstream cc46554..e139984. It also brings in a change[2] upstream made which should hopefully address the breakage in 2.13.1 on Cygwin, see [3]. Cygwin defines both _BIG_ENDIAN and _LITTLE_ENDIAN. Adam Dinwoodie reports on the mailing list that that upstream commit fixes the issue on Cygwin[4]. 1. https://github.com/cr-marcstevens/sha1collisiondetection/commit/e1399840b501a68ac6c8d7ed9a5cb1455480200e 2. https://github.com/cr-marcstevens/sha1collisiondetection/commit/a24eef58c0684078405f8c7a89f9b78271432005 3. <20170606100355.GC25777@dinwoodie.org> (https://public-inbox.org/git/20170606100355.GC25777@dinwoodie.org/) 4. <20170606124323.GD25777@dinwoodie.org> (https://public-inbox.org/git/20170606124323.GD25777@dinwoodie.org/) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- sha1dc/sha1.c | 30 ++++++++++++++++++++++++------ sha1dc/sha1.h | 6 +++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c index 3dff80ac72..facea1bb56 100644 --- a/sha1dc/sha1.c +++ b/sha1dc/sha1.c @@ -35,15 +35,33 @@ #ifdef SHA1DC_BIGENDIAN #undef SHA1DC_BIGENDIAN #endif -#if (!defined SHA1DC_FORCE_LITTLEENDIAN) && \ - ((defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ - (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) || \ - defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ - defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) || defined(SHA1DC_FORCE_BIGENDIAN)) +#if (defined(_BYTE_ORDER) || defined(__BYTE_ORDER) || defined(__BYTE_ORDER__)) + +#if ((defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \ + (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) ) #define SHA1DC_BIGENDIAN +#endif -#endif /*ENDIANNESS SELECTION*/ +#else + +#if (defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \ + defined(__sparc)) +#define SHA1DC_BIGENDIAN +#endif + +#endif + +#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN)) +#undef SHA1DC_BIGENDIAN +#endif +#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN)) +#define SHA1DC_BIGENDIAN +#endif +/*ENDIANNESS SELECTION*/ #if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \ defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \ diff --git a/sha1dc/sha1.h b/sha1dc/sha1.h index a0ff5d1305..1e4e94be54 100644 --- a/sha1dc/sha1.h +++ b/sha1dc/sha1.h @@ -61,9 +61,9 @@ void SHA1DCInit(SHA1_CTX*); Function to enable safe SHA-1 hashing: Collision attacks are thwarted by hashing a detected near-collision block 3 times. Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks: - The best collision attacks against SHA-1 have complexity about 2^60, - thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. - An attacker would be better off using a generic birthday search of complexity 2^80. + The best collision attacks against SHA-1 have complexity about 2^60, + thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180. + An attacker would be better off using a generic birthday search of complexity 2^80. Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected, but it will result in a different SHA-1 hash for messages where a collision attack was detected. From 16fbca07e295358406dfad88c4a9721f57357cbc Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Wed, 7 Jun 2017 20:32:14 +0200 Subject: [PATCH 480/566] doc: describe git svn init --ignore-refs Add the missing documentation for `git svn init --ignore-refs`. Signed-off-by: Andreas Heiduk Signed-off-by: Eric Wong --- Documentation/git-svn.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 9bee9b0c4c..fba0b4eece 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -95,6 +95,10 @@ If you still want the old default, you can get it by passing `--prefix ""` on the command line (`--prefix=""` may not work if your Perl's Getopt::Long is < v2.37). +--ignore-refs=;; + When passed to 'init' or 'clone' this regular expression will + be preserved as a config key. See 'fetch' for a description + of `--ignore-refs`. --ignore-paths=;; When passed to 'init' or 'clone' this regular expression will be preserved as a config key. See 'fetch' for a description @@ -138,6 +142,18 @@ the same local time zone. --parent;; Fetch only from the SVN parent of the current HEAD. +--ignore-refs=;; + Ignore refs for branches or tags matching the Perl regular + expression. A "negative look-ahead assertion" like + `^refs/remotes/origin/(?!tags/wanted-tag|wanted-branch).*$` + can be used to allow only certain refs. ++ +[verse] +config key: svn-remote..ignore-refs ++ +If the ignore-refs configuration key is set, and the command-line +option is also given, both regular expressions will be used. + --ignore-paths=;; This allows one to specify a Perl regular expression that will cause skipping of all matching paths from checkout from SVN. From 68241cb9dd40c546aa32e9bbe0606c67a38b484d Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 6 Jun 2017 19:18:05 -0700 Subject: [PATCH 481/566] t4005: modernize style and drop hard coded sha1 Use modern style in the test t4005. Remove hard coded sha1 values. Combine test prep work and the actual test. Rename the first test to contain the word "setup". Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- t/t4005-diff-rename-2.sh | 95 ++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 52 deletions(-) diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh index 135addbfbd..f542d2929d 100755 --- a/t/t4005-diff-rename-2.sh +++ b/t/t4005-diff-rename-2.sh @@ -3,84 +3,75 @@ # Copyright (c) 2005 Junio C Hamano # -test_description='Same rename detection as t4003 but testing diff-raw. +test_description='Same rename detection as t4003 but testing diff-raw.' -' . ./test-lib.sh . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash -test_expect_success \ - 'prepare reference tree' \ - 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING && - echo frotz >rezrov && - git update-index --add COPYING rezrov && - tree=$(git write-tree) && - echo $tree' - -test_expect_success \ - 'prepare work tree' \ - 'sed -e 's/HOWEVER/However/' COPYING.1 && - sed -e 's/GPL/G.P.L/g' COPYING.2 && - rm -f COPYING && - git update-index --add --remove COPYING COPYING.?' +test_expect_success 'setup reference tree' ' + cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING && + echo frotz >rezrov && + git update-index --add COPYING rezrov && + tree=$(git write-tree) && + echo $tree && + sed -e 's/HOWEVER/However/' COPYING.1 && + sed -e 's/GPL/G.P.L/g' COPYING.2 && + origoid=$(git hash-object COPYING) && + oid1=$(git hash-object COPYING.1) && + oid2=$(git hash-object COPYING.2) +' +################################################################ # tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2, # both are slightly edited, and unchanged rezrov. We say COPYING.1 # and COPYING.2 are based on COPYING, and do not say anything about # rezrov. -git diff-index -C $tree >current +test_expect_success 'validate output from rename/copy detection (#1)' ' + rm -f COPYING && + git update-index --add --remove COPYING COPYING.? && -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1 -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234 COPYING COPYING.2 -EOF - -test_expect_success \ - 'validate output from rename/copy detection (#1)' \ - 'compare_diff_raw current expected' + cat <<-EOF >expected && + :100644 100644 $origoid $oid1 C1234 COPYING COPYING.1 + :100644 100644 $origoid $oid2 R1234 COPYING COPYING.2 + EOF + git diff-index -C $tree >current && + compare_diff_raw expected current +' ################################################################ - -test_expect_success \ - 'prepare work tree again' \ - 'mv COPYING.2 COPYING && - git update-index --add --remove COPYING COPYING.1 COPYING.2' - # tree has COPYING and rezrov. work tree has COPYING and COPYING.1, # both are slightly edited, and unchanged rezrov. We say COPYING.1 # is based on COPYING and COPYING is still there, and do not say anything # about rezrov. -git diff-index -C $tree >current -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M COPYING -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1 -EOF +test_expect_success 'validate output from rename/copy detection (#2)' ' + mv COPYING.2 COPYING && + git update-index --add --remove COPYING COPYING.1 COPYING.2 && -test_expect_success \ - 'validate output from rename/copy detection (#2)' \ - 'compare_diff_raw current expected' + cat <<-EOF >expected && + :100644 100644 $origoid $oid2 M COPYING + :100644 100644 $origoid $oid1 C1234 COPYING COPYING.1 + EOF + git diff-index -C $tree >current && + compare_diff_raw current expected +' ################################################################ - # tree has COPYING and rezrov. work tree has the same COPYING and # copy-edited COPYING.1, and unchanged rezrov. We should not say # anything about rezrov or COPYING, since the revised again diff-raw # nows how to say Copy. -test_expect_success \ - 'prepare work tree once again' \ - 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING && - git update-index --add --remove COPYING COPYING.1' +test_expect_success 'validate output from rename/copy detection (#3)' ' + cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING && + git update-index --add --remove COPYING COPYING.1 && -git diff-index -C --find-copies-harder $tree >current -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1 -EOF - -test_expect_success \ - 'validate output from rename/copy detection (#3)' \ - 'compare_diff_raw current expected' + cat <<-EOF >expected && + :100644 100644 $origoid $oid1 C1234 COPYING COPYING.1 + EOF + git diff-index -C --find-copies-harder $tree >current && + compare_diff_raw current expected +' test_done From ae52d57f0b45b2db93f13c009b7a31108d5d6525 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Wed, 31 May 2017 03:26:10 -0700 Subject: [PATCH 482/566] t5100: add some more mailinfo tests Add some more simple mailinfo tests including a few that produce: fatal: `pos + len' is too far after the end of the buffer Mark those as 'test_expect_failure'. Signed-off-by: Kyle J. McKay Signed-off-by: Junio C Hamano --- t/t5100-mailinfo.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index 45d228ebc8..d19cd8c2aa 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -158,4 +158,46 @@ test_expect_success 'mailinfo handles rfc2822 comment' ' test_cmp "$DATA/comment.expect" comment/info ' +test_expect_success 'mailinfo no options' ' + subj="$(echo "Subject: [PATCH] [other] [PATCH] message" | + git mailinfo /dev/null /dev/null)" && + test z"$subj" = z"Subject: message" +' + +test_expect_success 'mailinfo -k' ' + subj="$(echo "Subject: [PATCH] [other] [PATCH] message" | + git mailinfo -k /dev/null /dev/null)" && + test z"$subj" = z"Subject: [PATCH] [other] [PATCH] message" +' + +test_expect_success 'mailinfo -b no [PATCH]' ' + subj="$(echo "Subject: [other] message" | + git mailinfo -b /dev/null /dev/null)" && + test z"$subj" = z"Subject: [other] message" +' + +test_expect_success 'mailinfo -b leading [PATCH]' ' + subj="$(echo "Subject: [PATCH] [other] message" | + git mailinfo -b /dev/null /dev/null)" && + test z"$subj" = z"Subject: [other] message" +' + +test_expect_success 'mailinfo -b double [PATCH]' ' + subj="$(echo "Subject: [PATCH] [PATCH] message" | + git mailinfo -b /dev/null /dev/null)" && + test z"$subj" = z"Subject: message" +' + +test_expect_failure 'mailinfo -b trailing [PATCH]' ' + subj="$(echo "Subject: [other] [PATCH] message" | + git mailinfo -b /dev/null /dev/null)" && + test z"$subj" = z"Subject: [other] message" +' + +test_expect_failure 'mailinfo -b separated double [PATCH]' ' + subj="$(echo "Subject: [PATCH] [other] [PATCH] message" | + git mailinfo -b /dev/null /dev/null)" && + test z"$subj" = z"Subject: [other] message" +' + test_done From 3b117f730185ec8acdc8f1b680b9f0a9b647fc0f Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Sat, 10 Jun 2017 10:54:44 +0200 Subject: [PATCH 483/566] filter-branch: add `--setup` step A `--setup` step in `git filter-branch` makes it much easier to define the initial values of variables used in the real filters. Also sourcing/defining utility functions here instead of `--env-filter` improves performance and minimizes clogging the output in case of errors. Signed-off-by: Andreas Heiduk Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 17 ++++++++++++----- git-filter-branch.sh | 18 +++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 7b695dbb72..9e5169aa64 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -8,11 +8,11 @@ git-filter-branch - Rewrite branches SYNOPSIS -------- [verse] -'git filter-branch' [--env-filter ] [--tree-filter ] - [--index-filter ] [--parent-filter ] - [--msg-filter ] [--commit-filter ] - [--tag-name-filter ] [--subdirectory-filter ] - [--prune-empty] +'git filter-branch' [--setup ] [--env-filter ] + [--tree-filter ] [--index-filter ] + [--parent-filter ] [--msg-filter ] + [--commit-filter ] [--tag-name-filter ] + [--subdirectory-filter ] [--prune-empty] [--original ] [-d ] [-f | --force] [--] [...] @@ -82,6 +82,13 @@ multiple commits. OPTIONS ------- +--setup :: + This is not a real filter executed for each commit but a one + time setup just before the loop. Therefore no commit-specific + variables are defined yet. Functions or variables defined here + can be used or modified in the following filter steps except + the commit filter, for technical reasons. + --env-filter :: This filter may be used if you only need to modify the environment in which the commit will be performed. Specifically, you might diff --git a/git-filter-branch.sh b/git-filter-branch.sh index aafaf708da..2758ae5ebf 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -81,11 +81,12 @@ set_ident () { finish_ident COMMITTER } -USAGE="[--env-filter ] [--tree-filter ] - [--index-filter ] [--parent-filter ] - [--msg-filter ] [--commit-filter ] - [--tag-name-filter ] [--subdirectory-filter ] - [--original ] [-d ] [-f | --force] +USAGE="[--setup ] [--env-filter ] + [--tree-filter ] [--index-filter ] + [--parent-filter ] [--msg-filter ] + [--commit-filter ] [--tag-name-filter ] + [--subdirectory-filter ] [--original ] + [-d ] [-f | --force] [...]" OPTIONS_SPEC= @@ -96,6 +97,7 @@ if [ "$(is_bare_repository)" = false ]; then fi tempdir=.git-rewrite +filter_setup= filter_env= filter_tree= filter_index= @@ -148,6 +150,9 @@ do -d) tempdir="$OPTARG" ;; + --setup) + filter_setup="$OPTARG" + ;; --env-filter) filter_env="$OPTARG" ;; @@ -317,6 +322,9 @@ else need_index= fi +eval "$filter_setup" < /dev/null || + die "filter setup failed: $filter_setup" + while read commit parents; do git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1)) From d612975e8e15800cb4e2d1250589fb9a697aa65c Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Sat, 10 Jun 2017 10:54:45 +0200 Subject: [PATCH 484/566] filter-branch: add [--] to usage Signed-off-by: Andreas Heiduk Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 2758ae5ebf..3a74602ef3 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -87,7 +87,7 @@ USAGE="[--setup ] [--env-filter ] [--commit-filter ] [--tag-name-filter ] [--subdirectory-filter ] [--original ] [-d ] [-f | --force] - [...]" + [--] [...]" OPTIONS_SPEC= . git-sh-setup From 4f2220e606a52e891915540523d5d03aaa2dbc8f Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 8 Jun 2017 14:34:36 +0900 Subject: [PATCH 485/566] fast-import: increase the default pack depth to 50 In 618e613a70, 10 years ago, the default for pack depth used for git-pack-objects and git-repack was changed from 10 to 50, while leaving fast-import's default to 10. There doesn't seem to be a reason besides oversight for the change not having happened in fast-import as well. Interestingly, fast-import uses pack.depth when it's set, and the git-config manual says the default for pack.depth is 50. While the git-fast-import manual does say the default depth is 10, the inconsistency is also confusing. Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- Documentation/git-fast-import.txt | 2 +- fast-import.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 2b762654bf..3d3d219e58 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -121,7 +121,7 @@ Performance and Compression Tuning --depth=:: Maximum delta depth, for blob and tree deltification. - Default is 10. + Default is 50. --export-pack-edges=:: After creating a packfile, print a line of data to diff --git a/fast-import.c b/fast-import.c index e69d219682..05c73858b0 100644 --- a/fast-import.c +++ b/fast-import.c @@ -280,7 +280,7 @@ struct recent_command { }; /* Configured limits on output */ -static unsigned long max_depth = 10; +static unsigned long max_depth = 50; static off_t max_packsize; static int unpack_limit = 100; static int force_update; From fed6ebebf132eb32478a5cbbebba3760c943970a Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 12 Jun 2017 10:06:13 +0200 Subject: [PATCH 486/566] lock_packed_refs(): fix cache validity check Commit 28ed9830b1 (get_packed_ref_cache(): assume "packed-refs" won't change while locked, 2017-05-22) assumes that the "packed-refs" file cannot change while we hold the lock. That assumption is justified *if* the lock has been held the whole time since the "packed-refs" file was last read. But in `lock_packed_refs()`, we ourselves lock the "packed-refs" file and then call `get_packed_ref_cache()` to ensure that the cache agrees with the file. The intent is to guard against the possibility that another process changed the "packed-refs" file the moment before we locked it. This check was defeated because `get_packed_ref_cache()` saw that the file was locked, and therefore didn't do the `stat_validity_check()` that we want. The mistake was compounded with a misleading comment in `lock_packed_refs()` claiming that it was doing the right thing. That comment came from an earlier draft of the mh/packed-ref-store-prep patch series when the commits were in a different order. So instead: * Extract a function `validate_packed_ref_cache()` that does the validity check independent of whether the lock is held. * Change `get_packed_ref_cache()` to call the new function, but only if the lock *isn't* held. * Change `lock_packed_refs()` to call the new function in any case before calling `get_packed_ref_cache()`. * Fix the comment in `lock_packed_refs()`. Signed-off-by: Michael Haggerty Signed-off-by: Junio C Hamano --- refs/files-backend.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index d8b3f73147..b040bb3b0a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -369,6 +369,18 @@ static void files_ref_path(struct files_ref_store *refs, } } +/* + * Check that the packed refs cache (if any) still reflects the + * contents of the file. If not, clear the cache. + */ +static void validate_packed_ref_cache(struct files_ref_store *refs) +{ + if (refs->packed && + !stat_validity_check(&refs->packed->validity, + files_packed_refs_path(refs))) + clear_packed_ref_cache(refs); +} + /* * Get the packed_ref_cache for the specified files_ref_store, * creating and populating it if it hasn't been read before or if the @@ -381,10 +393,8 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref { const char *packed_refs_file = files_packed_refs_path(refs); - if (refs->packed && - !is_lock_file_locked(&refs->packed_refs_lock) && - !stat_validity_check(&refs->packed->validity, packed_refs_file)) - clear_packed_ref_cache(refs); + if (!is_lock_file_locked(&refs->packed_refs_lock)) + validate_packed_ref_cache(refs); if (!refs->packed) refs->packed = read_packed_refs(packed_refs_file); @@ -1311,13 +1321,17 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags) &refs->packed_refs_lock, files_packed_refs_path(refs), flags, timeout_value) < 0) return -1; + /* - * Get the current packed-refs while holding the lock. It is - * important that we call `get_packed_ref_cache()` before - * setting `packed_ref_cache->lock`, because otherwise the - * former will see that the file is locked and assume that the - * cache can't be stale. + * Now that we hold the `packed-refs` lock, make sure that our + * cache matches the current version of the file. Normally + * `get_packed_ref_cache()` does that for us, but that + * function assumes that when the file is locked, any existing + * cache is still valid. We've just locked the file, but it + * might have changed the moment *before* we locked it. */ + validate_packed_ref_cache(refs); + packed_ref_cache = get_packed_ref_cache(refs); /* Increment the reference count to prevent it from being freed: */ acquire_packed_ref_cache(packed_ref_cache); From e0556a928fdba489307d13d8d4c4d2a461fbc3ad Mon Sep 17 00:00:00 2001 From: Prathamesh Chavan Date: Thu, 8 Jun 2017 23:38:12 +0530 Subject: [PATCH 487/566] dir: create function count_slashes() Similar functions exist in apply.c and builtin/show-branch.c for counting the number of slashes in a string. Also in the later patches, we introduce a third caller for the same. Hence, we unify it now by cleaning the existing functions and declaring a common function count_slashes in dir.h and implementing it in dir.c to remove this code duplication. Mentored-by: Christian Couder Mentored-by: Stefan Beller Signed-off-by: Junio C Hamano Signed-off-by: Prathamesh Chavan Signed-off-by: Junio C Hamano --- apply.c | 11 ----------- builtin/show-branch.c | 13 +++---------- dir.c | 9 +++++++++ dir.h | 3 +++ 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/apply.c b/apply.c index e6dbab26ad..27d5dc0d45 100644 --- a/apply.c +++ b/apply.c @@ -762,17 +762,6 @@ static char *find_name_traditional(struct apply_state *state, return find_name_common(state, line, def, p_value, line + len, 0); } -static int count_slashes(const char *cp) -{ - int cnt = 0; - char ch; - - while ((ch = *cp++)) - if (ch == '/') - cnt++; - return cnt; -} - /* * Given the string after "--- " or "+++ ", guess the appropriate * p_value for the given patch. diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 19756595d5..3a5924293a 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -5,6 +5,7 @@ #include "color.h" #include "argv-array.h" #include "parse-options.h" +#include "dir.h" static const char* show_branch_usage[] = { N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" @@ -421,14 +422,6 @@ static int append_tag_ref(const char *refname, const struct object_id *oid, static const char *match_ref_pattern = NULL; static int match_ref_slash = 0; -static int count_slash(const char *s) -{ - int cnt = 0; - while (*s) - if (*s++ == '/') - cnt++; - return cnt; -} static int append_matching_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) @@ -438,7 +431,7 @@ static int append_matching_ref(const char *refname, const struct object_id *oid, * refs/tags/v0.99.9a and friends. */ const char *tail; - int slash = count_slash(refname); + int slash = count_slashes(refname); for (tail = refname; *tail && match_ref_slash < slash; ) if (*tail++ == '/') slash--; @@ -529,7 +522,7 @@ static void append_one_rev(const char *av) int saved_matches = ref_name_cnt; match_ref_pattern = av; - match_ref_slash = count_slash(av); + match_ref_slash = count_slashes(av); for_each_ref(append_matching_ref, NULL); if (saved_matches == ref_name_cnt && ref_name_cnt < MAX_REVS) diff --git a/dir.c b/dir.c index f451bfa48c..a870f42ddc 100644 --- a/dir.c +++ b/dir.c @@ -49,6 +49,15 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, int check_only, const struct pathspec *pathspec); static int get_dtype(struct dirent *de, const char *path, int len); +int count_slashes(const char *s) +{ + int cnt = 0; + while (*s) + if (*s++ == '/') + cnt++; + return cnt; +} + int fspathcmp(const char *a, const char *b) { return ignore_case ? strcasecmp(a, b) : strcmp(a, b); diff --git a/dir.h b/dir.h index bf23a470af..bdeeab7f7f 100644 --- a/dir.h +++ b/dir.h @@ -196,6 +196,9 @@ struct dir_struct { unsigned unmanaged_exclude_files; }; +/*Count the number of slashes for string s*/ +extern int count_slashes(const char *s); + /* * The ordering of these constants is significant, with * higher-numbered match types signifying "closer" (i.e. more From e35b6ac56f6582f85319ebc86d4a9b42ca5a21cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 10 Jun 2017 13:41:01 +0200 Subject: [PATCH 488/566] revision.h: turn rev_info.early_output back into an unsigned int MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rev_info.early_output started out as an unsigned int in cdcefbc97 (Add "--early-output" log flag for interactive GUI use, 2007-11-03), but later it was turned into a single bit in a bit field in cc243c3ce (show: --ignore-missing, 2011-05-18) without explanation, though the code using it still expects it to be a regular integer type and uses it as a counter. Consequently, any even number given via '--early-output=', or indeed a plain '--early-output' defaulting to 100 effectively disabled the feature. Turn rev_info.early_output back into its origin unsigned int data type, making '--early-output' work again. Signed-off-by: SZEDER Gábor Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- revision.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/revision.h b/revision.h index 9fac1a607d..f801118e10 100644 --- a/revision.h +++ b/revision.h @@ -74,8 +74,9 @@ struct rev_info { /* topo-sort */ enum rev_sort_order sort_order; - unsigned int early_output:1, - ignore_missing:1, + unsigned int early_output; + + unsigned int ignore_missing:1, ignore_missing_links:1; /* Traversal flags */ From 9ada7aee198c3644604eac1ff7cc50284b48d85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 9 Jun 2017 20:17:30 +0200 Subject: [PATCH 489/566] revision.c: stricter parsing of '--no-{min,max}-parents' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two options are parsed using starts_with(), allowing things like 'git log --no-min-parents-foobarbaz' to succeed. Use strcmp() instead. Signed-off-by: SZEDER Gábor Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/revision.c b/revision.c index b37dbec378..8a40cc7b34 100644 --- a/revision.c +++ b/revision.c @@ -1777,11 +1777,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->max_parents = 1; } else if (starts_with(arg, "--min-parents=")) { revs->min_parents = atoi(arg+14); - } else if (starts_with(arg, "--no-min-parents")) { + } else if (!strcmp(arg, "--no-min-parents")) { revs->min_parents = 0; } else if (starts_with(arg, "--max-parents=")) { revs->max_parents = atoi(arg+14); - } else if (starts_with(arg, "--no-max-parents")) { + } else if (!strcmp(arg, "--no-max-parents")) { revs->max_parents = -1; } else if (!strcmp(arg, "--boundary")) { revs->boundary = 1; From dffc651ed191ff0eb3cfd0e8df83a423b4dae994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 9 Jun 2017 20:17:31 +0200 Subject: [PATCH 490/566] revision.c: stricter parsing of '--early-output' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parsing of '--early-output' with or without its optional integer argument allowed bogus options like '--early-output-foobarbaz' to slip through and be ignored. Fix it by parsing '--early-output' in the same way as other options with an optional argument are parsed. Furthermore, use strtoul_ui() to parse the optional integer argument and to refuse negative numbers. While at it, use skip_prefix() instead of starts_with() and magic numbers. Signed-off-by: SZEDER Gábor Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/revision.c b/revision.c index 8a40cc7b34..b942fed748 100644 --- a/revision.c +++ b/revision.c @@ -1750,16 +1750,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--author-date-order")) { revs->sort_order = REV_SORT_BY_AUTHOR_DATE; revs->topo_order = 1; - } else if (starts_with(arg, "--early-output")) { - int count = 100; - switch (arg[14]) { - case '=': - count = atoi(arg+15); - /* Fallthrough */ - case 0: - revs->topo_order = 1; - revs->early_output = count; - } + } else if (!strcmp(arg, "--early-output")) { + revs->early_output = 100; + revs->topo_order = 1; + } else if (skip_prefix(arg, "--early-output=", &optarg)) { + if (strtoul_ui(optarg, 10, &revs->early_output) < 0) + die("'%s': not a non-negative integer", optarg); + revs->topo_order = 1; } else if (!strcmp(arg, "--parents")) { revs->rewrite_parents = 1; revs->print_parents = 1; From 479b3d978597110208d5a7623a626b28d5c801e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 9 Jun 2017 20:17:32 +0200 Subject: [PATCH 491/566] revision.c: use skip_prefix() in handle_revision_opt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of starts_with() and a bunch of magic numbers. Signed-off-by: SZEDER Gábor Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/revision.c b/revision.c index b942fed748..bb2e094549 100644 --- a/revision.c +++ b/revision.c @@ -1690,8 +1690,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->max_count = atoi(argv[1]); revs->no_walk = 0; return 2; - } else if (starts_with(arg, "-n")) { - revs->max_count = atoi(arg + 2); + } else if (skip_prefix(arg, "-n", &optarg)) { + revs->max_count = atoi(optarg); revs->no_walk = 0; } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) { revs->max_age = atoi(optarg); @@ -1772,12 +1772,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->min_parents = 2; } else if (!strcmp(arg, "--no-merges")) { revs->max_parents = 1; - } else if (starts_with(arg, "--min-parents=")) { - revs->min_parents = atoi(arg+14); + } else if (skip_prefix(arg, "--min-parents=", &optarg)) { + revs->min_parents = atoi(optarg); } else if (!strcmp(arg, "--no-min-parents")) { revs->min_parents = 0; - } else if (starts_with(arg, "--max-parents=")) { - revs->max_parents = atoi(arg+14); + } else if (skip_prefix(arg, "--max-parents=", &optarg)) { + revs->max_parents = atoi(optarg); } else if (!strcmp(arg, "--no-max-parents")) { revs->max_parents = -1; } else if (!strcmp(arg, "--boundary")) { @@ -1859,14 +1859,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(NULL, revs); - } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) { + } else if (skip_prefix(arg, "--pretty=", &optarg) || + skip_prefix(arg, "--format=", &optarg)) { /* * Detached form ("--pretty X" as opposed to "--pretty=X") * not allowed, since the argument is optional. */ revs->verbose_header = 1; revs->pretty_given = 1; - get_commit_format(arg+9, revs); + get_commit_format(optarg, revs); } else if (!strcmp(arg, "--expand-tabs")) { revs->expand_tabs_in_log = 8; } else if (!strcmp(arg, "--no-expand-tabs")) { @@ -1884,26 +1885,23 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->show_signature = 1; } else if (!strcmp(arg, "--no-show-signature")) { revs->show_signature = 0; - } else if (!strcmp(arg, "--show-linear-break") || - starts_with(arg, "--show-linear-break=")) { - if (starts_with(arg, "--show-linear-break=")) - revs->break_bar = xstrdup(arg + 20); - else - revs->break_bar = " .........."; + } else if (!strcmp(arg, "--show-linear-break")) { + revs->break_bar = " .........."; revs->track_linear = 1; revs->track_first_time = 1; - } else if (starts_with(arg, "--show-notes=") || - starts_with(arg, "--notes=")) { + } else if (skip_prefix(arg, "--show-linear-break=", &optarg)) { + revs->break_bar = xstrdup(optarg); + revs->track_linear = 1; + revs->track_first_time = 1; + } else if (skip_prefix(arg, "--show-notes=", &optarg) || + skip_prefix(arg, "--notes=", &optarg)) { struct strbuf buf = STRBUF_INIT; revs->show_notes = 1; revs->show_notes_given = 1; - if (starts_with(arg, "--show-notes")) { - if (revs->notes_opt.use_default_notes < 0) - revs->notes_opt.use_default_notes = 1; - strbuf_addstr(&buf, arg+13); - } - else - strbuf_addstr(&buf, arg+8); + if (starts_with(arg, "--show-notes=") && + revs->notes_opt.use_default_notes < 0) + revs->notes_opt.use_default_notes = 1; + strbuf_addstr(&buf, optarg); expand_notes_ref(&buf); string_list_append(&revs->notes_opt.extra_notes_refs, strbuf_detach(&buf, NULL)); @@ -1940,8 +1938,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->abbrev = 0; } else if (!strcmp(arg, "--abbrev")) { revs->abbrev = DEFAULT_ABBREV; - } else if (starts_with(arg, "--abbrev=")) { - revs->abbrev = strtoul(arg + 9, NULL, 10); + } else if (skip_prefix(arg, "--abbrev=", &optarg)) { + revs->abbrev = strtoul(optarg, NULL, 10); if (revs->abbrev < MINIMUM_ABBREV) revs->abbrev = MINIMUM_ABBREV; else if (revs->abbrev > 40) From 8b1d9136e1ca0125ad6f62667b039f1f59513e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 9 Jun 2017 20:17:33 +0200 Subject: [PATCH 492/566] revision.c: use skip_prefix() in handle_revision_pseudo_opt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of starts_with() and a bunch of magic numbers. Signed-off-by: SZEDER Gábor Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- revision.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/revision.c b/revision.c index bb2e094549..2f6f75f960 100644 --- a/revision.c +++ b/revision.c @@ -2099,20 +2099,20 @@ static int handle_revision_pseudo_opt(const char *submodule, } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) { add_ref_exclusion(&revs->ref_excludes, optarg); return argcount; - } else if (starts_with(arg, "--branches=")) { + } else if (skip_prefix(arg, "--branches=", &optarg)) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); - for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb); + for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb); clear_ref_exclusion(&revs->ref_excludes); - } else if (starts_with(arg, "--tags=")) { + } else if (skip_prefix(arg, "--tags=", &optarg)) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); - for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb); + for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb); clear_ref_exclusion(&revs->ref_excludes); - } else if (starts_with(arg, "--remotes=")) { + } else if (skip_prefix(arg, "--remotes=", &optarg)) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); - for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb); + for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb); clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--reflog")) { add_reflogs_to_pending(revs, *flags); @@ -2122,14 +2122,14 @@ static int handle_revision_pseudo_opt(const char *submodule, *flags ^= UNINTERESTING | BOTTOM; } else if (!strcmp(arg, "--no-walk")) { revs->no_walk = REVISION_WALK_NO_WALK_SORTED; - } else if (starts_with(arg, "--no-walk=")) { + } else if (skip_prefix(arg, "--no-walk=", &optarg)) { /* * Detached form ("--no-walk X" as opposed to "--no-walk=X") * not allowed, since the argument is optional. */ - if (!strcmp(arg + 10, "sorted")) + if (!strcmp(optarg, "sorted")) revs->no_walk = REVISION_WALK_NO_WALK_SORTED; - else if (!strcmp(arg + 10, "unsorted")) + else if (!strcmp(optarg, "unsorted")) revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED; else return error("invalid argument to --no-walk"); From bb8efa1772c5a2ae1ce13ee2482fc8d97011ab7a Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 13 Jun 2017 10:47:22 +0000 Subject: [PATCH 493/566] t3200: add test for single parameter passed to -m option Add a test for the case when only one parameter is passed to '-m' (move/rename) option. For example - if 'git branch -m bbb' is run while checked out on aaa branch, it should rename the currently checked out branch to bbb. There was no test for this particular case with only one parameter for -m option. However, there's one similar test case for -M option. Add test for making sure HEAD points to the bbb (new branch name). Also add a test for making sure the reflog that is moved to 'bbb' retains entries created for the currently checked out branch. Note that since the topmost entry on reflog for bbb will be about branch creation, we compare bbb@{1} (instead of bbb@{0}) with aaa@{0} to make sure the reflog for bbb retains entries from aaa. Signed-off-by: Sahil Dua Signed-off-by: Junio C Hamano --- t/t3200-branch.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 10f8f026ff..48d152b9a9 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -100,6 +100,23 @@ test_expect_success 'git branch -m n/n n should work' ' git reflog exists refs/heads/n ' +# The topmost entry in reflog for branch bbb is about branch creation. +# Hence, we compare bbb@{1} (instead of bbb@{0}) with aaa@{0}. + +test_expect_success 'git branch -m bbb should rename checked out branch' ' + test_when_finished git branch -D bbb && + test_when_finished git checkout master && + git checkout -b aaa && + git commit --allow-empty -m "a new commit" && + git rev-parse aaa@{0} >expect && + git branch -m bbb && + git rev-parse bbb@{1} >actual && + test_cmp expect actual && + git symbolic-ref HEAD >actual && + echo refs/heads/bbb >expect && + test_cmp expect actual +' + test_expect_success 'git branch -m o/o o should fail when o/p exists' ' git branch o/o && git branch o/p && From 22fc703ec949602e9fd4e2ab0bb63dd47c2945b5 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 13 Jun 2017 13:38:34 +0200 Subject: [PATCH 494/566] git-stash: fix pushing stash with pathspec from subdir The `git stash push` command recently gained the ability to get a pathspec as its argument to only stash matching files. Calling this command from a subdirectory does not work, though, as one of the first things we do is changing to the top level directory without keeping track of the prefix from which the command is being run. Fix the shortcoming by storing the prefix previous to the call to `cd_to_toplevel` and then subsequently using `git rev-parse --prefix` to correctly resolve the pathspec. Add a test to catch future breakage of this usecase. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- git-stash.sh | 3 +++ t/t3903-stash.sh | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/git-stash.sh b/git-stash.sh index 2fb651b2b8..e7b85932d6 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -19,6 +19,7 @@ OPTIONS_SPEC= START_DIR=$(pwd) . git-sh-setup require_work_tree +prefix=$(git rev-parse --show-prefix) || exit 1 cd_to_toplevel TMP="$GIT_DIR/.git-stash.$$" @@ -273,6 +274,8 @@ push_stash () { shift done + eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")" + if test -n "$patch_mode" && test -n "$untracked" then die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")" diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index b71d1e659e..d10ddb5b35 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -812,6 +812,22 @@ test_expect_success 'stash -- stashes and restores the file' ' test_path_is_file bar ' +test_expect_success 'stash -- stashes in subdirectory' ' + mkdir sub && + >foo && + >bar && + git add foo bar && + ( + cd sub && + git stash push -- ../foo + ) && + test_path_is_file bar && + test_path_is_missing foo && + git stash pop && + test_path_is_file foo && + test_path_is_file bar +' + test_expect_success 'stash with multiple pathspec arguments' ' >foo && >bar && From 0b1bb0c032091930523399cc265c13a70c01aa2d Mon Sep 17 00:00:00 2001 From: Lars Schneider Date: Tue, 13 Jun 2017 10:18:07 +0200 Subject: [PATCH 495/566] Configure Git contribution guidelines for github.com Many open source projects use github.com for their contribution process. Although we mirror the Git core repository to github.com [1] we do not use any other github.com service. This is unknown/unexpected to a number of (potential) contributors and consequently they create Pull Requests against our mirror with their contributions. These Pull Requests become stale. This is frustrating to them as they think we ignore them and it is also unsatisfactory for us as we miss potential code improvements and/or new contributors. GitHub contribution guidelines and a GitHub Pull Request template that is visible to every Pull Request creator can be configured with special files in a Git repository [2]. Let's make use of this! [1] https://github.com/git/git [2] https://help.github.com/articles/creating-a-pull-request-template-for-your-repository/ Signed-off-by: Lars Schneider Helped-by: Jeff King Signed-off-by: Junio C Hamano --- .github/CONTRIBUTING.md | 19 +++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 7 +++++++ 2 files changed, 26 insertions(+) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000000..64e605a02b --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,19 @@ +## Contributing to Git + +Thanks for taking the time to contribute to Git! Please be advised that the +Git community does not use github.com for their contributions. Instead, we use +a mailing list (git@vger.kernel.org) for code submissions, code +reviews, and bug reports. + +Nevertheless, you can use [submitGit](http://submitgit.herokuapp.com/) to +conveniently send your Pull Requests commits to our mailing list. + +Please read ["A note from the maintainer"](https://git.kernel.org/pub/scm/git/git.git/plain/MaintNotes?h=todo) +to learn how the Git project is managed, and how you can work with it. +In addition, we highly recommend you to read [our submission guidelines](../Documentation/SubmittingPatches). + +If you prefer video, then [this talk](https://www.youtube.com/watch?v=Q7i_qQW__q4&feature=youtu.be&t=6m4s) +might be useful to you as the presenter walks you through the contribution +process by example. + +Your friendly Git community! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..adba13e5ba --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thanks for taking the time to contribute to Git! Please be advised that the +Git community does not use github.com for their contributions. Instead, we use +a mailing list (git@vger.kernel.org) for code submissions, code reviews, and +bug reports. Nevertheless, you can use submitGit to conveniently send your Pull +Requests commits to our mailing list. + +Please read the "guidelines for contributing" linked above! From a7609c54b399219bae5b8b94b305cf8e18bf20f8 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:52 -0700 Subject: [PATCH 496/566] convert: convert get_cached_convert_stats_ascii to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 3 ++- convert.c | 5 +++-- convert.h | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b376afc312..0044abf669 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -63,7 +63,8 @@ static void write_eolinfo(const struct cache_entry *ce, const char *path) const char *w_txt = ""; const char *a_txt = get_convert_attr_ascii(path); if (ce && S_ISREG(ce->ce_mode)) - i_txt = get_cached_convert_stats_ascii(ce->name); + i_txt = get_cached_convert_stats_ascii(&the_index, + ce->name); if (!lstat(path, &st) && S_ISREG(st.st_mode)) w_txt = get_wt_convert_stats_ascii(path); printf("i/%-5s w/%-5s attr/%-17s\t", i_txt, w_txt, a_txt); diff --git a/convert.c b/convert.c index f1e168bc30..03160b376a 100644 --- a/convert.c +++ b/convert.c @@ -134,11 +134,12 @@ static const char *gather_convert_stats_ascii(const char *data, unsigned long si } } -const char *get_cached_convert_stats_ascii(const char *path) +const char *get_cached_convert_stats_ascii(const struct index_state *istate, + const char *path) { const char *ret; unsigned long sz; - void *data = read_blob_data_from_cache(path, &sz); + void *data = read_blob_data_from_index(istate, path, &sz); ret = gather_convert_stats_ascii(data, sz); free(data); return ret; diff --git a/convert.h b/convert.h index 82871a11d5..667b7dfe07 100644 --- a/convert.h +++ b/convert.h @@ -4,6 +4,8 @@ #ifndef CONVERT_H #define CONVERT_H +struct index_state; + enum safe_crlf { SAFE_CRLF_FALSE = 0, SAFE_CRLF_FAIL = 1, @@ -33,7 +35,8 @@ enum eol { }; extern enum eol core_eol; -extern const char *get_cached_convert_stats_ascii(const char *path); +extern const char *get_cached_convert_stats_ascii(const struct index_state *istate, + const char *path); extern const char *get_wt_convert_stats_ascii(const char *path); extern const char *get_convert_attr_ascii(const char *path); From 49a6d31fc8c4277b1c5f5b82331e7190afa1d4ce Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:53 -0700 Subject: [PATCH 497/566] convert: convert crlf_to_git to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- convert.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/convert.c b/convert.c index 03160b376a..0cafb06f56 100644 --- a/convert.c +++ b/convert.c @@ -218,13 +218,13 @@ static void check_safe_crlf(const char *path, enum crlf_action crlf_action, } } -static int has_cr_in_index(const char *path) +static int has_cr_in_index(const struct index_state *istate, const char *path) { unsigned long sz; void *data; int has_cr; - data = read_blob_data_from_cache(path, &sz); + data = read_blob_data_from_index(istate, path, &sz); if (!data) return 0; has_cr = memchr(data, '\r', sz) != NULL; @@ -254,7 +254,8 @@ static int will_convert_lf_to_crlf(size_t len, struct text_stat *stats, } -static int crlf_to_git(const char *path, const char *src, size_t len, +static int crlf_to_git(const struct index_state *istate, + const char *path, const char *src, size_t len, struct strbuf *buf, enum crlf_action crlf_action, enum safe_crlf checksafe) { @@ -286,7 +287,8 @@ static int crlf_to_git(const char *path, const char *src, size_t len, * unless we want to renormalize in a merge or * cherry-pick. */ - if ((checksafe != SAFE_CRLF_RENORMALIZE) && has_cr_in_index(path)) + if ((checksafe != SAFE_CRLF_RENORMALIZE) && + has_cr_in_index(istate, path)) convert_crlf_into_lf = 0; } if ((checksafe == SAFE_CRLF_WARN || @@ -1098,7 +1100,7 @@ int convert_to_git(const char *path, const char *src, size_t len, src = dst->buf; len = dst->len; } - ret |= crlf_to_git(path, src, len, dst, ca.crlf_action, checksafe); + ret |= crlf_to_git(&the_index, path, src, len, dst, ca.crlf_action, checksafe); if (ret && dst) { src = dst->buf; len = dst->len; @@ -1118,7 +1120,7 @@ void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst, if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN)) die("%s: clean filter '%s' failed", path, ca.drv->name); - crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe); + crlf_to_git(&the_index, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe); ident_to_git(path, dst->buf, dst->len, dst, ca.ident); } From d6c41c20e688a1b284b92d92320ba56f639688de Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:54 -0700 Subject: [PATCH 498/566] convert: convert convert_to_git_filter_fd to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- convert.c | 5 +++-- convert.h | 3 ++- sha1_file.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/convert.c b/convert.c index 0cafb06f56..c09242cec0 100644 --- a/convert.c +++ b/convert.c @@ -1108,7 +1108,8 @@ int convert_to_git(const char *path, const char *src, size_t len, return ret | ident_to_git(path, src, len, dst, ca.ident); } -void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst, +void convert_to_git_filter_fd(const struct index_state *istate, + const char *path, int fd, struct strbuf *dst, enum safe_crlf checksafe) { struct conv_attrs ca; @@ -1120,7 +1121,7 @@ void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst, if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN)) die("%s: clean filter '%s' failed", path, ca.drv->name); - crlf_to_git(&the_index, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe); + crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe); ident_to_git(path, dst->buf, dst->len, dst, ca.ident); } diff --git a/convert.h b/convert.h index 667b7dfe07..3a813a7976 100644 --- a/convert.h +++ b/convert.h @@ -52,7 +52,8 @@ static inline int would_convert_to_git(const char *path) return convert_to_git(path, NULL, 0, NULL, 0); } /* Precondition: would_convert_to_git_filter_fd(path) == true */ -extern void convert_to_git_filter_fd(const char *path, int fd, +extern void convert_to_git_filter_fd(const struct index_state *istate, + const char *path, int fd, struct strbuf *dst, enum safe_crlf checksafe); extern int would_convert_to_git_filter_fd(const char *path); diff --git a/sha1_file.c b/sha1_file.c index 59a4ed2ed3..ab09241d20 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -3580,7 +3580,7 @@ static int index_stream_convert_blob(unsigned char *sha1, int fd, assert(path); assert(would_convert_to_git_filter_fd(path)); - convert_to_git_filter_fd(path, fd, &sbuf, + convert_to_git_filter_fd(&the_index, path, fd, &sbuf, write_object ? safe_crlf : SAFE_CRLF_FALSE); if (write_object) From 82b474e025e89cfa294e81611c81355a73dc23a2 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:55 -0700 Subject: [PATCH 499/566] convert: convert convert_to_git to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- apply.c | 2 +- blame.c | 2 +- combine-diff.c | 2 +- convert.c | 7 ++++--- convert.h | 8 +++++--- diff.c | 6 +++--- dir.c | 2 +- sha1_file.c | 4 ++-- 8 files changed, 18 insertions(+), 15 deletions(-) diff --git a/apply.c b/apply.c index c49cef0637..97afb6f60a 100644 --- a/apply.c +++ b/apply.c @@ -2267,7 +2267,7 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) case S_IFREG: if (strbuf_read_file(buf, path, st->st_size) != st->st_size) return error(_("unable to open or read %s"), path); - convert_to_git(path, buf->buf, buf->len, buf, 0); + convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0); return 0; default: return -1; diff --git a/blame.c b/blame.c index 843c845cba..a6f3d72df8 100644 --- a/blame.c +++ b/blame.c @@ -229,7 +229,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, if (strbuf_read(&buf, 0, 0) < 0) die_errno("failed to read from stdin"); } - convert_to_git(path, buf.buf, buf.len, &buf, 0); + convert_to_git(&the_index, path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash); diff --git a/combine-diff.c b/combine-diff.c index 2848034fe9..74f723af3d 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1053,7 +1053,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, if (is_file) { struct strbuf buf = STRBUF_INIT; - if (convert_to_git(elem->path, result, len, &buf, safe_crlf)) { + if (convert_to_git(&the_index, elem->path, result, len, &buf, safe_crlf)) { free(result); result = strbuf_detach(&buf, &len); result_size = len; diff --git a/convert.c b/convert.c index c09242cec0..600d11e73f 100644 --- a/convert.c +++ b/convert.c @@ -1084,7 +1084,8 @@ const char *get_convert_attr_ascii(const char *path) return ""; } -int convert_to_git(const char *path, const char *src, size_t len, +int convert_to_git(const struct index_state *istate, + const char *path, const char *src, size_t len, struct strbuf *dst, enum safe_crlf checksafe) { int ret = 0; @@ -1100,7 +1101,7 @@ int convert_to_git(const char *path, const char *src, size_t len, src = dst->buf; len = dst->len; } - ret |= crlf_to_git(&the_index, path, src, len, dst, ca.crlf_action, checksafe); + ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe); if (ret && dst) { src = dst->buf; len = dst->len; @@ -1171,7 +1172,7 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str src = dst->buf; len = dst->len; } - return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_RENORMALIZE); + return ret | convert_to_git(&the_index, path, src, len, dst, SAFE_CRLF_RENORMALIZE); } /***************************************************************** diff --git a/convert.h b/convert.h index 3a813a7976..60cb41d6a4 100644 --- a/convert.h +++ b/convert.h @@ -41,15 +41,17 @@ extern const char *get_wt_convert_stats_ascii(const char *path); extern const char *get_convert_attr_ascii(const char *path); /* returns 1 if *dst was used */ -extern int convert_to_git(const char *path, const char *src, size_t len, +extern int convert_to_git(const struct index_state *istate, + const char *path, const char *src, size_t len, struct strbuf *dst, enum safe_crlf checksafe); extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst); -static inline int would_convert_to_git(const char *path) +static inline int would_convert_to_git(const struct index_state *istate, + const char *path) { - return convert_to_git(path, NULL, 0, NULL, 0); + return convert_to_git(istate, path, NULL, 0, NULL, 0); } /* Precondition: would_convert_to_git_filter_fd(path) == true */ extern void convert_to_git_filter_fd(const struct index_state *istate, diff --git a/diff.c b/diff.c index 5275c4b780..976a6f91b1 100644 --- a/diff.c +++ b/diff.c @@ -2755,7 +2755,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int * Similarly, if we'd have to convert the file contents anyway, that * makes the optimization not worthwhile. */ - if (!want_file && would_convert_to_git(name)) + if (!want_file && would_convert_to_git(&the_index, name)) return 0; len = strlen(name); @@ -2877,7 +2877,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) * point if the path requires us to run the content * conversion. */ - if (size_only && !would_convert_to_git(s->path)) + if (size_only && !would_convert_to_git(&the_index, s->path)) return 0; /* @@ -2904,7 +2904,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) /* * Convert from working tree format to canonical git format */ - if (convert_to_git(s->path, s->data, s->size, &buf, crlf_warn)) { + if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, crlf_warn)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; diff --git a/dir.c b/dir.c index 9efcf1eab6..f673b86f31 100644 --- a/dir.c +++ b/dir.c @@ -795,7 +795,7 @@ static int add_excludes(const char *fname, const char *base, int baselen, (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 && !ce_stage(istate->cache[pos]) && ce_uptodate(istate->cache[pos]) && - !would_convert_to_git(fname)) + !would_convert_to_git(istate, fname)) hashcpy(sha1_stat->sha1, istate->cache[pos]->oid.hash); else diff --git a/sha1_file.c b/sha1_file.c index ab09241d20..feb227a837 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -3546,7 +3546,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size, */ if ((type == OBJ_BLOB) && path) { struct strbuf nbuf = STRBUF_INIT; - if (convert_to_git(path, buf, size, &nbuf, + if (convert_to_git(&the_index, path, buf, size, &nbuf, write_object ? safe_crlf : SAFE_CRLF_FALSE)) { buf = strbuf_detach(&nbuf, &size); re_allocated = 1; @@ -3668,7 +3668,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, else if (!S_ISREG(st->st_mode)) ret = index_pipe(sha1, fd, type, path, flags); else if (st->st_size <= big_file_threshold || type != OBJ_BLOB || - (path && would_convert_to_git(path))) + (path && would_convert_to_git(&the_index, path))) ret = index_core(sha1, fd, xsize_t(st->st_size), type, path, flags); else From a33e0b2a77d7010ba8bf0e025fffaf98f464a938 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:56 -0700 Subject: [PATCH 500/566] convert: convert renormalize_buffer to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- convert.c | 6 ++++-- convert.h | 3 ++- ll-merge.c | 2 +- merge-recursive.c | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/convert.c b/convert.c index 600d11e73f..4097f521f2 100644 --- a/convert.c +++ b/convert.c @@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "attr.h" #include "run-command.h" @@ -1165,14 +1166,15 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc return convert_to_working_tree_internal(path, src, len, dst, 0); } -int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst) +int renormalize_buffer(const struct index_state *istate, const char *path, + const char *src, size_t len, struct strbuf *dst) { int ret = convert_to_working_tree_internal(path, src, len, dst, 1); if (ret) { src = dst->buf; len = dst->len; } - return ret | convert_to_git(&the_index, path, src, len, dst, SAFE_CRLF_RENORMALIZE); + return ret | convert_to_git(istate, path, src, len, dst, SAFE_CRLF_RENORMALIZE); } /***************************************************************** diff --git a/convert.h b/convert.h index 60cb41d6a4..cecf59d1aa 100644 --- a/convert.h +++ b/convert.h @@ -46,7 +46,8 @@ extern int convert_to_git(const struct index_state *istate, struct strbuf *dst, enum safe_crlf checksafe); extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); -extern int renormalize_buffer(const char *path, const char *src, size_t len, +extern int renormalize_buffer(const struct index_state *istate, + const char *path, const char *src, size_t len, struct strbuf *dst); static inline int would_convert_to_git(const struct index_state *istate, const char *path) diff --git a/ll-merge.c b/ll-merge.c index ac0d4a5d78..d7eafb61a6 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -339,7 +339,7 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr static void normalize_file(mmfile_t *mm, const char *path) { struct strbuf strbuf = STRBUF_INIT; - if (renormalize_buffer(path, mm->ptr, mm->size, &strbuf)) { + if (renormalize_buffer(&the_index, path, mm->ptr, mm->size, &strbuf)) { free(mm->ptr); mm->size = strbuf.len; mm->ptr = strbuf_detach(&strbuf, NULL); diff --git a/merge-recursive.c b/merge-recursive.c index ae5238d82c..eac12d4888 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1639,8 +1639,8 @@ static int blob_unchanged(struct merge_options *opt, * performed. Comparison can be skipped if both files are * unchanged since their sha1s have already been compared. */ - if (renormalize_buffer(path, o.buf, o.len, &o) | - renormalize_buffer(path, a.buf, a.len, &a)) + if (renormalize_buffer(&the_index, path, o.buf, o.len, &o) | + renormalize_buffer(&the_index, path, a.buf, a.len, &a)) ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len)); error_return: From 85ab50f938601cf874c841cee4c56f1d1dc8199e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:57 -0700 Subject: [PATCH 501/566] tree: convert read_tree to take an index parameter Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 2 +- tree.c | 28 ++++++++++++++++++---------- tree.h | 3 ++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 0044abf669..93e46ab5fb 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -460,7 +460,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) PATHSPEC_PREFER_CWD, prefix, matchbuf); } else memset(&pathspec, 0, sizeof(pathspec)); - if (read_tree(tree, 1, &pathspec)) + if (read_tree(tree, 1, &pathspec, &the_index)) die("unable to read tree entries %s", tree_name); for (i = 0; i < active_nr; i++) { diff --git a/tree.c b/tree.c index 603b29ee80..dd69423d9a 100644 --- a/tree.c +++ b/tree.c @@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "cache-tree.h" #include "tree.h" @@ -8,7 +9,11 @@ const char *tree_type = "tree"; -static int read_one_entry_opt(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, int opt) +static int read_one_entry_opt(struct index_state *istate, + const unsigned char *sha1, + const char *base, int baselen, + const char *pathname, + unsigned mode, int stage, int opt) { int len; unsigned int size; @@ -27,14 +32,15 @@ static int read_one_entry_opt(const unsigned char *sha1, const char *base, int b memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len+1); hashcpy(ce->oid.hash, sha1); - return add_cache_entry(ce, opt); + return add_index_entry(istate, ce, opt); } static int read_one_entry(const unsigned char *sha1, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { - return read_one_entry_opt(sha1, base->buf, base->len, pathname, + struct index_state *istate = context; + return read_one_entry_opt(istate, sha1, base->buf, base->len, pathname, mode, stage, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK); } @@ -47,7 +53,8 @@ static int read_one_entry_quick(const unsigned char *sha1, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { - return read_one_entry_opt(sha1, base->buf, base->len, pathname, + struct index_state *istate = context; + return read_one_entry_opt(istate, sha1, base->buf, base->len, pathname, mode, stage, ADD_CACHE_JUST_APPEND); } @@ -144,7 +151,8 @@ static int cmp_cache_name_compare(const void *a_, const void *b_) ce2->name, ce2->ce_namelen, ce_stage(ce2)); } -int read_tree(struct tree *tree, int stage, struct pathspec *match) +int read_tree(struct tree *tree, int stage, struct pathspec *match, + struct index_state *istate) { read_tree_fn_t fn = NULL; int i, err; @@ -164,23 +172,23 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match) * do it the original slow way, otherwise, append and then * sort at the end. */ - for (i = 0; !fn && i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; !fn && i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; if (ce_stage(ce) == stage) fn = read_one_entry; } if (!fn) fn = read_one_entry_quick; - err = read_tree_recursive(tree, "", 0, stage, match, fn, NULL); + err = read_tree_recursive(tree, "", 0, stage, match, fn, istate); if (fn == read_one_entry || err) return err; /* * Sort the cache entry -- we need to nuke the cache tree, though. */ - cache_tree_free(&active_cache_tree); - QSORT(active_cache, active_nr, cmp_cache_name_compare); + cache_tree_free(&istate->cache_tree); + QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare); return 0; } diff --git a/tree.h b/tree.h index 0d4734b94b..744e6dc2ac 100644 --- a/tree.h +++ b/tree.h @@ -34,6 +34,7 @@ extern int read_tree_recursive(struct tree *tree, int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context); -extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec); +extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec, + struct index_state *istate); #endif /* TREE_H */ From 312c984a027f50247501d002fbd228a982f4f096 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:58 -0700 Subject: [PATCH 502/566] ls-files: convert overlay_tree_on_cache to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/commit.c | 3 ++- builtin/ls-files.c | 15 ++++++++------- cache.h | 3 ++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/builtin/commit.c b/builtin/commit.c index da1ba4c862..78ef319a24 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -253,7 +253,8 @@ static int list_paths(struct string_list *list, const char *with_tree, if (with_tree) { char *max_prefix = common_prefix(pattern); - overlay_tree_on_cache(with_tree, max_prefix ? max_prefix : prefix); + overlay_tree_on_index(&the_index, with_tree, + max_prefix ? max_prefix : prefix); free(max_prefix); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 93e46ab5fb..a78b291abd 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -431,7 +431,8 @@ static int get_common_prefix_len(const char *common_prefix) * that were given from the command line. We are not * going to write this index out. */ -void overlay_tree_on_cache(const char *tree_name, const char *prefix) +void overlay_tree_on_index(struct index_state *istate, + const char *tree_name, const char *prefix) { struct tree *tree; struct object_id oid; @@ -446,8 +447,8 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) die("bad tree-ish %s", tree_name); /* Hoist the unmerged entries up to stage #3 to make room */ - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; if (!ce_stage(ce)) continue; ce->ce_flags |= CE_STAGEMASK; @@ -460,11 +461,11 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) PATHSPEC_PREFER_CWD, prefix, matchbuf); } else memset(&pathspec, 0, sizeof(pathspec)); - if (read_tree(tree, 1, &pathspec, &the_index)) + if (read_tree(tree, 1, &pathspec, istate)) die("unable to read tree entries %s", tree_name); - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i]; switch (ce_stage(ce)) { case 0: last_stage0 = ce; @@ -679,7 +680,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) */ if (show_stage || show_unmerged) die("ls-files --with-tree is incompatible with -s or -u"); - overlay_tree_on_cache(with_tree, max_prefix); + overlay_tree_on_index(&the_index, with_tree, max_prefix); } show_files(&dir); if (show_resolve_undo) diff --git a/cache.h b/cache.h index 4d92aae0e8..5a0e0a9e5d 100644 --- a/cache.h +++ b/cache.h @@ -2186,7 +2186,8 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule); #define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK) /* ls-files */ -void overlay_tree_on_cache(const char *tree_name, const char *prefix); +void overlay_tree_on_index(struct index_state *istate, + const char *tree_name, const char *prefix); char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); From 1985fd68c65f2989033924416367a683b1d8ca67 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:13:59 -0700 Subject: [PATCH 503/566] ls-files: convert write_eolinfo to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a78b291abd..8c3f3d8cac 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -53,17 +53,16 @@ static const char *tag_modified = ""; static const char *tag_skip_worktree = ""; static const char *tag_resolve_undo = ""; -static void write_eolinfo(const struct cache_entry *ce, const char *path) +static void write_eolinfo(const struct index_state *istate, + const struct cache_entry *ce, const char *path) { - if (!show_eol) - return; - else { + if (show_eol) { struct stat st; const char *i_txt = ""; const char *w_txt = ""; const char *a_txt = get_convert_attr_ascii(path); if (ce && S_ISREG(ce->ce_mode)) - i_txt = get_cached_convert_stats_ascii(&the_index, + i_txt = get_cached_convert_stats_ascii(istate, ce->name); if (!lstat(path, &st) && S_ISREG(st.st_mode)) w_txt = get_wt_convert_stats_ascii(path); @@ -105,7 +104,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) return; fputs(tag, stdout); - write_eolinfo(NULL, ent->name); + write_eolinfo(NULL, NULL, ent->name); write_name(ent->name); } @@ -275,7 +274,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) find_unique_abbrev(ce->oid.hash, abbrev), ce_stage(ce)); } - write_eolinfo(ce, ce->name); + write_eolinfo(&the_index, ce, ce->name); write_name(ce->name); if (debug_mode) { const struct stat_data *sd = &ce->ce_stat_data; From 23d6236a07be1434479a5d2f1f82cd8dda4818f9 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:00 -0700 Subject: [PATCH 504/566] ls-files: convert show_killed_files to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 8c3f3d8cac..b82b78036d 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -120,7 +120,8 @@ static void show_other_files(struct dir_struct *dir) } } -static void show_killed_files(struct dir_struct *dir) +static void show_killed_files(const struct index_state *istate, + const struct dir_struct *dir) { int i; for (i = 0; i < dir->nr; i++) { @@ -134,29 +135,29 @@ static void show_killed_files(struct dir_struct *dir) /* If ent->name is prefix of an entry in the * cache, it will be killed. */ - pos = cache_name_pos(ent->name, ent->len); + pos = index_name_pos(istate, ent->name, ent->len); if (0 <= pos) die("BUG: killed-file %.*s not found", ent->len, ent->name); pos = -pos - 1; - while (pos < active_nr && - ce_stage(active_cache[pos])) + while (pos < istate->cache_nr && + ce_stage(istate->cache[pos])) pos++; /* skip unmerged */ - if (active_nr <= pos) + if (istate->cache_nr <= pos) break; /* pos points at a name immediately after * ent->name in the cache. Does it expect * ent->name to be a directory? */ - len = ce_namelen(active_cache[pos]); + len = ce_namelen(istate->cache[pos]); if ((ent->len < len) && - !strncmp(active_cache[pos]->name, + !strncmp(istate->cache[pos]->name, ent->name, ent->len) && - active_cache[pos]->name[ent->len] == '/') + istate->cache[pos]->name[ent->len] == '/') killed = 1; break; } - if (0 <= cache_name_pos(ent->name, sp - ent->name)) { + if (0 <= index_name_pos(istate, ent->name, sp - ent->name)) { /* If any of the leading directories in * ent->name is registered in the cache, * ent->name will be killed. @@ -337,7 +338,7 @@ static void show_files(struct dir_struct *dir) if (show_others) show_other_files(dir); if (show_killed) - show_killed_files(dir); + show_killed_files(&the_index, dir); } if (show_cached || show_stage) { for (i = 0; i < active_nr; i++) { From 23d6846b2324ef647a339b84ba3abc52e0a4dc94 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:01 -0700 Subject: [PATCH 505/566] ls-files: convert show_other_files to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b82b78036d..5dbff94961 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -108,13 +108,14 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) write_name(ent->name); } -static void show_other_files(struct dir_struct *dir) +static void show_other_files(const struct index_state *istate, + const struct dir_struct *dir) { int i; for (i = 0; i < dir->nr; i++) { struct dir_entry *ent = dir->entries[i]; - if (!cache_name_is_other(ent->name, ent->len)) + if (!index_name_is_other(istate, ent->name, ent->len)) continue; show_dir_entry(tag_other, ent); } @@ -336,7 +337,7 @@ static void show_files(struct dir_struct *dir) dir->flags |= DIR_COLLECT_KILLED_ONLY; fill_directory(dir, &the_index, &pathspec); if (show_others) - show_other_files(dir); + show_other_files(&the_index, dir); if (show_killed) show_killed_files(&the_index, dir); } From 2d407e2da1b12fd1f3cb5fc0b072732ab2ac1c6a Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:02 -0700 Subject: [PATCH 506/566] ls-files: convert show_ru_info to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 5dbff94961..375fe09d1e 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -292,14 +292,14 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) strbuf_release(&name); } -static void show_ru_info(void) +static void show_ru_info(const struct index_state *istate) { struct string_list_item *item; - if (!the_index.resolve_undo) + if (!istate->resolve_undo) return; - for_each_string_list_item(item, the_index.resolve_undo) { + for_each_string_list_item(item, istate->resolve_undo) { const char *path = item->string; struct resolve_undo_info *ui = item->util; int i, len; @@ -685,7 +685,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) } show_files(&dir); if (show_resolve_undo) - show_ru_info(); + show_ru_info(&the_index); if (ps_matched) { int bad; From 1d35e3bf056b508f4ece3875f9d2851be5fcd3d4 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:03 -0700 Subject: [PATCH 507/566] ls-files: convert ce_excluded to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 375fe09d1e..762257f399 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -321,10 +321,11 @@ static void show_ru_info(const struct index_state *istate) } } -static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce) +static int ce_excluded(struct dir_struct *dir, struct index_state *istate, + const struct cache_entry *ce) { int dtype = ce_to_dtype(ce); - return is_excluded(dir, &the_index, ce->name, &dtype); + return is_excluded(dir, istate, ce->name, &dtype); } static void show_files(struct dir_struct *dir) @@ -345,7 +346,7 @@ static void show_files(struct dir_struct *dir) for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, ce)) + !ce_excluded(dir, &the_index, ce)) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -361,7 +362,7 @@ static void show_files(struct dir_struct *dir) struct stat st; int err; if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, ce)) + !ce_excluded(dir, &the_index, ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; From 6510ae173a8630e39a225f15031a7975547979ec Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:04 -0700 Subject: [PATCH 508/566] ls-files: convert prune_cache to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 762257f399..b1626b13b4 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -380,30 +380,31 @@ static void show_files(struct dir_struct *dir) /* * Prune the index to only contain stuff starting with "prefix" */ -static void prune_cache(const char *prefix, size_t prefixlen) +static void prune_index(struct index_state *istate, + const char *prefix, size_t prefixlen) { int pos; unsigned int first, last; if (!prefix) return; - pos = cache_name_pos(prefix, prefixlen); + pos = index_name_pos(istate, prefix, prefixlen); if (pos < 0) pos = -pos-1; first = pos; - last = active_nr; + last = istate->cache_nr; while (last > first) { int next = (last + first) >> 1; - const struct cache_entry *ce = active_cache[next]; + const struct cache_entry *ce = istate->cache[next]; if (!strncmp(ce->name, prefix, prefixlen)) { first = next+1; continue; } last = next; } - memmove(active_cache, active_cache + pos, + memmove(istate->cache, istate->cache + pos, (last - pos) * sizeof(struct cache_entry *)); - active_nr = last - pos; + istate->cache_nr = last - pos; } static int get_common_prefix_len(const char *common_prefix) @@ -661,7 +662,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) max_prefix = common_prefix(&pathspec); max_prefix_len = get_common_prefix_len(max_prefix); - prune_cache(max_prefix, max_prefix_len); + prune_index(&the_index, max_prefix, max_prefix_len); /* Treat unmatching pathspec elements as errors */ if (pathspec.nr && error_unmatch) From ff020a8ab0a40a3f938a30be6d73607c2041cc34 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:05 -0700 Subject: [PATCH 509/566] ls-files: convert show_ce_entry to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b1626b13b4..927aa67469 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -232,7 +232,8 @@ static void show_gitlink(const struct cache_entry *ce) exit(status); } -static void show_ce_entry(const char *tag, const struct cache_entry *ce) +static void show_ce_entry(const struct index_state *istate, + const char *tag, const struct cache_entry *ce) { struct strbuf name = STRBUF_INIT; int len = max_prefix_len; @@ -276,7 +277,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) find_unique_abbrev(ce->oid.hash, abbrev), ce_stage(ce)); } - write_eolinfo(&the_index, ce, ce->name); + write_eolinfo(istate, ce, ce->name); write_name(ce->name); if (debug_mode) { const struct stat_data *sd = &ce->ce_stat_data; @@ -352,7 +353,7 @@ static void show_files(struct dir_struct *dir) continue; if (ce->ce_flags & CE_UPDATE) continue; - show_ce_entry(ce_stage(ce) ? tag_unmerged : + show_ce_entry(&the_index, ce_stage(ce) ? tag_unmerged : (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce); } } @@ -370,9 +371,9 @@ static void show_files(struct dir_struct *dir) continue; err = lstat(ce->name, &st); if (show_deleted && err) - show_ce_entry(tag_removed, ce); + show_ce_entry(&the_index, tag_removed, ce); if (show_modified && ce_modified(ce, &st, 0)) - show_ce_entry(tag_modified, ce); + show_ce_entry(&the_index, tag_modified, ce); } } } From f587c8dcdef17aea15aee45689e181b58b011706 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:06 -0700 Subject: [PATCH 510/566] ls-files: convert show_files to take an index Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 927aa67469..55d6f54fd8 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -329,7 +329,7 @@ static int ce_excluded(struct dir_struct *dir, struct index_state *istate, return is_excluded(dir, istate, ce->name, &dtype); } -static void show_files(struct dir_struct *dir) +static void show_files(struct index_state *istate, struct dir_struct *dir) { int i; @@ -337,33 +337,33 @@ static void show_files(struct dir_struct *dir) if (show_others || show_killed) { if (!show_others) dir->flags |= DIR_COLLECT_KILLED_ONLY; - fill_directory(dir, &the_index, &pathspec); + fill_directory(dir, istate, &pathspec); if (show_others) - show_other_files(&the_index, dir); + show_other_files(istate, dir); if (show_killed) - show_killed_files(&the_index, dir); + show_killed_files(istate, dir); } if (show_cached || show_stage) { - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, &the_index, ce)) + !ce_excluded(dir, istate, ce)) continue; if (show_unmerged && !ce_stage(ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; - show_ce_entry(&the_index, ce_stage(ce) ? tag_unmerged : + show_ce_entry(istate, ce_stage(ce) ? tag_unmerged : (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce); } } if (show_deleted || show_modified) { - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; + for (i = 0; i < istate->cache_nr; i++) { + const struct cache_entry *ce = istate->cache[i]; struct stat st; int err; if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, &the_index, ce)) + !ce_excluded(dir, istate, ce)) continue; if (ce->ce_flags & CE_UPDATE) continue; @@ -371,9 +371,9 @@ static void show_files(struct dir_struct *dir) continue; err = lstat(ce->name, &st); if (show_deleted && err) - show_ce_entry(&the_index, tag_removed, ce); - if (show_modified && ce_modified(ce, &st, 0)) - show_ce_entry(&the_index, tag_modified, ce); + show_ce_entry(istate, tag_removed, ce); + if (show_modified && ie_modified(istate, ce, &st, 0)) + show_ce_entry(istate, tag_modified, ce); } } } @@ -686,7 +686,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) die("ls-files --with-tree is incompatible with -s or -u"); overlay_tree_on_index(&the_index, with_tree, max_prefix); } - show_files(&dir); + show_files(&the_index, &dir); if (show_resolve_undo) show_ru_info(&the_index); From 5306ccf9e9ec7204eef409e90b57ddf23b8f5ca6 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:07 -0700 Subject: [PATCH 511/566] ls-files: factor out debug info into a function Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 55d6f54fd8..c9307f9ef4 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -93,6 +93,19 @@ static void write_name(const char *name) strbuf_release(&full_name); } +static void print_debug(const struct cache_entry *ce) +{ + if (debug_mode) { + const struct stat_data *sd = &ce->ce_stat_data; + + printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec); + printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec); + printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino); + printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid); + printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags); + } +} + static void show_dir_entry(const char *tag, struct dir_entry *ent) { int len = max_prefix_len; @@ -279,15 +292,7 @@ static void show_ce_entry(const struct index_state *istate, } write_eolinfo(istate, ce, ce->name); write_name(ce->name); - if (debug_mode) { - const struct stat_data *sd = &ce->ce_stat_data; - - printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec); - printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec); - printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino); - printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid); - printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags); - } + print_debug(ce); } strbuf_release(&name); From a84f3e59ebde9e891275ef8325c432db6bdf950c Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Jun 2017 15:14:08 -0700 Subject: [PATCH 512/566] ls-files: factor out tag calculation Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index c9307f9ef4..cdc1cfdd26 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -93,6 +93,30 @@ static void write_name(const char *name) strbuf_release(&full_name); } +static const char *get_tag(const struct cache_entry *ce, const char *tag) +{ + static char alttag[4]; + + if (tag && *tag && show_valid_bit && (ce->ce_flags & CE_VALID)) { + memcpy(alttag, tag, 3); + + if (isalpha(tag[0])) { + alttag[0] = tolower(tag[0]); + } else if (tag[0] == '?') { + alttag[0] = '!'; + } else { + alttag[0] = 'v'; + alttag[1] = tag[0]; + alttag[2] = ' '; + alttag[3] = 0; + } + + tag = alttag; + } + + return tag; +} + static void print_debug(const struct cache_entry *ce) { if (debug_mode) { @@ -264,22 +288,7 @@ static void show_ce_entry(const struct index_state *istate, len, ps_matched, S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode))) { - if (tag && *tag && show_valid_bit && - (ce->ce_flags & CE_VALID)) { - static char alttag[4]; - memcpy(alttag, tag, 3); - if (isalpha(tag[0])) - alttag[0] = tolower(tag[0]); - else if (tag[0] == '?') - alttag[0] = '!'; - else { - alttag[0] = 'v'; - alttag[1] = tag[0]; - alttag[2] = ' '; - alttag[3] = 0; - } - tag = alttag; - } + tag = get_tag(ce, tag); if (!show_stage) { fputs(tag, stdout); From fd99e2bda0ca6a361ef03c04d6d7fdc7a9c40b78 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Jun 2017 13:30:16 -0700 Subject: [PATCH 513/566] Prepare for 2.13.2 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.13.2.txt | 37 +++++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes/2.13.2.txt diff --git a/Documentation/RelNotes/2.13.2.txt b/Documentation/RelNotes/2.13.2.txt new file mode 100644 index 0000000000..c8ba0fa16f --- /dev/null +++ b/Documentation/RelNotes/2.13.2.txt @@ -0,0 +1,37 @@ +Git v2.13.2 Release Notes +========================= + +Fixes since v2.13.1 +------------------- + + * The "collision detecting" SHA-1 implementation shipped with 2.13.1 + was still broken on some platforms. Update to the upstream code + again to take their fix. + + * "git checkout --recurse-submodules" did not quite work with a + submodule that itself has submodules. + + * Introduce the BUG() macro to improve die("BUG: ..."). + + * The "run-command" API implementation has been made more robust + against dead-locking in a threaded environment. + + * A recent update to t5545-push-options.sh started skipping all the + tests in the script when a web server testing is disabled or + unavailable, not just the ones that require a web server. Non HTTP + tests have been salvaged to always run in this script. + + * "git clean -d" used to clean directories that has ignored files, + even though the command should not lose ignored ones without "-x". + "git status --ignored" did not list ignored and untracked files + without "-uall". These have been corrected. + + * The timestamp of the index file is now taken after the file is + closed, to help Windows, on which a stale timestamp is reported by + fstat() on a file that is opened for writing and data was written + but not yet closed. + + * "git pull --rebase --autostash" didn't auto-stash when the local history + fast-forwards to the upstream. + +Also contains various documentation updates and code clean-ups. diff --git a/RelNotes b/RelNotes index 52c8e104fa..d75ac9dd09 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes/2.13.1.txt \ No newline at end of file +Documentation/RelNotes/2.13.2.txt \ No newline at end of file From a393b0a4ceb7ce4472370660dcea79cfff67a2a9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Jun 2017 13:52:29 -0700 Subject: [PATCH 514/566] Eighth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 35 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index e4ca72e50e..257f1e7d29 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -67,6 +67,9 @@ UI, Workflows & Features * Make the "indent" heuristics the default in "diff" and diff.indentHeuristics configuration variable an escape hatch for those who do no want it. + * Many commands learned to pay attention to submodule.recurse + configuration. + Performance, Internal Implementation, Development Support etc. @@ -129,6 +132,20 @@ Performance, Internal Implementation, Development Support etc. * The internal logic used in "git blame" has been libified to make it easier to use by cgit. + * Our code often opens a path to an optional file, to work on its + contents when we can successfully open it. We can ignore a failure + to open if such an optional file does not exist, but we do want to + report a failure in opening for other reasons (e.g. we got an I/O + error, or the file is there, but we lack the permission to open). + + The exact errors we need to ignore are ENOENT (obviously) and + ENOTDIR (less obvious). Instead of repeating comparison of errno + with these two constants, introduce a helper function to do so. + + * We often try to open a file for reading whose existence is + optional, and silently ignore errors from open/fopen; report such + errors if they are not due to missing files. + Also contains various documentation updates and code clean-ups. @@ -176,7 +193,6 @@ notes for details). * "git checkout --recurse-submodules" did not quite work with a submodule that itself has submodules. - (merge 218c883783 sb/checkout-recurse-submodules later to maint). * Plug some leaks and updates internal API used to implement the split index feature to make it easier to avoid such a leak in the @@ -203,7 +219,6 @@ notes for details). checked out with eol=LF even on Windows. * Introduce the BUG() macro to improve die("BUG: ..."). - (merge 3d7dd2d3b6 jk/bug-to-abort later to maint). * Clarify documentation for include.path and includeIf..path configuration variables. @@ -249,17 +264,14 @@ notes for details). * The "run-command" API implementation has been made more robust against dead-locking in a threaded environment. - (merge e3f43ce765 bw/forking-and-threading later to maint). * A recent update to t5545-push-options.sh started skipping all the tests in the script when a web server testing is disabled or unavailable, not just the ones that require a web server. Non HTTP tests have been salvaged to always run in this script. - (merge 2e397e4ddf jc/skip-test-in-the-middle later to maint). * "git send-email" now uses Net::SMTP::SSL, which is obsolete, only when needed. Recent versions of Net::SMTP can do TLS natively. - (merge bfbfc9a953 dk/send-email-avoid-net-smtp-ssl-when-able later to maint). * "foo\bar\baz" in "git fetch foo\bar\baz", even though there is no slashes in it, cannot be a nickname for a remote on Windows, as @@ -269,7 +281,6 @@ notes for details). even though the command should not lose ignored ones without "-x". "git status --ignored" did not list ignored and untracked files without "-uall". These have been corrected. - (merge 6b1db43109 sl/clean-d-ignored-fix later to maint). * The result from "git diff" that compares two blobs, e.g. "git diff $commit1:$path $commit2:$path", used to be shown with the full @@ -291,16 +302,12 @@ notes for details). closed, to help Windows, on which a stale timestamp is reported by fstat() on a file that is opened for writing and data was written but not yet closed. - (merge 9f41c7a6b3 jh/close-index-before-stat later to maint). * "git pull --rebase --autostash" didn't auto-stash when the local history fast-forwards to the upstream. - (merge f15e7cf5cc tb/pull-ff-rebase-autostash later to maint). + + * A flaky test has been corrected. + (merge 7c2115aa07 jk/pack-idx-corruption-safety later to maint). * Other minor doc, test and build updates and code cleanups. - (merge c5a9157393 jh/memihash-opt later to maint). - (merge 44e2ff09ce ab/t3070-test-dedup later to maint). - (merge 9ee4aa95db rf/completion-config-commit later to maint). - (merge ef4fe5617e jk/connect-symref-info-leak-fix later to maint). - (merge a56eea28c4 jk/drop-free-refspecs later to maint). - (merge 0c79cee697 ad/pull-remote-doc later to maint). + (merge 8ba74bfd7c jc/diff-tree-stale-comment later to maint). From da446109ff75120a9836d21653e12154e58f8c46 Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Wed, 14 Jun 2017 11:31:38 +0200 Subject: [PATCH 515/566] git-svn: document special options for commit-diff Some options specific for `git svn commit-diff` where not documented so far. Signed-off-by: Andreas Heiduk Signed-off-by: Eric Wong --- Documentation/git-svn.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index fba0b4eece..aa2aeabb60 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -459,6 +459,21 @@ Any other arguments are passed directly to 'git log' (URL) may be omitted if you are working from a 'git svn'-aware repository (that has been `init`-ed with 'git svn'). The -r option is required for this. ++ +The commit message is supplied either directly with the `-m` or `-F` +option, or indirectly from the tag or commit when the second tree-ish +denotes such an object, or it is requested by invoking an editor (see +`--edit` option below). + +-m ;; +--message=;; + Use the given `msg` as the commit message. This option + disables the `--edit` option. + +-F ;; +--file=;; + Take the commit message from the given file. This option + disables the `--edit` option. 'info':: Shows information about a file or directory similar to what From 532139940c98a98bce2001bb495d75dec3d88e4d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 14 Jun 2017 06:58:22 -0400 Subject: [PATCH 516/566] add: warn when adding an embedded repository It's an easy mistake to add a repository inside another repository, like: git clone $url git add . The resulting entry is a gitlink, but there's no matching .gitmodules entry. Trying to use "submodule init" (or clone with --recursive) doesn't do anything useful. Prior to v2.13, such an entry caused git-submodule to barf entirely. In v2.13, the entry is considered "inactive" and quietly ignored. Either way, no clone of your repository can do anything useful with the gitlink without the user manually adding the submodule config. In most cases, the user probably meant to either add a real submodule, or they forgot to put the embedded repository in their .gitignore file. Let's issue a warning when we see this case. There are a few things to note: - the warning will go in the git-add porcelain; anybody wanting to do low-level manipulation of the index is welcome to create whatever funny states they want. - we detect the case by looking for a newly added gitlink; updates via "git add submodule" are perfectly reasonable, and this avoids us having to investigate .gitmodules entirely - there's a command-line option to suppress the warning. This is needed for git-submodule itself (which adds the entry before adding any submodule config), but also provides a mechanism for other scripts doing submodule-like things. We could make this a hard error instead of a warning. However, we do add lots of sub-repos in our test suite. It's not _wrong_ to do so. It just creates a state where users may be surprised. Pointing them in the right direction with a gentle hint is probably the best option. There is a config knob that can disable the (long) hint. But I intentionally omitted a config knob to disable the warning entirely. Whether the warning is sensible or not is generally about context, not about the user's preferences. If there's a tool or workflow that adds gitlinks without matching .gitmodules, it should probably be taught about the new command-line option, rather than blanket-disabling the warning. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/config.txt | 3 +++ Documentation/git-add.txt | 7 ++++++ advice.c | 2 ++ advice.h | 1 + builtin/add.c | 46 ++++++++++++++++++++++++++++++++++- git-submodule.sh | 5 ++-- t/t7414-submodule-mistakes.sh | 37 ++++++++++++++++++++++++++++ 7 files changed, 98 insertions(+), 3 deletions(-) create mode 100755 t/t7414-submodule-mistakes.sh diff --git a/Documentation/config.txt b/Documentation/config.txt index dd4beec39d..e909239bc5 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -348,6 +348,9 @@ advice.*:: rmHints:: In case of failure in the output of linkgit:git-rm[1], show directions on how to proceed from the current state. + addEmbeddedRepo:: + Advice on what to do when you've accidentally added one + git repo inside of another. -- core.fileMode:: diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 7ed63dce0b..f4169fb1ec 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -165,6 +165,13 @@ for "git add --no-all ...", i.e. ignored removed files. be ignored, no matter if they are already present in the work tree or not. +--no-warn-embedded-repo:: + By default, `git add` will warn when adding an embedded + repository to the index without using `git submodule add` to + create an entry in `.gitmodules`. This option will suppress the + warning (e.g., if you are manually performing operations on + submodules). + --chmod=(+|-)x:: Override the executable bit of the added files. The executable bit is only changed in the index, the files on disk are left diff --git a/advice.c b/advice.c index b84ae4960f..e0611d52bd 100644 --- a/advice.c +++ b/advice.c @@ -15,6 +15,7 @@ int advice_detached_head = 1; int advice_set_upstream_failure = 1; int advice_object_name_warning = 1; int advice_rm_hints = 1; +int advice_add_embedded_repo = 1; static struct { const char *name; @@ -35,6 +36,7 @@ static struct { { "setupstreamfailure", &advice_set_upstream_failure }, { "objectnamewarning", &advice_object_name_warning }, { "rmhints", &advice_rm_hints }, + { "addembeddedrepo", &advice_add_embedded_repo }, /* make this an alias for backward compatibility */ { "pushnonfastforward", &advice_push_update_rejected } diff --git a/advice.h b/advice.h index b341a55ce7..c84a44531c 100644 --- a/advice.h +++ b/advice.h @@ -18,6 +18,7 @@ extern int advice_detached_head; extern int advice_set_upstream_failure; extern int advice_object_name_warning; extern int advice_rm_hints; +extern int advice_add_embedded_repo; int git_default_advice_config(const char *var, const char *value); __attribute__((format (printf, 1, 2))) diff --git a/builtin/add.c b/builtin/add.c index d9a2491e48..d7cab1bd85 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -249,6 +249,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n"); static int verbose, show_only, ignored_too, refresh_only; static int ignore_add_errors, intent_to_add, ignore_missing; +static int warn_on_embedded_repo = 1; #define ADDREMOVE_DEFAULT 1 static int addremove = ADDREMOVE_DEFAULT; @@ -282,6 +283,8 @@ static struct option builtin_add_options[] = { OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")), OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")), OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")), + OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo, + N_("warn when adding an embedded repository")), OPT_END(), }; @@ -295,6 +298,45 @@ static int add_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static const char embedded_advice[] = N_( +"You've added another git repository inside your current repository.\n" +"Clones of the outer repository will not contain the contents of\n" +"the embedded repository and will not know how to obtain it.\n" +"If you meant to add a submodule, use:\n" +"\n" +" git submodule add %s\n" +"\n" +"If you added this path by mistake, you can remove it from the\n" +"index with:\n" +"\n" +" git rm --cached %s\n" +"\n" +"See \"git help submodule\" for more information." +); + +static void check_embedded_repo(const char *path) +{ + struct strbuf name = STRBUF_INIT; + + if (!warn_on_embedded_repo) + return; + if (!ends_with(path, "/")) + return; + + /* Drop trailing slash for aesthetics */ + strbuf_addstr(&name, path); + strbuf_strip_suffix(&name, "/"); + + warning(_("adding embedded git repository: %s"), name.buf); + if (advice_add_embedded_repo) { + advise(embedded_advice, name.buf, name.buf); + /* there may be multiple entries; advise only once */ + advice_add_embedded_repo = 0; + } + + strbuf_release(&name); +} + static int add_files(struct dir_struct *dir, int flags) { int i, exit_status = 0; @@ -307,12 +349,14 @@ static int add_files(struct dir_struct *dir, int flags) exit_status = 1; } - for (i = 0; i < dir->nr; i++) + for (i = 0; i < dir->nr; i++) { + check_embedded_repo(dir->entries[i]->name); if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) { if (!ignore_add_errors) die(_("adding files failed")); exit_status = 1; } + } return exit_status; } diff --git a/git-submodule.sh b/git-submodule.sh index c0d0e9a4c6..e131760eec 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -213,7 +213,8 @@ cmd_add() die "$(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")" fi - if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1 + if test -z "$force" && + ! git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" > /dev/null 2>&1 then eval_gettextln "The following path is ignored by one of your .gitignore files: \$sm_path @@ -267,7 +268,7 @@ or you are unsure what this means choose another name with the '--name' option." fi git config submodule."$sm_name".url "$realrepo" - git add $force "$sm_path" || + git add --no-warn-embedded-repo $force "$sm_path" || die "$(eval_gettext "Failed to add submodule '\$sm_path'")" git config -f .gitmodules submodule."$sm_name".path "$sm_path" && diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh new file mode 100755 index 0000000000..f2e7df59cf --- /dev/null +++ b/t/t7414-submodule-mistakes.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +test_description='handling of common mistakes people may make with submodules' +. ./test-lib.sh + +test_expect_success 'create embedded repository' ' + git init embed && + test_commit -C embed one +' + +test_expect_success 'git-add on embedded repository warns' ' + test_when_finished "git rm --cached -f embed" && + git add embed 2>stderr && + test_i18ngrep warning stderr +' + +test_expect_success '--no-warn-embedded-repo suppresses warning' ' + test_when_finished "git rm --cached -f embed" && + git add --no-warn-embedded-repo embed 2>stderr && + test_i18ngrep ! warning stderr +' + +test_expect_success 'no warning when updating entry' ' + test_when_finished "git rm --cached -f embed" && + git add embed && + git -C embed commit --allow-empty -m two && + git add embed 2>stderr && + test_i18ngrep ! warning stderr +' + +test_expect_success 'submodule add does not warn' ' + test_when_finished "git rm -rf submodule .gitmodules" && + git submodule add ./embed submodule 2>stderr && + test_i18ngrep ! warning stderr +' + +test_done From 17f2f88c9c9e0015cba6d962dc6d9e2329ddf713 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 14 Jun 2017 06:58:25 -0400 Subject: [PATCH 517/566] t: move "git add submodule" into test blocks Some submodule tests do some setup outside of a test_expect block. This is bad because we won't actually check the outcome of those commands. But it's doubly so because "git add submodule" now produces a warning to stderr, which is not suppressed by the test scripts in non-verbose mode. This patch does the minimal to fix the annoying warnings. All three of these scripts could use more cleanup of related setup. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t4041-diff-submodule-option.sh | 8 +++++--- t/t4060-diff-submodule-option-diff-format.sh | 8 +++++--- t/t7401-submodule-summary.sh | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh index 2d9731b52d..058ee0829d 100755 --- a/t/t4041-diff-submodule-option.sh +++ b/t/t4041-diff-submodule-option.sh @@ -430,9 +430,11 @@ test_expect_success 'deleted submodule' ' test_cmp expected actual ' -test_create_repo sm2 && -head7=$(add_file sm2 foo8 foo9) && -git add sm2 +test_expect_success 'create second submodule' ' + test_create_repo sm2 && + head7=$(add_file sm2 foo8 foo9) && + git add sm2 +' test_expect_success 'multiple submodules' ' git diff-index -p --submodule=log HEAD >actual && diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh index 33ec26d755..4b168d0ed7 100755 --- a/t/t4060-diff-submodule-option-diff-format.sh +++ b/t/t4060-diff-submodule-option-diff-format.sh @@ -643,9 +643,11 @@ test_expect_success 'deleted submodule' ' test_cmp expected actual ' -test_create_repo sm2 && -head7=$(add_file sm2 foo8 foo9) && -git add sm2 +test_expect_success 'create second submodule' ' + test_create_repo sm2 && + head7=$(add_file sm2 foo8 foo9) && + git add sm2 +' test_expect_success 'multiple submodules' ' git diff-index -p --submodule=diff HEAD >actual && diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 366746f0d4..4e4c455502 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -241,9 +241,11 @@ EOF test_cmp expected actual " -test_create_repo sm2 && -head7=$(add_file sm2 foo8 foo9) && -git add sm2 +test_expect_success 'create second submodule' ' + test_create_repo sm2 && + head7=$(add_file sm2 foo8 foo9) && + git add sm2 +' test_expect_success 'multiple submodules' " git submodule summary >actual && From 773a88914f7b951cb33b27b12b4b715b66451a07 Mon Sep 17 00:00:00 2001 From: Andreas Heiduk Date: Wed, 14 Jun 2017 08:51:39 +0200 Subject: [PATCH 518/566] doc: do not use `rm .git/index` when normalizing line endings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When illustrating how to normalize the line endings, the documentation in gitattributes tells the user to `rm .git/index`. This is incorrect for two reasons: - Users shouldn't be instructed to mess around with the internal implementation of Git using raw file system tools like `rm`. - Within a submodule or an additional working tree `.git` is just a file containing a `gitdir: ` pointer into the real `.git` directory. Therefore `rm .git/index` does not work. The purpose of the `rm .git/index` instruction is to remove all entries from the index without touching the working tree. The way to do this with Git is to use `read-tree --empty`. Signed-off-by: Andreas Heiduk Helped-by: Junio C Hamano Helped-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 4736483865..2a2d7e2a4d 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -229,7 +229,7 @@ From a clean working directory: ------------------------------------------------- $ echo "* text=auto" >.gitattributes -$ rm .git/index # Remove the index to re-scan the working directory +$ git read-tree --empty # Clean index, force re-scan of working directory $ git add . $ git status # Show files that will be normalized $ git commit -m "Introduce end-of-line normalization" From fe9e2aefd4adac63821d0c007effcc8087e32ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Wed, 14 Jun 2017 20:24:25 +0200 Subject: [PATCH 519/566] pretty: recalculate duplicate short hashes b9c6232138 (--format=pretty: avoid calculating expensive expansions twice) optimized adding short hashes multiple times by using the fact that the output strbuf was only ever simply appended to and copying the added string from the previous run. That prerequisite is no longer given; we now have modfiers like %< and %+ that can cause the cache to lose track of the correct offsets. Remove it. Reported-by: Michael Giuffrida Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- pretty.c | 32 -------------------------------- strbuf.c | 7 ------- strbuf.h | 6 ------ 3 files changed, 45 deletions(-) diff --git a/pretty.c b/pretty.c index 0c31495240..37b37d0f9d 100644 --- a/pretty.c +++ b/pretty.c @@ -782,29 +782,9 @@ struct format_commit_context { size_t body_off; /* The following ones are relative to the result struct strbuf. */ - struct chunk abbrev_commit_hash; - struct chunk abbrev_tree_hash; - struct chunk abbrev_parent_hashes; size_t wrap_start; }; -static int add_again(struct strbuf *sb, struct chunk *chunk) -{ - if (chunk->len) { - strbuf_adddup(sb, chunk->off, chunk->len); - return 1; - } - - /* - * We haven't seen this chunk before. Our caller is surely - * going to add it the hard way now. Remember the most likely - * start of the to-be-added chunk: the current end of the - * struct strbuf. - */ - chunk->off = sb->len; - return 0; -} - static void parse_commit_header(struct format_commit_context *context) { const char *msg = context->message; @@ -1136,24 +1116,16 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ return 1; case 'h': /* abbreviated commit hash */ strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT)); - if (add_again(sb, &c->abbrev_commit_hash)) { - strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET)); - return 1; - } strbuf_add_unique_abbrev(sb, commit->object.oid.hash, c->pretty_ctx->abbrev); strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET)); - c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off; return 1; case 'T': /* tree hash */ strbuf_addstr(sb, oid_to_hex(&commit->tree->object.oid)); return 1; case 't': /* abbreviated tree hash */ - if (add_again(sb, &c->abbrev_tree_hash)) - return 1; strbuf_add_unique_abbrev(sb, commit->tree->object.oid.hash, c->pretty_ctx->abbrev); - c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off; return 1; case 'P': /* parent hashes */ for (p = commit->parents; p; p = p->next) { @@ -1163,16 +1135,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ } return 1; case 'p': /* abbreviated parent hashes */ - if (add_again(sb, &c->abbrev_parent_hashes)) - return 1; for (p = commit->parents; p; p = p->next) { if (p != commit->parents) strbuf_addch(sb, ' '); strbuf_add_unique_abbrev(sb, p->item->object.oid.hash, c->pretty_ctx->abbrev); } - c->abbrev_parent_hashes.len = sb->len - - c->abbrev_parent_hashes.off; return 1; case 'm': /* left/right/bottom */ strbuf_addstr(sb, get_revision_mark(NULL, commit)); diff --git a/strbuf.c b/strbuf.c index b839be491b..df2c113f21 100644 --- a/strbuf.c +++ b/strbuf.c @@ -204,13 +204,6 @@ void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) strbuf_setlen(sb, sb->len + sb2->len); } -void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) -{ - strbuf_grow(sb, len); - memcpy(sb->buf + sb->len, sb->buf + pos, len); - strbuf_setlen(sb, sb->len + len); -} - void strbuf_addchars(struct strbuf *sb, int c, size_t n) { strbuf_grow(sb, n); diff --git a/strbuf.h b/strbuf.h index ba8d5f1d46..ca4eff8bff 100644 --- a/strbuf.h +++ b/strbuf.h @@ -265,12 +265,6 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) */ extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2); -/** - * Copy part of the buffer from a given position till a given length to the - * end of the buffer. - */ -extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); - /** * This function can be used to expand a format string containing * placeholders. To that end, it parses the string and calls the specified From 0c977dbc8180892af42d7ab9235fd3e51d6c4078 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 15 Jun 2017 12:30:55 -0400 Subject: [PATCH 520/566] diff-highlight: split code into module The diff-so-fancy project is also written in perl, and most of its users pipe diffs through both diff-highlight and diff-so-fancy. It would be nice if this could be done in a single script. So let's pull most of diff-highlight's code into its own module which can be used by diff-so-fancy. In addition, we'll abstract a few basic items like reading from stdio so that a script using the module can do more processing before or after diff-highlight handles the lines. See the README update for more details. One small downside is that the diff-highlight script must now be built using the Makefile. There are ways around this, but it quickly gets into perl arcana. Let's go with the simple solution. As a bonus, our Makefile now respects the PERL_PATH variable if it is set. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/diff-highlight/.gitignore | 2 + .../{diff-highlight => DiffHighlight.pm} | 40 +++++++++++-------- contrib/diff-highlight/Makefile | 21 ++++++++-- contrib/diff-highlight/README | 30 ++++++++++++++ contrib/diff-highlight/diff-highlight.perl | 8 ++++ 5 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 contrib/diff-highlight/.gitignore rename contrib/diff-highlight/{diff-highlight => DiffHighlight.pm} (91%) mode change 100755 => 100644 create mode 100644 contrib/diff-highlight/diff-highlight.perl diff --git a/contrib/diff-highlight/.gitignore b/contrib/diff-highlight/.gitignore new file mode 100644 index 0000000000..c07454824e --- /dev/null +++ b/contrib/diff-highlight/.gitignore @@ -0,0 +1,2 @@ +shebang.perl +diff-highlight diff --git a/contrib/diff-highlight/diff-highlight b/contrib/diff-highlight/DiffHighlight.pm old mode 100755 new mode 100644 similarity index 91% rename from contrib/diff-highlight/diff-highlight rename to contrib/diff-highlight/DiffHighlight.pm index 81bd8040e3..663992e530 --- a/contrib/diff-highlight/diff-highlight +++ b/contrib/diff-highlight/DiffHighlight.pm @@ -1,4 +1,4 @@ -#!/usr/bin/perl +package DiffHighlight; use 5.008; use warnings FATAL => 'all'; @@ -29,13 +29,14 @@ my @removed; my @added; my $in_hunk; -# Some scripts may not realize that SIGPIPE is being ignored when launching the -# pager--for instance scripts written in Python. -$SIG{PIPE} = 'DEFAULT'; +our $line_cb = sub { print @_ }; +our $flush_cb = sub { local $| = 1 }; + +sub handle_line { + local $_ = shift; -while (<>) { if (!$in_hunk) { - print; + $line_cb->($_); $in_hunk = /^$GRAPH*$COLOR*\@\@ /; } elsif (/^$GRAPH*$COLOR*-/) { @@ -49,7 +50,7 @@ while (<>) { @removed = (); @added = (); - print; + $line_cb->($_); $in_hunk = /^$GRAPH*$COLOR*[\@ ]/; } @@ -62,15 +63,22 @@ while (<>) { # place to flush. Flushing on a blank line is a heuristic that # happens to match git-log output. if (!length) { - local $| = 1; + $flush_cb->(); } } -# Flush any queued hunk (this can happen when there is no trailing context in -# the final diff of the input). -show_hunk(\@removed, \@added); +sub flush { + # Flush any queued hunk (this can happen when there is no trailing + # context in the final diff of the input). + show_hunk(\@removed, \@added); +} -exit 0; +sub highlight_stdin { + while () { + handle_line($_); + } + flush(); +} # Ideally we would feed the default as a human-readable color to # git-config as the fallback value. But diff-highlight does @@ -88,7 +96,7 @@ sub show_hunk { # If one side is empty, then there is nothing to compare or highlight. if (!@$a || !@$b) { - print @$a, @$b; + $line_cb->(@$a, @$b); return; } @@ -97,17 +105,17 @@ sub show_hunk { # stupid, and only handle multi-line hunks that remove and add the same # number of lines. if (@$a != @$b) { - print @$a, @$b; + $line_cb->(@$a, @$b); return; } my @queue; for (my $i = 0; $i < @$a; $i++) { my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]); - print $rm; + $line_cb->($rm); push @queue, $add; } - print @queue; + $line_cb->(@queue); } sub highlight_pair { diff --git a/contrib/diff-highlight/Makefile b/contrib/diff-highlight/Makefile index 9018724524..fbf5c58249 100644 --- a/contrib/diff-highlight/Makefile +++ b/contrib/diff-highlight/Makefile @@ -1,5 +1,20 @@ -# nothing to build -all: +all: diff-highlight -test: +PERL_PATH = /usr/bin/perl +-include ../../config.mak + +PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) + +diff-highlight: shebang.perl DiffHighlight.pm diff-highlight.perl + cat $^ >$@+ + chmod +x $@+ + mv $@+ $@ + +shebang.perl: FORCE + @echo '#!$(PERL_PATH_SQ)' >$@+ + @cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@ + +test: all $(MAKE) -C t + +.PHONY: FORCE diff --git a/contrib/diff-highlight/README b/contrib/diff-highlight/README index 836b97a730..d4c2343175 100644 --- a/contrib/diff-highlight/README +++ b/contrib/diff-highlight/README @@ -99,6 +99,36 @@ newHighlight = "black #aaffaa" --------------------------------------------- +Using diff-highlight as a module +-------------------------------- + +If you want to pre- or post- process the highlighted lines as part of +another perl script, you can use the DiffHighlight module. You can +either "require" it or just cat the module together with your script (to +avoid run-time dependencies). + +Your script may set up one or more of the following variables: + + - $DiffHighlight::line_cb - this should point to a function which is + called whenever DiffHighlight has lines (which may contain + highlights) to output. The default function prints each line to + stdout. Note that the function may be called with multiple lines. + + - $DiffHighlight::flush_cb - this should point to a function which + flushes the output (because DiffHighlight believes it has completed + processing a logical chunk of input). The default function flushes + stdout. + +The script may then feed lines, one at a time, to DiffHighlight::handle_line(). +When lines are done processing, they will be fed to $line_cb. Note that +DiffHighlight may queue up many input lines (to analyze a whole hunk) +before calling $line_cb. After providing all lines, call +DiffHighlight::flush() to flush any unprocessed lines. + +If you just want to process stdin, DiffHighlight::highlight_stdin() +is a convenience helper which will loop and flush for you. + + Bugs ---- diff --git a/contrib/diff-highlight/diff-highlight.perl b/contrib/diff-highlight/diff-highlight.perl new file mode 100644 index 0000000000..9b3e9c1f4d --- /dev/null +++ b/contrib/diff-highlight/diff-highlight.perl @@ -0,0 +1,8 @@ +package main; + +# Some scripts may not realize that SIGPIPE is being ignored when launching the +# pager--for instance scripts written in Python. +$SIG{PIPE} = 'DEFAULT'; + +DiffHighlight::highlight_stdin(); +exit 0; From 69743f9b4f8411853e347fbd392221552b43fd12 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 Jun 2017 13:35:26 +0200 Subject: [PATCH 521/566] discover_git_directory(): avoid setting invalid git_dir When discovering a .git/ directory, we take pains to ensure that its repository format version matches Git's expectations, and we return NULL otherwise. However, we still appended the invalid path to the strbuf passed as argument. Let's just reset the strbuf to the state before we appended the .git/ directory that was eventually rejected. There is another early return path in that function, when setup_git_directory_gently_1() returns GIT_DIR_NONE or an error. In that case, the gitdir parameter has not been touched, therefore there is no need for an equivalent change in that code path. Signed-off-by: Johannes Schindelin Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.c b/setup.c index 0309c27821..ba3241bf02 100644 --- a/setup.c +++ b/setup.c @@ -977,6 +977,7 @@ const char *discover_git_directory(struct strbuf *gitdir) warning("ignoring git dir '%s': %s", gitdir->buf + gitdir_offset, err.buf); strbuf_release(&err); + strbuf_setlen(gitdir, gitdir_offset); return NULL; } From e2e142510762712b4b005dca6c7a9676f93a3278 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 Jun 2017 13:35:46 +0200 Subject: [PATCH 522/566] config: report correct line number upon error When get_value() parses a key/value pair, it is possible that the line number is decreased (because the \n has been consumed already) before the key/value pair is passed to the callback function, to allow for the correct line to be attributed in case of an error. However, when git_parse_source() asks get_value() to parse the key/value pair, the error reporting is performed *after* get_value() returns. Which means that we have to be careful not to increase the line number in get_value() after the callback function returned an error. Signed-off-by: Johannes Schindelin Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 3 ++- t/t1300-repo-config.sh | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index b4a3205da3..3df7515db2 100644 --- a/config.c +++ b/config.c @@ -588,7 +588,8 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name) */ cf->linenr--; ret = fn(name->buf, value, data); - cf->linenr++; + if (ret >= 0) + cf->linenr++; return ret; } diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index afcca0d52c..f664bfc671 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -703,6 +703,12 @@ test_expect_success 'invalid unit' ' test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual ' +test_expect_success 'line number is reported correctly' ' + printf "[bool]\n\tvar\n" >invalid && + test_must_fail git config -f invalid --path bool.var 2>actual && + test_i18ngrep "line 2" actual +' + test_expect_success 'invalid stdin config' ' echo "[broken" | test_must_fail git config --list --file - >output 2>&1 && test_i18ngrep "bad config line 1 in standard input" output From 659fef199fb52d4a5cb24f1dfd2b272980fc5038 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 Jun 2017 13:35:50 +0200 Subject: [PATCH 523/566] help: use early config when autocorrecting aliases Git has this feature which suggests similar commands (including aliases) in case the user specified an unknown command. This feature currently relies on a side effect of the way we expand aliases right now: when a command is not a builtin, we use the regular config machinery (meaning: discovering the .git/ directory and initializing global state such as the config cache) to see whether the command refers to an alias. However, we will change the way aliases are expanded in the next commits, to use the early config instead. That means that the autocorrect feature can no longer discover the available aliases by looking at the config cache (because it has not yet been initialized). So let's just use the early config machinery instead. This is slightly less performant than the previous way, as the early config is used *twice*: once to see whether the command refers to an alias, and then to see what aliases are most similar. However, this is hardly a performance-critical code path, so performance is less important here. Signed-off-by: Johannes Schindelin Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- help.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help.c b/help.c index bc6cd19cf3..91ec8ab36f 100644 --- a/help.c +++ b/help.c @@ -330,7 +330,7 @@ const char *help_unknown_cmd(const char *cmd) memset(&other_cmds, 0, sizeof(other_cmds)); memset(&aliases, 0, sizeof(aliases)); - git_config(git_unknown_cmd_config, NULL); + read_early_config(git_unknown_cmd_config, NULL); load_command_list("git-", &main_cmds, &other_cmds); From e4feff4898f4705cc8df50e3dc152ed45ef06db6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 Jun 2017 13:35:53 +0200 Subject: [PATCH 524/566] t1308: relax the test verifying that empty alias values are disallowed We are about to change the way aliases are expanded, to use the early config machinery. This machinery reports errors in a slightly different manner than the cached config machinery. Let's not get hung up by the precise wording of the message mentioning the line number. It is really sufficient to verify that all the relevant information is given to the user. Signed-off-by: Johannes Schindelin Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- t/t1308-config-set.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index ff50960cca..69a0aa56d6 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -215,7 +215,9 @@ test_expect_success 'check line errors for malformed values' ' br EOF test_expect_code 128 git br 2>result && - test_i18ngrep "fatal: .*alias\.br.*\.git/config.*line 2" result + test_i18ngrep "missing value for .alias\.br" result && + test_i18ngrep "fatal: .*\.git/config" result && + test_i18ngrep "fatal: .*line 2" result ' test_expect_success 'error on modifying repo config without repo' ' From 3f9c5dfb7118256747de5efbaa4b5cd3f0e02331 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 Jun 2017 13:35:56 +0200 Subject: [PATCH 525/566] t7006: demonstrate a problem with aliases in subdirectories When expanding aliases, the git_dir is set during the alias expansion (by virtue of running setup_git_directory_gently()). This git_dir may be relative to the current working directory, and indeed often is simply ".git/". When the alias expands to a shell command, we restore the original working directory, though, yet we do not reset git_dir. As a consequence, subsequent read_early_config() runs will mistake the git_dir to be populated properly and not find the correct config. Demonstrate this problem by adding a test case. Signed-off-by: Johannes Schindelin Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- t/t7006-pager.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh index 4f3794d415..83881ec3a0 100755 --- a/t/t7006-pager.sh +++ b/t/t7006-pager.sh @@ -391,6 +391,17 @@ test_expect_success TTY 'core.pager in repo config works and retains cwd' ' ) ' +test_expect_failure TTY 'core.pager is found via alias in subdirectory' ' + sane_unset GIT_PAGER && + test_config core.pager "cat >via-alias" && + ( + cd sub && + rm -f via-alias && + test_terminal git -c alias.r="-p rev-parse" r HEAD && + test_path_is_file via-alias + ) +' + test_doesnt_paginate expect_failure test_must_fail 'git -p nonsense' test_pager_choices 'git shortlog' From a9bcf6586d1a4888aea91553d73cda20494b8335 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 Jun 2017 13:36:00 +0200 Subject: [PATCH 526/566] alias: use the early config machinery to expand aliases Instead of discovering the .git/ directory, reading the config and then trying to painstakingly reset all the global state if we did not find a matching alias, let's use the early config machinery instead. It may look like unnecessary work to discover the .git/ directory in the early config machinery and then call setup_git_directory_gently() in the case of a shell alias, repeating the very same discovery *again*. However, we have to do this as the early config machinery takes pains *not* to touch any global state, while shell aliases expect a possibly changed working directory and at least the GIT_PREFIX and GIT_DIR variables to be set. This change also fixes a known issue where Git tried to read the pager config from an incorrect path in a subdirectory of a Git worktree if an alias expanded to a shell command. Signed-off-by: Johannes Schindelin Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- alias.c | 28 ++++++++++++++++++------ git.c | 55 ++++-------------------------------------------- t/t7006-pager.sh | 2 +- 3 files changed, 26 insertions(+), 59 deletions(-) diff --git a/alias.c b/alias.c index 3b90397a99..0526304661 100644 --- a/alias.c +++ b/alias.c @@ -1,14 +1,28 @@ #include "cache.h" +struct config_alias_data { + const char *alias; + char *v; +}; + +static int config_alias_cb(const char *key, const char *value, void *d) +{ + struct config_alias_data *data = d; + const char *p; + + if (skip_prefix(key, "alias.", &p) && !strcmp(p, data->alias)) + return git_config_string((const char **)&data->v, key, value); + + return 0; +} + char *alias_lookup(const char *alias) { - char *v = NULL; - struct strbuf key = STRBUF_INIT; - strbuf_addf(&key, "alias.%s", alias); - if (git_config_key_is_valid(key.buf)) - git_config_get_string(key.buf, &v); - strbuf_release(&key); - return v; + struct config_alias_data data = { alias, NULL }; + + read_early_config(config_alias_cb, &data); + + return data.v; } #define SPLIT_CMDLINE_BAD_ENDING 1 diff --git a/git.c b/git.c index 8ff44f081d..58ef570294 100644 --- a/git.c +++ b/git.c @@ -16,50 +16,6 @@ const char git_more_info_string[] = "to read about a specific subcommand or concept."); static int use_pager = -1; -static char *orig_cwd; -static const char *env_names[] = { - GIT_DIR_ENVIRONMENT, - GIT_WORK_TREE_ENVIRONMENT, - GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, - GIT_PREFIX_ENVIRONMENT -}; -static char *orig_env[4]; -static int save_restore_env_balance; - -static void save_env_before_alias(void) -{ - int i; - - assert(save_restore_env_balance == 0); - save_restore_env_balance = 1; - orig_cwd = xgetcwd(); - for (i = 0; i < ARRAY_SIZE(env_names); i++) { - orig_env[i] = getenv(env_names[i]); - orig_env[i] = xstrdup_or_null(orig_env[i]); - } -} - -static void restore_env(int external_alias) -{ - int i; - - assert(save_restore_env_balance == 1); - save_restore_env_balance = 0; - if (!external_alias && orig_cwd && chdir(orig_cwd)) - die_errno("could not move to %s", orig_cwd); - free(orig_cwd); - for (i = 0; i < ARRAY_SIZE(env_names); i++) { - if (external_alias && - !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT)) - continue; - if (orig_env[i]) { - setenv(env_names[i], orig_env[i], 1); - free(orig_env[i]); - } else { - unsetenv(env_names[i]); - } - } -} static void commit_pager_choice(void) { switch (use_pager) { @@ -250,19 +206,18 @@ static int handle_alias(int *argcp, const char ***argv) const char **new_argv; const char *alias_command; char *alias_string; - int unused_nongit; - - save_env_before_alias(); - setup_git_directory_gently(&unused_nongit); alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { struct child_process child = CHILD_PROCESS_INIT; + int nongit_ok; + + /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */ + setup_git_directory_gently(&nongit_ok); commit_pager_choice(); - restore_env(1); child.use_shell = 1; argv_array_push(&child.args, alias_string + 1); @@ -308,8 +263,6 @@ static int handle_alias(int *argcp, const char ***argv) ret = 1; } - restore_env(0); - errno = saved_errno; return ret; diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh index 83881ec3a0..20b4d83c28 100755 --- a/t/t7006-pager.sh +++ b/t/t7006-pager.sh @@ -391,7 +391,7 @@ test_expect_success TTY 'core.pager in repo config works and retains cwd' ' ) ' -test_expect_failure TTY 'core.pager is found via alias in subdirectory' ' +test_expect_success TTY 'core.pager is found via alias in subdirectory' ' sane_unset GIT_PAGER && test_config core.pager "cat >via-alias" && ( From e67a57fc5187f93ced9631edf80fc09eefb3fed9 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Jun 2017 11:07:34 -0700 Subject: [PATCH 527/566] config: create config.h Move all config related declarations from cache.h to a new config.h header file. This makes cache.h smaller and allows for the opportunity in a following patch to only include config.h when needed. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- cache.h | 190 +---------------------------------------------------- config.h | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 189 deletions(-) create mode 100644 config.h diff --git a/cache.h b/cache.h index e1f0e182ad..67efb47a14 100644 --- a/cache.h +++ b/cache.h @@ -11,6 +11,7 @@ #include "string-list.h" #include "pack-revindex.h" #include "hash.h" +#include "config.h" #ifndef platform_SHA_CTX /* @@ -1865,188 +1866,9 @@ extern int packed_object_info(struct packed_git *pack, off_t offset, struct obje /* Dumb servers support */ extern int update_server_info(int); -/* git_config_parse_key() returns these negated: */ -#define CONFIG_INVALID_KEY 1 -#define CONFIG_NO_SECTION_OR_NAME 2 -/* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */ -#define CONFIG_NO_LOCK -1 -#define CONFIG_INVALID_FILE 3 -#define CONFIG_NO_WRITE 4 -#define CONFIG_NOTHING_SET 5 -#define CONFIG_INVALID_PATTERN 6 -#define CONFIG_GENERIC_ERROR 7 - -#define CONFIG_REGEX_NONE ((void *)1) - -struct git_config_source { - unsigned int use_stdin:1; - const char *file; - const char *blob; -}; - -enum config_origin_type { - CONFIG_ORIGIN_BLOB, - CONFIG_ORIGIN_FILE, - CONFIG_ORIGIN_STDIN, - CONFIG_ORIGIN_SUBMODULE_BLOB, - CONFIG_ORIGIN_CMDLINE -}; - -struct config_options { - unsigned int respect_includes : 1; - const char *git_dir; -}; - -typedef int (*config_fn_t)(const char *, const char *, void *); -extern int git_default_config(const char *, const char *, void *); -extern int git_config_from_file(config_fn_t fn, const char *, void *); -extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type, - const char *name, const char *buf, size_t len, void *data); -extern int git_config_from_blob_sha1(config_fn_t fn, const char *name, - const unsigned char *sha1, void *data); -extern void git_config_push_parameter(const char *text); -extern int git_config_from_parameters(config_fn_t fn, void *data); -extern void read_early_config(config_fn_t cb, void *data); -extern void git_config(config_fn_t fn, void *); -extern int git_config_with_options(config_fn_t fn, void *, - struct git_config_source *config_source, - const struct config_options *opts); -extern int git_parse_ulong(const char *, unsigned long *); -extern int git_parse_maybe_bool(const char *); -extern int git_config_int(const char *, const char *); -extern int64_t git_config_int64(const char *, const char *); -extern unsigned long git_config_ulong(const char *, const char *); -extern ssize_t git_config_ssize_t(const char *, const char *); -extern int git_config_bool_or_int(const char *, const char *, int *); -extern int git_config_bool(const char *, const char *); -extern int git_config_maybe_bool(const char *, const char *); -extern int git_config_string(const char **, const char *, const char *); -extern int git_config_pathname(const char **, const char *, const char *); -extern int git_config_set_in_file_gently(const char *, const char *, const char *); -extern void git_config_set_in_file(const char *, const char *, const char *); -extern int git_config_set_gently(const char *, const char *); -extern void git_config_set(const char *, const char *); -extern int git_config_parse_key(const char *, char **, int *); -extern int git_config_key_is_valid(const char *key); -extern int git_config_set_multivar_gently(const char *, const char *, const char *, int); -extern void git_config_set_multivar(const char *, const char *, const char *, int); -extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int); -extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int); -extern int git_config_rename_section(const char *, const char *); -extern int git_config_rename_section_in_file(const char *, const char *, const char *); -extern const char *git_etc_gitconfig(void); -extern int git_env_bool(const char *, int); -extern unsigned long git_env_ulong(const char *, unsigned long); -extern int git_config_system(void); -extern int config_error_nonbool(const char *); -#if defined(__GNUC__) -#define config_error_nonbool(s) (config_error_nonbool(s), const_error()) -#endif extern const char *get_log_output_encoding(void); extern const char *get_commit_output_encoding(void); -extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data); - -enum config_scope { - CONFIG_SCOPE_UNKNOWN = 0, - CONFIG_SCOPE_SYSTEM, - CONFIG_SCOPE_GLOBAL, - CONFIG_SCOPE_REPO, - CONFIG_SCOPE_CMDLINE, -}; - -extern enum config_scope current_config_scope(void); -extern const char *current_config_origin_type(void); -extern const char *current_config_name(void); - -struct config_include_data { - int depth; - config_fn_t fn; - void *data; - const struct config_options *opts; -}; -#define CONFIG_INCLUDE_INIT { 0 } -extern int git_config_include(const char *name, const char *value, void *data); - -/* - * Match and parse a config key of the form: - * - * section.(subsection.)?key - * - * (i.e., what gets handed to a config_fn_t). The caller provides the section; - * we return -1 if it does not match, 0 otherwise. The subsection and key - * out-parameters are filled by the function (and *subsection is NULL if it is - * missing). - * - * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if - * there is no subsection at all. - */ -extern int parse_config_key(const char *var, - const char *section, - const char **subsection, int *subsection_len, - const char **key); - -struct config_set_element { - struct hashmap_entry ent; - char *key; - struct string_list value_list; -}; - -struct configset_list_item { - struct config_set_element *e; - int value_index; -}; - -/* - * the contents of the list are ordered according to their - * position in the config files and order of parsing the files. - * (i.e. key-value pair at the last position of .git/config will - * be at the last item of the list) - */ -struct configset_list { - struct configset_list_item *items; - unsigned int nr, alloc; -}; - -struct config_set { - struct hashmap config_hash; - int hash_initialized; - struct configset_list list; -}; - -extern void git_configset_init(struct config_set *cs); -extern int git_configset_add_file(struct config_set *cs, const char *filename); -extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value); -extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key); -extern void git_configset_clear(struct config_set *cs); -extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest); -extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest); -extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest); -extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest); -extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest); -extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest); -extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest); -extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest); - -extern int git_config_get_value(const char *key, const char **value); -extern const struct string_list *git_config_get_value_multi(const char *key); -extern void git_config_clear(void); -extern void git_config_iter(config_fn_t fn, void *data); -extern int git_config_get_string_const(const char *key, const char **dest); -extern int git_config_get_string(const char *key, char **dest); -extern int git_config_get_int(const char *key, int *dest); -extern int git_config_get_ulong(const char *key, unsigned long *dest); -extern int git_config_get_bool(const char *key, int *dest); -extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest); -extern int git_config_get_maybe_bool(const char *key, int *dest); -extern int git_config_get_pathname(const char *key, const char **dest); -extern int git_config_get_untracked_cache(void); -extern int git_config_get_split_index(void); -extern int git_config_get_max_percent_split_change(void); - -/* This dies if the configured or default date is in the future */ -extern int git_config_get_expiry(const char *key, const char **output); - /* * This is a hack for test programs like test-dump-untracked-cache to * ensure that they do not modify the untracked cache when reading it. @@ -2054,16 +1876,6 @@ extern int git_config_get_expiry(const char *key, const char **output); */ extern int ignore_untracked_cache_config; -struct key_value_info { - const char *filename; - int linenr; - enum config_origin_type origin_type; - enum config_scope scope; -}; - -extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3))); -extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr); - extern int committer_ident_sufficiently_given(void); extern int author_ident_sufficiently_given(void); diff --git a/config.h b/config.h new file mode 100644 index 0000000000..f7f8b66c59 --- /dev/null +++ b/config.h @@ -0,0 +1,194 @@ +#ifndef CONFIG_H +#define CONFIG_H + +/* git_config_parse_key() returns these negated: */ +#define CONFIG_INVALID_KEY 1 +#define CONFIG_NO_SECTION_OR_NAME 2 +/* git_config_set_gently(), git_config_set_multivar_gently() return the above or these: */ +#define CONFIG_NO_LOCK -1 +#define CONFIG_INVALID_FILE 3 +#define CONFIG_NO_WRITE 4 +#define CONFIG_NOTHING_SET 5 +#define CONFIG_INVALID_PATTERN 6 +#define CONFIG_GENERIC_ERROR 7 + +#define CONFIG_REGEX_NONE ((void *)1) + +struct git_config_source { + unsigned int use_stdin:1; + const char *file; + const char *blob; +}; + +enum config_origin_type { + CONFIG_ORIGIN_BLOB, + CONFIG_ORIGIN_FILE, + CONFIG_ORIGIN_STDIN, + CONFIG_ORIGIN_SUBMODULE_BLOB, + CONFIG_ORIGIN_CMDLINE +}; + +struct config_options { + unsigned int respect_includes : 1; + const char *git_dir; +}; + +typedef int (*config_fn_t)(const char *, const char *, void *); +extern int git_default_config(const char *, const char *, void *); +extern int git_config_from_file(config_fn_t fn, const char *, void *); +extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type, + const char *name, const char *buf, size_t len, void *data); +extern int git_config_from_blob_sha1(config_fn_t fn, const char *name, + const unsigned char *sha1, void *data); +extern void git_config_push_parameter(const char *text); +extern int git_config_from_parameters(config_fn_t fn, void *data); +extern void read_early_config(config_fn_t cb, void *data); +extern void git_config(config_fn_t fn, void *); +extern int git_config_with_options(config_fn_t fn, void *, + struct git_config_source *config_source, + const struct config_options *opts); +extern int git_parse_ulong(const char *, unsigned long *); +extern int git_parse_maybe_bool(const char *); +extern int git_config_int(const char *, const char *); +extern int64_t git_config_int64(const char *, const char *); +extern unsigned long git_config_ulong(const char *, const char *); +extern ssize_t git_config_ssize_t(const char *, const char *); +extern int git_config_bool_or_int(const char *, const char *, int *); +extern int git_config_bool(const char *, const char *); +extern int git_config_maybe_bool(const char *, const char *); +extern int git_config_string(const char **, const char *, const char *); +extern int git_config_pathname(const char **, const char *, const char *); +extern int git_config_set_in_file_gently(const char *, const char *, const char *); +extern void git_config_set_in_file(const char *, const char *, const char *); +extern int git_config_set_gently(const char *, const char *); +extern void git_config_set(const char *, const char *); +extern int git_config_parse_key(const char *, char **, int *); +extern int git_config_key_is_valid(const char *key); +extern int git_config_set_multivar_gently(const char *, const char *, const char *, int); +extern void git_config_set_multivar(const char *, const char *, const char *, int); +extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int); +extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int); +extern int git_config_rename_section(const char *, const char *); +extern int git_config_rename_section_in_file(const char *, const char *, const char *); +extern const char *git_etc_gitconfig(void); +extern int git_env_bool(const char *, int); +extern unsigned long git_env_ulong(const char *, unsigned long); +extern int git_config_system(void); +extern int config_error_nonbool(const char *); +#if defined(__GNUC__) +#define config_error_nonbool(s) (config_error_nonbool(s), const_error()) +#endif + +extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data); + +enum config_scope { + CONFIG_SCOPE_UNKNOWN = 0, + CONFIG_SCOPE_SYSTEM, + CONFIG_SCOPE_GLOBAL, + CONFIG_SCOPE_REPO, + CONFIG_SCOPE_CMDLINE, +}; + +extern enum config_scope current_config_scope(void); +extern const char *current_config_origin_type(void); +extern const char *current_config_name(void); + +struct config_include_data { + int depth; + config_fn_t fn; + void *data; + const struct config_options *opts; +}; +#define CONFIG_INCLUDE_INIT { 0 } +extern int git_config_include(const char *name, const char *value, void *data); + +/* + * Match and parse a config key of the form: + * + * section.(subsection.)?key + * + * (i.e., what gets handed to a config_fn_t). The caller provides the section; + * we return -1 if it does not match, 0 otherwise. The subsection and key + * out-parameters are filled by the function (and *subsection is NULL if it is + * missing). + * + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if + * there is no subsection at all. + */ +extern int parse_config_key(const char *var, + const char *section, + const char **subsection, int *subsection_len, + const char **key); + +struct config_set_element { + struct hashmap_entry ent; + char *key; + struct string_list value_list; +}; + +struct configset_list_item { + struct config_set_element *e; + int value_index; +}; + +/* + * the contents of the list are ordered according to their + * position in the config files and order of parsing the files. + * (i.e. key-value pair at the last position of .git/config will + * be at the last item of the list) + */ +struct configset_list { + struct configset_list_item *items; + unsigned int nr, alloc; +}; + +struct config_set { + struct hashmap config_hash; + int hash_initialized; + struct configset_list list; +}; + +extern void git_configset_init(struct config_set *cs); +extern int git_configset_add_file(struct config_set *cs, const char *filename); +extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value); +extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key); +extern void git_configset_clear(struct config_set *cs); +extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest); +extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest); +extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest); +extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest); +extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest); +extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest); +extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest); +extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest); + +extern int git_config_get_value(const char *key, const char **value); +extern const struct string_list *git_config_get_value_multi(const char *key); +extern void git_config_clear(void); +extern void git_config_iter(config_fn_t fn, void *data); +extern int git_config_get_string_const(const char *key, const char **dest); +extern int git_config_get_string(const char *key, char **dest); +extern int git_config_get_int(const char *key, int *dest); +extern int git_config_get_ulong(const char *key, unsigned long *dest); +extern int git_config_get_bool(const char *key, int *dest); +extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest); +extern int git_config_get_maybe_bool(const char *key, int *dest); +extern int git_config_get_pathname(const char *key, const char **dest); +extern int git_config_get_untracked_cache(void); +extern int git_config_get_split_index(void); +extern int git_config_get_max_percent_split_change(void); + +/* This dies if the configured or default date is in the future */ +extern int git_config_get_expiry(const char *key, const char **output); + +struct key_value_info { + const char *filename; + int linenr; + enum config_origin_type origin_type; + enum config_scope scope; +}; + +extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3))); +extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr); + +#endif /* CONFIG_H */ From f1c985da05c136414c10f034cdc09bdb6939cb8f Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Jun 2017 11:07:35 -0700 Subject: [PATCH 528/566] config: remove git_config_iter Since there is no implementation of the function 'git_config_iter', let's stop exporting it and remove the prototype from config.h. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- config.h | 1 - 1 file changed, 1 deletion(-) diff --git a/config.h b/config.h index f7f8b66c59..c70599bd5f 100644 --- a/config.h +++ b/config.h @@ -165,7 +165,6 @@ extern int git_configset_get_pathname(struct config_set *cs, const char *key, co extern int git_config_get_value(const char *key, const char **value); extern const struct string_list *git_config_get_value_multi(const char *key); extern void git_config_clear(void); -extern void git_config_iter(config_fn_t fn, void *data); extern int git_config_get_string_const(const char *key, const char **dest); extern int git_config_get_string(const char *key, char **dest); extern int git_config_get_int(const char *key, int *dest); From b2141fc1d20e659810245ec6ca1c143c60e033ec Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Jun 2017 11:07:36 -0700 Subject: [PATCH 529/566] config: don't include config.h by default Stop including config.h by default in cache.h. Instead only include config.h in those files which require use of the config system. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- advice.c | 1 + alias.c | 1 + apply.c | 1 + archive-tar.c | 1 + archive-zip.c | 1 + archive.c | 1 + attr.c | 1 + bisect.c | 1 + branch.c | 1 + builtin/add.c | 1 + builtin/am.c | 1 + builtin/blame.c | 1 + builtin/branch.c | 1 + builtin/cat-file.c | 1 + builtin/check-attr.c | 1 + builtin/check-ignore.c | 1 + builtin/check-mailmap.c | 1 + builtin/checkout-index.c | 1 + builtin/checkout.c | 1 + builtin/clean.c | 1 + builtin/clone.c | 1 + builtin/column.c | 1 + builtin/commit-tree.c | 1 + builtin/commit.c | 1 + builtin/config.c | 1 + builtin/count-objects.c | 1 + builtin/describe.c | 1 + builtin/diff-files.c | 1 + builtin/diff-index.c | 1 + builtin/diff-tree.c | 1 + builtin/diff.c | 1 + builtin/difftool.c | 1 + builtin/fast-export.c | 1 + builtin/fetch.c | 1 + builtin/fmt-merge-msg.c | 1 + builtin/for-each-ref.c | 1 + builtin/fsck.c | 1 + builtin/gc.c | 1 + builtin/grep.c | 1 + builtin/hash-object.c | 1 + builtin/help.c | 1 + builtin/index-pack.c | 1 + builtin/init-db.c | 1 + builtin/log.c | 1 + builtin/ls-files.c | 1 + builtin/ls-tree.c | 1 + builtin/merge-base.c | 1 + builtin/merge-file.c | 1 + builtin/merge.c | 1 + builtin/mv.c | 1 + builtin/name-rev.c | 1 + builtin/notes.c | 1 + builtin/pack-objects.c | 1 + builtin/patch-id.c | 1 + builtin/pull.c | 1 + builtin/push.c | 1 + builtin/read-tree.c | 1 + builtin/rebase--helper.c | 1 + builtin/receive-pack.c | 1 + builtin/reflog.c | 1 + builtin/remote.c | 1 + builtin/repack.c | 1 + builtin/replace.c | 1 + builtin/rerere.c | 1 + builtin/reset.c | 1 + builtin/rev-list.c | 1 + builtin/rev-parse.c | 1 + builtin/revert.c | 1 + builtin/rm.c | 1 + builtin/send-pack.c | 1 + builtin/shortlog.c | 1 + builtin/show-branch.c | 1 + builtin/stripspace.c | 1 + builtin/submodule--helper.c | 1 + builtin/symbolic-ref.c | 1 + builtin/tag.c | 1 + builtin/unpack-file.c | 1 + builtin/unpack-objects.c | 1 + builtin/update-index.c | 1 + builtin/update-ref.c | 1 + builtin/update-server-info.c | 1 + builtin/var.c | 1 + builtin/verify-commit.c | 1 + builtin/verify-pack.c | 1 + builtin/verify-tag.c | 1 + builtin/worktree.c | 1 + builtin/write-tree.c | 1 + cache.h | 1 - color.c | 1 + column.c | 1 + compat/precompose_utf8.c | 1 + config.c | 1 + connect.c | 1 + convert.c | 1 + credential-cache--daemon.c | 1 + credential.c | 1 + daemon.c | 1 + diff.c | 1 + dir.c | 1 + environment.c | 1 + fast-import.c | 1 + fetch-pack.c | 1 + git.c | 1 + gpg-interface.c | 1 + graph.c | 1 + grep.c | 1 + help.c | 1 + http-backend.c | 1 + http-fetch.c | 1 + http.c | 1 + ident.c | 1 + imap-send.c | 1 + ll-merge.c | 1 + log-tree.c | 1 + mailinfo.c | 1 + merge-recursive.c | 1 + notes-utils.c | 1 + notes.c | 1 + pager.c | 1 + parse-options.c | 1 + pathspec.c | 1 + pretty.c | 1 + prompt.c | 1 + read-cache.c | 1 + refs.c | 1 + refs/files-backend.c | 1 + remote-curl.c | 1 + remote.c | 1 + rerere.c | 1 + send-pack.c | 1 + sequencer.c | 1 + setup.c | 1 + sha1_file.c | 1 + sha1_name.c | 1 + submodule-config.c | 1 + submodule.c | 1 + t/helper/test-config.c | 1 + t/helper/test-submodule-config.c | 1 + trailer.c | 1 + transport.c | 1 + unpack-trees.c | 1 + upload-pack.c | 1 + userdiff.c | 1 + versioncmp.c | 1 + wrapper.c | 1 + xdiff-interface.c | 1 + 146 files changed, 145 insertions(+), 1 deletion(-) diff --git a/advice.c b/advice.c index b84ae4960f..3fa04fca0b 100644 --- a/advice.c +++ b/advice.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" int advice_push_update_rejected = 1; int advice_push_non_ff_current = 1; diff --git a/alias.c b/alias.c index 0526304661..de8e6a3f49 100644 --- a/alias.c +++ b/alias.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" struct config_alias_data { const char *alias; diff --git a/apply.c b/apply.c index e6dbab26ad..5b442860df 100644 --- a/apply.c +++ b/apply.c @@ -8,6 +8,7 @@ */ #include "cache.h" +#include "config.h" #include "blob.h" #include "delta.h" #include "diff.h" diff --git a/archive-tar.c b/archive-tar.c index 380e3aedd2..d65ac016df 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -2,6 +2,7 @@ * Copyright (c) 2005, 2006 Rene Scharfe */ #include "cache.h" +#include "config.h" #include "tar.h" #include "archive.h" #include "streaming.h" diff --git a/archive-zip.c b/archive-zip.c index b429a8d974..04fcd86872 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -2,6 +2,7 @@ * Copyright (c) 2006 Rene Scharfe */ #include "cache.h" +#include "config.h" #include "archive.h" #include "streaming.h" #include "utf8.h" diff --git a/archive.c b/archive.c index 60b8891986..0cba5b4096 100644 --- a/archive.c +++ b/archive.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "refs.h" #include "commit.h" #include "tree-walk.h" diff --git a/attr.c b/attr.c index 7e2134471c..9f8b029363 100644 --- a/attr.c +++ b/attr.c @@ -9,6 +9,7 @@ #define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" +#include "config.h" #include "exec_cmd.h" #include "attr.h" #include "dir.h" diff --git a/bisect.c b/bisect.c index 08c9fb7266..e8f03a08c4 100644 --- a/bisect.c +++ b/bisect.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "commit.h" #include "diff.h" #include "revision.h" diff --git a/branch.c b/branch.c index ad5a2299ba..5532b4218f 100644 --- a/branch.c +++ b/branch.c @@ -1,5 +1,6 @@ #include "git-compat-util.h" #include "cache.h" +#include "config.h" #include "branch.h" #include "refs.h" #include "remote.h" diff --git a/builtin/add.c b/builtin/add.c index 9f53f020d0..55759a6a78 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -4,6 +4,7 @@ * Copyright (C) 2006 Linus Torvalds */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "lockfile.h" #include "dir.h" diff --git a/builtin/am.c b/builtin/am.c index a95dd8b4e6..59f46a29ee 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -4,6 +4,7 @@ * Based on git-am.sh by Junio C Hamano. */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "exec_cmd.h" #include "parse-options.h" diff --git a/builtin/blame.c b/builtin/blame.c index 07506a3e45..1a7371808a 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -6,6 +6,7 @@ */ #include "cache.h" +#include "config.h" #include "refs.h" #include "builtin.h" #include "blob.h" diff --git a/builtin/branch.c b/builtin/branch.c index 48a513a84d..50b59238d4 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -6,6 +6,7 @@ */ #include "cache.h" +#include "config.h" #include "color.h" #include "refs.h" #include "commit.h" diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 1890d7a639..12451205cf 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "parse-options.h" #include "userdiff.h" diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 4d01ca0c8b..91444dc044 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "attr.h" #include "quote.h" #include "parse-options.h" diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 1d73d3ca3d..a2da09f4ae 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "dir.h" #include "quote.h" #include "pathspec.h" diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index cf0f54f6b9..cdce144f3b 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "mailmap.h" #include "parse-options.h" #include "string-list.h" diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 07631d0c9c..39c8be05dc 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -5,6 +5,7 @@ * */ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "quote.h" #include "cache-tree.h" diff --git a/builtin/checkout.c b/builtin/checkout.c index bfa5419f33..e0b58ddc0b 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "parse-options.h" #include "refs.h" diff --git a/builtin/clean.c b/builtin/clean.c index d861f836a2..57093db8be 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -8,6 +8,7 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "dir.h" #include "parse-options.h" #include "string-list.h" diff --git a/builtin/clone.c b/builtin/clone.c index de85b85254..eaacac221b 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -9,6 +9,7 @@ */ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "parse-options.h" #include "fetch-pack.h" diff --git a/builtin/column.c b/builtin/column.c index 33314b4d71..0c3223d64b 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "strbuf.h" #include "parse-options.h" #include "string-list.h" diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 605017261c..e1c17ba64b 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "commit.h" #include "tree.h" #include "builtin.h" diff --git a/builtin/commit.c b/builtin/commit.c index 1d805f5da8..929a45abed 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -6,6 +6,7 @@ */ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "cache-tree.h" #include "color.h" diff --git a/builtin/config.c b/builtin/config.c index 3a554ad50c..b2045cd6c3 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "color.h" #include "parse-options.h" #include "urlmatch.h" diff --git a/builtin/count-objects.c b/builtin/count-objects.c index acb05940fc..1d82e61f2a 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -5,6 +5,7 @@ */ #include "cache.h" +#include "config.h" #include "dir.h" #include "builtin.h" #include "parse-options.h" diff --git a/builtin/describe.c b/builtin/describe.c index a5cd8c513f..3eddfd3706 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "commit.h" #include "tag.h" diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 15c61fd8d1..29d3e4f416 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "diff.h" #include "commit.h" #include "revision.h" diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 1af373d002..e3d418361c 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "diff.h" #include "commit.h" #include "revision.h" diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 326f88b657..c81063d534 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "diff.h" #include "commit.h" #include "log-tree.h" diff --git a/builtin/diff.c b/builtin/diff.c index d184aafab9..1ce40c63b2 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -4,6 +4,7 @@ * Copyright (c) 2006 Junio C Hamano */ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "color.h" #include "commit.h" diff --git a/builtin/difftool.c b/builtin/difftool.c index 1354d0e462..538b0187fa 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -12,6 +12,7 @@ * Copyright (C) 2016 Johannes Schindelin */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "run-command.h" #include "exec_cmd.h" diff --git a/builtin/fast-export.c b/builtin/fast-export.c index e0220630d0..e4304e2dd6 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -5,6 +5,7 @@ */ #include "builtin.h" #include "cache.h" +#include "config.h" #include "refs.h" #include "commit.h" #include "object.h" diff --git a/builtin/fetch.c b/builtin/fetch.c index 5f2c2ab23e..1fe8f62385 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -2,6 +2,7 @@ * "git fetch" */ #include "cache.h" +#include "config.h" #include "refs.h" #include "commit.h" #include "builtin.h" diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 6faa3c0d24..2e30b514f2 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "refs.h" #include "commit.h" #include "diff.h" diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index eca365bf89..52be99cbac 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "refs.h" #include "object.h" #include "parse-options.h" diff --git a/builtin/fsck.c b/builtin/fsck.c index b5e13a4556..0c63f6259f 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "commit.h" #include "tree.h" #include "blob.h" diff --git a/builtin/gc.c b/builtin/gc.c index 91f7696a85..0a6790d714 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -11,6 +11,7 @@ */ #include "builtin.h" +#include "config.h" #include "tempfile.h" #include "lockfile.h" #include "parse-options.h" diff --git a/builtin/grep.c b/builtin/grep.c index 3ffb5b4e81..3df9e08f03 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -4,6 +4,7 @@ * Copyright (c) 2006 Junio C Hamano */ #include "cache.h" +#include "config.h" #include "blob.h" #include "tree.h" #include "commit.h" diff --git a/builtin/hash-object.c b/builtin/hash-object.c index bbeaf20bcc..d04baf999a 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -5,6 +5,7 @@ * Copyright (C) Junio C Hamano, 2005 */ #include "builtin.h" +#include "config.h" #include "blob.h" #include "quote.h" #include "parse-options.h" diff --git a/builtin/help.c b/builtin/help.c index 49f7a07f85..334a8494ab 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -2,6 +2,7 @@ * Builtin help command */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "exec_cmd.h" #include "parse-options.h" diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 4ff567db47..dfb3d1296a 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "delta.h" #include "pack.h" #include "csum-file.h" diff --git a/builtin/init-db.c b/builtin/init-db.c index 8a6acb0ec6..47823f9aa4 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "refs.h" #include "builtin.h" #include "exec_cmd.h" diff --git a/builtin/log.c b/builtin/log.c index b3b10cc1ed..b333d4fea2 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -5,6 +5,7 @@ * 2006 Junio Hamano */ #include "cache.h" +#include "config.h" #include "refs.h" #include "color.h" #include "commit.h" diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a6c70dbe9e..9751010780 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -6,6 +6,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "quote.h" #include "dir.h" #include "builtin.h" diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index d7ebeb4ce6..4b53a8282b 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "blob.h" #include "tree.h" #include "commit.h" diff --git a/builtin/merge-base.c b/builtin/merge-base.c index cfe2a796f8..9c201cbfc0 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "commit.h" #include "refs.h" #include "diff.h" diff --git a/builtin/merge-file.c b/builtin/merge-file.c index 47dde7c39c..b08803e611 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "parse-options.h" diff --git a/builtin/merge.c b/builtin/merge.c index 703827f006..9bdd577e09 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -7,6 +7,7 @@ */ #include "cache.h" +#include "config.h" #include "parse-options.h" #include "builtin.h" #include "lockfile.h" diff --git a/builtin/mv.c b/builtin/mv.c index 61d20037ad..dcf6736b5b 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -4,6 +4,7 @@ * Copyright (C) 2006 Johannes Schindelin */ #include "builtin.h" +#include "config.h" #include "pathspec.h" #include "lockfile.h" #include "dir.h" diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 92a5d8a5d2..c222daaa14 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "commit.h" #include "tag.h" #include "refs.h" diff --git a/builtin/notes.c b/builtin/notes.c index 7b891471c4..dceb681bad 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -8,6 +8,7 @@ */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "notes.h" #include "blob.h" diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0fe35d1b5a..0e17184354 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "attr.h" #include "object.h" #include "blob.h" diff --git a/builtin/patch-id.c b/builtin/patch-id.c index 81552e02e4..970d0d30b4 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result) { diff --git a/builtin/pull.c b/builtin/pull.c index dd1a4a94e4..3632921f03 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -6,6 +6,7 @@ * Fetch one or more remote refs and merge it/them into the current HEAD. */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "parse-options.h" #include "exec_cmd.h" diff --git a/builtin/push.c b/builtin/push.c index a597759d8f..76aa713d22 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -2,6 +2,7 @@ * "git push" */ #include "cache.h" +#include "config.h" #include "refs.h" #include "run-command.h" #include "builtin.h" diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 23e212ee8c..0a85b6d938 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -5,6 +5,7 @@ */ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "object.h" #include "tree.h" diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c index ca1ebb2fa1..c82b4dce68 100644 --- a/builtin/rebase--helper.c +++ b/builtin/rebase--helper.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "parse-options.h" #include "sequencer.h" diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index f96834f42c..01ae7154e6 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "pack.h" #include "refs.h" diff --git a/builtin/reflog.c b/builtin/reflog.c index 7472775778..6e9a8213ea 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "commit.h" #include "refs.h" diff --git a/builtin/remote.c b/builtin/remote.c index addf97ad29..a470ed7c62 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "parse-options.h" #include "transport.h" #include "remote.h" diff --git a/builtin/repack.c b/builtin/repack.c index 677bc7c81a..c01ff7c30b 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "dir.h" #include "parse-options.h" #include "run-command.h" diff --git a/builtin/replace.c b/builtin/replace.c index ab17668f43..905b1759a4 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -9,6 +9,7 @@ */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "refs.h" #include "parse-options.h" diff --git a/builtin/rerere.c b/builtin/rerere.c index 1bf72423bf..ffb66e2907 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "dir.h" #include "parse-options.h" #include "string-list.h" diff --git a/builtin/reset.c b/builtin/reset.c index fc3b906c47..fdd5e5e003 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -8,6 +8,7 @@ * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano */ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "tag.h" #include "object.h" diff --git a/builtin/rev-list.c b/builtin/rev-list.c index bcf77f0b8a..f9e2caee16 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "commit.h" #include "diff.h" #include "revision.h" diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 0513330910..9d18bbd648 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "commit.h" #include "refs.h" #include "quote.h" diff --git a/builtin/revert.c b/builtin/revert.c index 345d9586a7..16028b9ea8 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "builtin.h" #include "parse-options.h" #include "diff.h" diff --git a/builtin/rm.c b/builtin/rm.c index fb79dcab18..b1adf1c961 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds 2006 */ #include "builtin.h" +#include "config.h" #include "lockfile.h" #include "dir.h" #include "cache-tree.h" diff --git a/builtin/send-pack.c b/builtin/send-pack.c index b8e2e74fe0..633e0c3cdd 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "commit.h" #include "refs.h" #include "pkt-line.h" diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 7cff1839fc..43c4799ea9 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "commit.h" #include "diff.h" #include "string-list.h" diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 19756595d5..6825439970 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "commit.h" #include "refs.h" #include "builtin.h" diff --git a/builtin/stripspace.c b/builtin/stripspace.c index 1e62a008cb..bdf0328869 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "parse-options.h" #include "strbuf.h" diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 566a5b6a6f..700460c349 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "parse-options.h" #include "quote.h" #include "pathspec.h" diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 70addef158..df75cb9d4a 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "cache.h" #include "refs.h" #include "parse-options.h" diff --git a/builtin/tag.c b/builtin/tag.c index bdf1e88e93..b85bcf6809 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -7,6 +7,7 @@ */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "refs.h" #include "tag.h" diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c index 6fc6bcdf7f..73f1334191 100644 --- a/builtin/unpack-file.c +++ b/builtin/unpack-file.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" static char *create_temp_file(unsigned char *sha1) { diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 4532aa0831..9da06548f0 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "object.h" #include "delta.h" #include "pack.h" diff --git a/builtin/update-index.c b/builtin/update-index.c index ebfc09faa0..0a4c236487 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -4,6 +4,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "quote.h" #include "cache-tree.h" diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 0b2ecf41ae..40ccfc193b 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "refs.h" #include "builtin.h" #include "parse-options.h" diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index 6c8cc3edc1..873070e517 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "builtin.h" #include "parse-options.h" diff --git a/builtin/var.c b/builtin/var.c index aedbb53a2d..6c6f46b4ae 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -4,6 +4,7 @@ * Copyright (C) Eric Biederman, 2005 */ #include "builtin.h" +#include "config.h" static const char var_usage[] = "git var (-l | )"; diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 38bedf8f9f..d5e9a17a77 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -6,6 +6,7 @@ * Based on git-verify-tag */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "commit.h" #include "run-command.h" diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index c94e156932..c2a1a5c504 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "config.h" #include "run-command.h" #include "parse-options.h" diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 5199553d91..f9a5f7535a 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -6,6 +6,7 @@ * Based on git-verify-tag.sh */ #include "cache.h" +#include "config.h" #include "builtin.h" #include "tag.h" #include "run-command.h" diff --git a/builtin/worktree.c b/builtin/worktree.c index 1722a9bdc2..d414b6870b 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "builtin.h" #include "dir.h" #include "parse-options.h" diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 084c0df783..bd0a78aa3c 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -5,6 +5,7 @@ */ #include "builtin.h" #include "cache.h" +#include "config.h" #include "tree.h" #include "cache-tree.h" #include "parse-options.h" diff --git a/cache.h b/cache.h index 67efb47a14..160ff81aa7 100644 --- a/cache.h +++ b/cache.h @@ -11,7 +11,6 @@ #include "string-list.h" #include "pack-revindex.h" #include "hash.h" -#include "config.h" #ifndef platform_SHA_CTX /* diff --git a/color.c b/color.c index dee61557e0..31b6207a00 100644 --- a/color.c +++ b/color.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "color.h" static int git_use_color_default = GIT_COLOR_AUTO; diff --git a/column.c b/column.c index d55ead18ef..ff7bdab1a3 100644 --- a/column.c +++ b/column.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "column.h" #include "string-list.h" #include "parse-options.h" diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c index 4293b53b17..de61c15d34 100644 --- a/compat/precompose_utf8.c +++ b/compat/precompose_utf8.c @@ -6,6 +6,7 @@ #define PRECOMPOSE_UNICODE_C #include "cache.h" +#include "config.h" #include "utf8.h" #include "precompose_utf8.h" diff --git a/config.c b/config.c index 3df7515db2..eec7613e04 100644 --- a/config.c +++ b/config.c @@ -6,6 +6,7 @@ * */ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "exec_cmd.h" #include "strbuf.h" diff --git a/connect.c b/connect.c index cd21a1b6f7..efddb30ea8 100644 --- a/connect.c +++ b/connect.c @@ -1,5 +1,6 @@ #include "git-compat-util.h" #include "cache.h" +#include "config.h" #include "pkt-line.h" #include "quote.h" #include "refs.h" diff --git a/convert.c b/convert.c index 8d652bf27c..69f23cfcaa 100644 --- a/convert.c +++ b/convert.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "attr.h" #include "run-command.h" #include "quote.h" diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c index 46c5937526..2787b29c05 100644 --- a/credential-cache--daemon.c +++ b/credential-cache--daemon.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "tempfile.h" #include "credential.h" #include "unix-socket.h" diff --git a/credential.c b/credential.c index aa996669fc..67a523353b 100644 --- a/credential.c +++ b/credential.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "credential.h" #include "string-list.h" #include "run-command.h" diff --git a/daemon.c b/daemon.c index ac7181a483..30747075f0 100644 --- a/daemon.c +++ b/daemon.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "pkt-line.h" #include "run-command.h" #include "strbuf.h" diff --git a/diff.c b/diff.c index 74283d9001..4f9b9f8382 100644 --- a/diff.c +++ b/diff.c @@ -2,6 +2,7 @@ * Copyright (C) 2005 Junio C Hamano */ #include "cache.h" +#include "config.h" #include "tempfile.h" #include "quote.h" #include "diff.h" diff --git a/dir.c b/dir.c index f451bfa48c..42beb65bea 100644 --- a/dir.c +++ b/dir.c @@ -8,6 +8,7 @@ * Junio Hamano, 2005-2006 */ #include "cache.h" +#include "config.h" #include "dir.h" #include "attr.h" #include "refs.h" diff --git a/environment.c b/environment.c index ff6e4f06e9..9d9b367925 100644 --- a/environment.c +++ b/environment.c @@ -8,6 +8,7 @@ * are. */ #include "cache.h" +#include "config.h" #include "refs.h" #include "fmt-merge-msg.h" #include "commit.h" diff --git a/fast-import.c b/fast-import.c index cf58f875b8..bbc3e79eaf 100644 --- a/fast-import.c +++ b/fast-import.c @@ -154,6 +154,7 @@ Format of STDIN stream: #include "builtin.h" #include "cache.h" +#include "config.h" #include "lockfile.h" #include "object.h" #include "blob.h" diff --git a/fetch-pack.c b/fetch-pack.c index afb8b05024..963d45db91 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "refs.h" #include "pkt-line.h" diff --git a/git.c b/git.c index 58ef570294..c03de2c090 100644 --- a/git.c +++ b/git.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "exec_cmd.h" #include "help.h" #include "run-command.h" diff --git a/gpg-interface.c b/gpg-interface.c index e44cc27da1..8ab32df457 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "run-command.h" #include "strbuf.h" #include "gpg-interface.h" diff --git a/graph.c b/graph.c index 8b9049dd2c..e7e20650da 100644 --- a/graph.c +++ b/graph.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "commit.h" #include "color.h" #include "graph.h" diff --git a/grep.c b/grep.c index 47cee45067..d5211fc5a6 100644 --- a/grep.c +++ b/grep.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "grep.h" #include "userdiff.h" #include "xdiff-interface.h" diff --git a/help.c b/help.c index 91ec8ab36f..b8f3a98e4c 100644 --- a/help.c +++ b/help.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "builtin.h" #include "exec_cmd.h" #include "levenshtein.h" diff --git a/http-backend.c b/http-backend.c index eef0a361f4..109df44e31 100644 --- a/http-backend.c +++ b/http-backend.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "refs.h" #include "pkt-line.h" #include "object.h" diff --git a/http-fetch.c b/http-fetch.c index 3b556d6619..8af380050c 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "exec_cmd.h" #include "http.h" #include "walker.h" diff --git a/http.c b/http.c index d2e11ec6f0..013bb0cc65 100644 --- a/http.c +++ b/http.c @@ -1,5 +1,6 @@ #include "git-compat-util.h" #include "http.h" +#include "config.h" #include "pack.h" #include "sideband.h" #include "run-command.h" diff --git a/ident.c b/ident.c index bea871c8e0..d41fc91192 100644 --- a/ident.c +++ b/ident.c @@ -6,6 +6,7 @@ * Copyright (C) 2005 Linus Torvalds */ #include "cache.h" +#include "config.h" static struct strbuf git_default_name = STRBUF_INIT; static struct strbuf git_default_email = STRBUF_INIT; diff --git a/imap-send.c b/imap-send.c index 857591660f..59e9b12d29 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,6 +23,7 @@ */ #include "cache.h" +#include "config.h" #include "credential.h" #include "exec_cmd.h" #include "run-command.h" diff --git a/ll-merge.c b/ll-merge.c index ac0d4a5d78..24ff94e1dd 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -5,6 +5,7 @@ */ #include "cache.h" +#include "config.h" #include "attr.h" #include "xdiff-interface.h" #include "run-command.h" diff --git a/log-tree.c b/log-tree.c index 4618dd04ca..282510b105 100644 --- a/log-tree.c +++ b/log-tree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "diff.h" #include "commit.h" #include "tag.h" diff --git a/mailinfo.c b/mailinfo.c index 68037758f2..aaed3870a4 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "utf8.h" #include "strbuf.h" #include "mailinfo.h" diff --git a/merge-recursive.c b/merge-recursive.c index 62decd51cc..a85144c23a 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -4,6 +4,7 @@ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006 */ #include "cache.h" +#include "config.h" #include "advice.h" #include "lockfile.h" #include "cache-tree.h" diff --git a/notes-utils.c b/notes-utils.c index 24a33616a4..9d7fdd6354 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "commit.h" #include "refs.h" #include "notes-utils.h" diff --git a/notes.c b/notes.c index 542563b280..dbcfef4d7a 100644 --- a/notes.c +++ b/notes.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "notes.h" #include "blob.h" #include "tree.h" diff --git a/pager.c b/pager.c index c113d898a4..4dd9e1b265 100644 --- a/pager.c +++ b/pager.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "run-command.h" #include "sigchain.h" diff --git a/parse-options.c b/parse-options.c index a23a1e67f0..cbf84a6048 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1,6 +1,7 @@ #include "git-compat-util.h" #include "parse-options.h" #include "cache.h" +#include "config.h" #include "commit.h" #include "color.h" #include "utf8.h" diff --git a/pathspec.c b/pathspec.c index 50f76fff45..e4659b1440 100644 --- a/pathspec.c +++ b/pathspec.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "dir.h" #include "pathspec.h" #include "attr.h" diff --git a/pretty.c b/pretty.c index d0f86f5d85..06a1f13c60 100644 --- a/pretty.c +++ b/pretty.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "commit.h" #include "utf8.h" #include "diff.h" diff --git a/prompt.c b/prompt.c index 75406390c6..6d5885d009 100644 --- a/prompt.c +++ b/prompt.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "run-command.h" #include "strbuf.h" #include "prompt.h" diff --git a/read-cache.c b/read-cache.c index 0d0081a11b..e623e075de 100644 --- a/read-cache.c +++ b/read-cache.c @@ -5,6 +5,7 @@ */ #define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" +#include "config.h" #include "tempfile.h" #include "lockfile.h" #include "cache-tree.h" diff --git a/refs.c b/refs.c index df75f8e0d6..c0cd259141 100644 --- a/refs.c +++ b/refs.c @@ -3,6 +3,7 @@ */ #include "cache.h" +#include "config.h" #include "hashmap.h" #include "lockfile.h" #include "iterator.h" diff --git a/refs/files-backend.c b/refs/files-backend.c index 83ea080e01..1d530f6020 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,4 +1,5 @@ #include "../cache.h" +#include "../config.h" #include "../refs.h" #include "refs-internal.h" #include "ref-cache.h" diff --git a/remote-curl.c b/remote-curl.c index ece45993da..0053b09549 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "remote.h" #include "strbuf.h" #include "walker.h" diff --git a/remote.c b/remote.c index 801137c72e..6ef03bb730 100644 --- a/remote.c +++ b/remote.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "remote.h" #include "refs.h" #include "commit.h" diff --git a/rerere.c b/rerere.c index 3bd55caf3b..344d6aa818 100644 --- a/rerere.c +++ b/rerere.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "string-list.h" #include "rerere.h" diff --git a/send-pack.c b/send-pack.c index 78bb34ebec..ed3cee3211 100644 --- a/send-pack.c +++ b/send-pack.c @@ -1,4 +1,5 @@ #include "builtin.h" +#include "config.h" #include "commit.h" #include "refs.h" #include "pkt-line.h" diff --git a/sequencer.c b/sequencer.c index 10c3b4ff81..7356a93562 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "lockfile.h" #include "sequencer.h" #include "dir.h" diff --git a/setup.c b/setup.c index ba3241bf02..c45443f696 100644 --- a/setup.c +++ b/setup.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "dir.h" #include "string-list.h" diff --git a/sha1_file.c b/sha1_file.c index 59a4ed2ed3..44561e0b92 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -7,6 +7,7 @@ * creation etc. */ #include "cache.h" +#include "config.h" #include "string-list.h" #include "lockfile.h" #include "delta.h" diff --git a/sha1_name.c b/sha1_name.c index 8eec9f7c1b..7e1e86c97a 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "tag.h" #include "commit.h" #include "tree.h" diff --git a/submodule-config.c b/submodule-config.c index 4f58491ddb..d8f8d5ea32 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "submodule-config.h" #include "submodule.h" #include "strbuf.h" diff --git a/submodule.c b/submodule.c index d3299e29c0..8cfcb3bedd 100644 --- a/submodule.c +++ b/submodule.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "submodule-config.h" #include "submodule.h" #include "dir.h" diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 8e3ed6a76c..1a7b8bd3d6 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "string-list.h" /* diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c index 2f144d539a..c6c57bba0d 100644 --- a/t/helper/test-submodule-config.c +++ b/t/helper/test-submodule-config.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "submodule-config.h" #include "submodule.h" diff --git a/trailer.c b/trailer.c index 11f0b9fb40..751b56c009 100644 --- a/trailer.c +++ b/trailer.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "string-list.h" #include "run-command.h" #include "commit.h" diff --git a/transport.c b/transport.c index 4d33138a75..6096f82586 100644 --- a/transport.c +++ b/transport.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "transport.h" #include "run-command.h" #include "pkt-line.h" diff --git a/unpack-trees.c b/unpack-trees.c index aa15111fef..e7b5a21ef7 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1,5 +1,6 @@ #define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" +#include "config.h" #include "dir.h" #include "tree.h" #include "tree-walk.h" diff --git a/upload-pack.c b/upload-pack.c index ffb028d623..7581a51960 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "refs.h" #include "pkt-line.h" #include "sideband.h" diff --git a/userdiff.c b/userdiff.c index 8b732e40bc..2c1502f719 100644 --- a/userdiff.c +++ b/userdiff.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "userdiff.h" #include "attr.h" diff --git a/versioncmp.c b/versioncmp.c index 9f81dc1062..069ee94a4d 100644 --- a/versioncmp.c +++ b/versioncmp.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "string-list.h" /* diff --git a/wrapper.c b/wrapper.c index d837417709..487a9f7532 100644 --- a/wrapper.c +++ b/wrapper.c @@ -2,6 +2,7 @@ * Various trivial helper wrappers around standard functions */ #include "cache.h" +#include "config.h" static void do_nothing(size_t size) { diff --git a/xdiff-interface.c b/xdiff-interface.c index 060038c2d6..5ac07d7348 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "xdiff-interface.h" #include "xdiff/xtypes.h" #include "xdiff/xdiffi.h" From d3fb71b3cb36971608a46744261e6b5f8802e784 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Jun 2017 11:07:37 -0700 Subject: [PATCH 530/566] setup: teach discover_git_directory to respect the commondir Currently 'discover_git_directory' only looks at the gitdir to determine if a git directory was discovered. This causes a problem in the event that the gitdir which was discovered was in fact a per-worktree git directory and not the common git directory. This is because the repository config, which is checked to verify the repository's format, is stored in the commondir and not in the per-worktree gitdir. Correct this behavior by checking the config stored in the commondir. It will also be of use for callers to have access to the commondir, so lets also return that upon successfully discovering a git directory. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- cache.h | 13 ++++++++----- config.c | 10 ++++++---- setup.c | 17 +++++++++++------ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/cache.h b/cache.h index 160ff81aa7..82f39f7a9e 100644 --- a/cache.h +++ b/cache.h @@ -525,12 +525,15 @@ extern void set_git_work_tree(const char *tree); extern void setup_work_tree(void); /* - * Find GIT_DIR of the repository that contains the current working directory, - * without changing the working directory or other global state. The result is - * appended to gitdir. The return value is either NULL if no repository was - * found, or pointing to the path inside gitdir's buffer. + * Find the commondir and gitdir of the repository that contains the current + * working directory, without changing the working directory or other global + * state. The result is appended to commondir and gitdir. If the discovered + * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will + * both have the same result appended to the buffer. The return value is + * either 0 upon success and non-zero if no repository was found. */ -extern const char *discover_git_directory(struct strbuf *gitdir); +extern int discover_git_directory(struct strbuf *commondir, + struct strbuf *gitdir); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern char *prefix_path(const char *prefix, int len, const char *path); diff --git a/config.c b/config.c index eec7613e04..e8dbf9e64a 100644 --- a/config.c +++ b/config.c @@ -1639,7 +1639,8 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data) void read_early_config(config_fn_t cb, void *data) { struct config_options opts = {0}; - struct strbuf buf = STRBUF_INIT; + struct strbuf commondir = STRBUF_INIT; + struct strbuf gitdir = STRBUF_INIT; opts.respect_includes = 1; @@ -1653,12 +1654,13 @@ void read_early_config(config_fn_t cb, void *data) * notably, the current working directory is still the same after the * call). */ - else if (discover_git_directory(&buf)) - opts.git_dir = buf.buf; + else if (!discover_git_directory(&commondir, &gitdir)) + opts.git_dir = gitdir.buf; git_config_with_options(cb, data, NULL, &opts); - strbuf_release(&buf); + strbuf_release(&commondir); + strbuf_release(&gitdir); } static void git_config_check_init(void); diff --git a/setup.c b/setup.c index c45443f696..4e38cf2a4f 100644 --- a/setup.c +++ b/setup.c @@ -941,19 +941,21 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, } } -const char *discover_git_directory(struct strbuf *gitdir) +int discover_git_directory(struct strbuf *commondir, + struct strbuf *gitdir) { struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT; size_t gitdir_offset = gitdir->len, cwd_len; + size_t commondir_offset = commondir->len; struct repository_format candidate; if (strbuf_getcwd(&dir)) - return NULL; + return -1; cwd_len = dir.len; if (setup_git_directory_gently_1(&dir, gitdir, 0) <= 0) { strbuf_release(&dir); - return NULL; + return -1; } /* @@ -969,8 +971,10 @@ const char *discover_git_directory(struct strbuf *gitdir) strbuf_insert(gitdir, gitdir_offset, dir.buf, dir.len); } + get_common_dir(commondir, gitdir->buf + gitdir_offset); + strbuf_reset(&dir); - strbuf_addf(&dir, "%s/config", gitdir->buf + gitdir_offset); + strbuf_addf(&dir, "%s/config", commondir->buf + commondir_offset); read_repository_format(&candidate, dir.buf); strbuf_release(&dir); @@ -978,11 +982,12 @@ const char *discover_git_directory(struct strbuf *gitdir) warning("ignoring git dir '%s': %s", gitdir->buf + gitdir_offset, err.buf); strbuf_release(&err); + strbuf_setlen(commondir, commondir_offset); strbuf_setlen(gitdir, gitdir_offset); - return NULL; + return -1; } - return gitdir->buf + gitdir_offset; + return 0; } const char *setup_git_directory_gently(int *nongit_ok) From a577fb5fdc53a4729eeedc2922d1461350b92f73 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Jun 2017 11:07:38 -0700 Subject: [PATCH 531/566] config: respect commondir Worktrees present an interesting problem when it comes to the config. Historically we could assume that the per-repository config lives at 'gitdir/config', but since worktrees were introduced this isn't the case anymore. There is currently no way to specify per-worktree configuration, and as such the repository config is shared with all worktrees and is located at 'commondir/config'. Many users of the config machinery correctly set 'config_options.git_dir' with the repository's commondir, allowing the config to be properly loaded when operating in a worktree. But other's, like 'read_early_config()', set 'config_options.git_dir' with the repository's gitdir which can be incorrect when using worktrees. To fix this issue, and to make things less ambiguous, lets add a 'commondir' field to the 'config_options' struct and have all callers properly set both the 'git_dir' and 'commondir' fields so that the config machinery is able to properly find the repository's config. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- config.c | 11 +++++++---- config.h | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index e8dbf9e64a..edc0d17708 100644 --- a/config.c +++ b/config.c @@ -1531,8 +1531,8 @@ static int do_git_config_sequence(const struct config_options *opts, char *user_config = expand_user_path("~/.gitconfig", 0); char *repo_config; - if (opts->git_dir) - repo_config = mkpathdup("%s/config", opts->git_dir); + if (opts->commondir) + repo_config = mkpathdup("%s/config", opts->commondir); else if (have_git_dir()) repo_config = git_pathdup("config"); else @@ -1644,7 +1644,8 @@ void read_early_config(config_fn_t cb, void *data) opts.respect_includes = 1; - if (have_git_dir()) + if (have_git_dir()) { + opts.commondir = get_git_common_dir(); opts.git_dir = get_git_dir(); /* * When setup_git_directory() was not yet asked to discover the @@ -1654,8 +1655,10 @@ void read_early_config(config_fn_t cb, void *data) * notably, the current working directory is still the same after the * call). */ - else if (!discover_git_directory(&commondir, &gitdir)) + } else if (!discover_git_directory(&commondir, &gitdir)) { + opts.commondir = commondir.buf; opts.git_dir = gitdir.buf; + } git_config_with_options(cb, data, NULL, &opts); diff --git a/config.h b/config.h index c70599bd5f..63b92784cd 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,7 @@ enum config_origin_type { struct config_options { unsigned int respect_includes : 1; + const char *commondir; const char *git_dir; }; From dc8441fdb4598f54865a5c783d1f86c1e0bcb6dc Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Jun 2017 11:07:39 -0700 Subject: [PATCH 532/566] config: don't implicitly use gitdir or commondir 'git_config_with_options()' takes a 'config_options' struct which contains feilds for 'git_dir' and 'commondir'. If those feilds happen to be NULL the config machinery falls back to querying global repository state. Let's change this and instead use these fields in the 'config_options' struct explicilty all the time. Since the API is slightly changing to require these two fields to be set if callers want the config machinery to load the repository's config, let's change the name to 'config_with_optison()'. This allows the config machinery to not implicitly rely on any global repository state. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- builtin/config.c | 26 +++++++++++++++----------- config.c | 21 +++++++++++---------- config.h | 6 +++--- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/builtin/config.c b/builtin/config.c index b2045cd6c3..1fc6471f37 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -243,8 +243,8 @@ static int get_value(const char *key_, const char *regex_) } } - git_config_with_options(collect_config, &values, - &given_config_source, &config_options); + config_with_options(collect_config, &values, + &given_config_source, &config_options); ret = !values.nr; @@ -321,8 +321,8 @@ static void get_color(const char *var, const char *def_color) get_color_slot = var; get_color_found = 0; parsed_color[0] = '\0'; - git_config_with_options(git_get_color_config, NULL, - &given_config_source, &config_options); + config_with_options(git_get_color_config, NULL, + &given_config_source, &config_options); if (!get_color_found && def_color) { if (color_parse(def_color, parsed_color) < 0) @@ -353,8 +353,8 @@ static int get_colorbool(const char *var, int print) get_colorbool_found = -1; get_diff_color_found = -1; get_color_ui_found = -1; - git_config_with_options(git_get_colorbool_config, NULL, - &given_config_source, &config_options); + config_with_options(git_get_colorbool_config, NULL, + &given_config_source, &config_options); if (get_colorbool_found < 0) { if (!strcmp(get_colorbool_slot, "color.diff")) @@ -442,8 +442,8 @@ static int get_urlmatch(const char *var, const char *url) show_keys = 1; } - git_config_with_options(urlmatch_config_entry, &config, - &given_config_source, &config_options); + config_with_options(urlmatch_config_entry, &config, + &given_config_source, &config_options); ret = !values.nr; @@ -536,6 +536,10 @@ int cmd_config(int argc, const char **argv, const char *prefix) config_options.respect_includes = !given_config_source.file; else config_options.respect_includes = respect_includes_opt; + if (!nongit) { + config_options.commondir = get_git_common_dir(); + config_options.git_dir = get_git_dir(); + } if (end_null) { term = '\0'; @@ -580,9 +584,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (actions == ACTION_LIST) { check_argc(argc, 0, 0); - if (git_config_with_options(show_all_config, NULL, - &given_config_source, - &config_options) < 0) { + if (config_with_options(show_all_config, NULL, + &given_config_source, + &config_options) < 0) { if (given_config_source.file) die_errno("unable to read config file '%s'", given_config_source.file); diff --git a/config.c b/config.c index edc0d17708..ca9e9efec1 100644 --- a/config.c +++ b/config.c @@ -218,8 +218,6 @@ static int include_by_gitdir(const struct config_options *opts, if (opts->git_dir) git_dir = opts->git_dir; - else if (have_git_dir()) - git_dir = get_git_dir(); else goto done; @@ -1533,8 +1531,6 @@ static int do_git_config_sequence(const struct config_options *opts, if (opts->commondir) repo_config = mkpathdup("%s/config", opts->commondir); - else if (have_git_dir()) - repo_config = git_pathdup("config"); else repo_config = NULL; @@ -1565,9 +1561,9 @@ static int do_git_config_sequence(const struct config_options *opts, return ret; } -int git_config_with_options(config_fn_t fn, void *data, - struct git_config_source *config_source, - const struct config_options *opts) +int config_with_options(config_fn_t fn, void *data, + struct git_config_source *config_source, + const struct config_options *opts) { struct config_include_data inc = CONFIG_INCLUDE_INIT; @@ -1598,9 +1594,14 @@ static void git_config_raw(config_fn_t fn, void *data) struct config_options opts = {0}; opts.respect_includes = 1; - if (git_config_with_options(fn, data, NULL, &opts) < 0) + if (have_git_dir()) { + opts.commondir = get_git_common_dir(); + opts.git_dir = get_git_dir(); + } + + if (config_with_options(fn, data, NULL, &opts) < 0) /* - * git_config_with_options() normally returns only + * config_with_options() normally returns only * zero, as most errors are fatal, and * non-fatal potential errors are guarded by "if" * statements that are entered only when no error is @@ -1660,7 +1661,7 @@ void read_early_config(config_fn_t cb, void *data) opts.git_dir = gitdir.buf; } - git_config_with_options(cb, data, NULL, &opts); + config_with_options(cb, data, NULL, &opts); strbuf_release(&commondir); strbuf_release(&gitdir); diff --git a/config.h b/config.h index 63b92784cd..9e038cce25 100644 --- a/config.h +++ b/config.h @@ -45,9 +45,9 @@ extern void git_config_push_parameter(const char *text); extern int git_config_from_parameters(config_fn_t fn, void *data); extern void read_early_config(config_fn_t cb, void *data); extern void git_config(config_fn_t fn, void *); -extern int git_config_with_options(config_fn_t fn, void *, - struct git_config_source *config_source, - const struct config_options *opts); +extern int config_with_options(config_fn_t fn, void *, + struct git_config_source *config_source, + const struct config_options *opts); extern int git_parse_ulong(const char *, unsigned long *); extern int git_parse_maybe_bool(const char *); extern int git_config_int(const char *, const char *); From 680b469e83c23ea682786acb661ad3835f779fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20N=C4=9Bmec?= Date: Wed, 14 Jun 2017 11:36:58 +0200 Subject: [PATCH 533/566] doc: git-reset: fix a trivial typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Štěpán Němec Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 8a21198d65..70f3753e1e 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -115,7 +115,7 @@ $ git pull git://info.example.com/ nitfol <4> in these files are in good order. You do not want to see them when you run "git diff", because you plan to work on other files and changes with these files are distracting. -<2> Somebody asks you to pull, and the changes sounds worthy of merging. +<2> Somebody asks you to pull, and the changes sound worthy of merging. <3> However, you already dirtied the index (i.e. your index does not match the HEAD commit). But you know the pull you are going to make does not affect frotz.c or filfre.c, so you revert the From 3adf9fdecfb0cd31a83ef3af1d8d631a1acd392b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 14 Jun 2017 01:30:18 -0400 Subject: [PATCH 534/566] configure.ac: loosen FREAD_READS_DIRECTORIES test program MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We added an FREAD_READS_DIRECTORIES Makefile knob long ago in cba22528f (Add compat/fopen.c which returns NULL on attempt to open directory, 2008-02-08) to handle systems where reading from a directory returned garbage. This works by catching the problem at the fopen() stage and returning NULL. More recently, we found that there is a class of systems (including Linux) where fopen() succeeds but fread() fails. Since the solution is the same (having fopen return NULL), they use the same Makefile knob as of e2d90fd1c (config.mak.uname: set FREAD_READS_DIRECTORIES for Linux and FreeBSD, 2017-05-03). This works fine except for one thing: the autoconf test in configure.ac to set FREAD_READS_DIRECTORIES actually checks whether fread succeeds. Which means that on Linux systems, the knob isn't set (and we even override the config.mak.uname default). t1308 catches the failure. We can fix this by tweaking the autoconf test to cover both cases. In theory we might care about the distinction between the traditional "fread reads directories" case and the new "fopen opens directories". But since our solution catches the problem at the fopen stage either way, we don't actually need to know the difference. The "fopen" case is a superset. This does mean the FREAD_READS_DIRECTORIES name is slightly misleading. Probably FOPEN_OPENS_DIRECTORIES would be more accurate. But it would be disruptive to simply change the name (people's existing build configs would fail), and it's not worth the complexity of handling both. Let's just add a comment in the knob description. Reported-by: Øyvind A. Holm Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Makefile | 3 ++- configure.ac | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index e35542e631..4307588419 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,8 @@ all:: # have been written to the final string if enough space had been available. # # Define FREAD_READS_DIRECTORIES if you are on a system which succeeds -# when attempting to read from an fopen'ed directory. +# when attempting to read from an fopen'ed directory (or even to fopen +# it at all). # # Define NO_OPENSSL environment variable if you do not have OpenSSL. # This also implies BLK_SHA1. diff --git a/configure.ac b/configure.ac index 128165529f..ceeb9de575 100644 --- a/configure.ac +++ b/configure.ac @@ -865,9 +865,9 @@ AC_CACHE_CHECK([whether system succeeds to read fopen'ed directory], [ AC_RUN_IFELSE( [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], - [[char c; + [[ FILE *f = fopen(".", "r"); - return f && fread(&c, 1, 1, f)]])], + return f)]])], [ac_cv_fread_reads_directories=no], [ac_cv_fread_reads_directories=yes]) ]) From 97e2ff464302565877a00b8a9aa6a2d85bd1445e Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 14 Jun 2017 17:12:25 +0200 Subject: [PATCH 535/566] sub-process: correct path to API docs in a comment Signed-off-by: Christian Couder Acked-by: Ben Peart Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- sub-process.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sub-process.h b/sub-process.h index 7d451e1cde..d9a45cd359 100644 --- a/sub-process.h +++ b/sub-process.h @@ -7,7 +7,7 @@ /* * Generic implementation of background process infrastructure. - * See Documentation/technical/api-background-process.txt. + * See: Documentation/technical/api-sub-process.txt */ /* data structures */ From c3fbf81a8534cf88ff948d12004eb94929ec1174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Thu, 15 Jun 2017 14:29:53 +0200 Subject: [PATCH 536/566] strbuf: let strbuf_addftime handle %z and %Z itself There is no portable way to pass timezone information to strftime. Add parameters for timezone offset and name to strbuf_addftime and let it handle the timezone-related format specifiers %z and %Z internally. Callers can opt out for %Z by passing NULL as timezone name. %z is always handled internally -- this helps on Windows, where strftime would expand it to a timezone name (same as %Z), in violation of POSIX. Modifiers are not handled, e.g. %Ez is still passed to strftime. Use an empty string as timezone name in show_date (the only current caller) for now because we only have the timezone offset in non-local mode. POSIX allows %Z to resolve to an empty string in case of missing information. Helped-by: Ulrich Mueller Helped-by: Jeff King Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 3 ++- date.c | 2 +- strbuf.c | 41 +++++++++++++++++++++++++++--- strbuf.h | 8 +++++- t/t0006-date.sh | 6 +++++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index a02f7324c0..3921066b7f 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -764,7 +764,8 @@ timezone value. 1970). As with `--raw`, this is always in UTC and therefore `-local` has no effect. + -`--date=format:...` feeds the format `...` to your system `strftime`. +`--date=format:...` feeds the format `...` to your system `strftime`, +except for %z and %Z, which are handled internally. Use `--date=format:%c` to show the date in your system locale's preferred format. See the `strftime` manual for a complete list of format placeholders. When using `-local`, the correct syntax is diff --git a/date.c b/date.c index a996331f5b..6eccbd8546 100644 --- a/date.c +++ b/date.c @@ -233,7 +233,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) month_names[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, tz); else if (mode->type == DATE_STRFTIME) - strbuf_addftime(&timebuf, mode->strftime_fmt, tm); + strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, ""); else strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", weekday_names[tm->tm_wday], diff --git a/strbuf.c b/strbuf.c index 00457940cf..be3b9e37b1 100644 --- a/strbuf.c +++ b/strbuf.c @@ -785,14 +785,48 @@ char *xstrfmt(const char *fmt, ...) return ret; } -void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm) +void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm, + int tz_offset, const char *tz_name) { + struct strbuf munged_fmt = STRBUF_INIT; size_t hint = 128; size_t len; if (!*fmt) return; + /* + * There is no portable way to pass timezone information to + * strftime, so we handle %z and %Z here. + */ + for (;;) { + const char *percent = strchrnul(fmt, '%'); + strbuf_add(&munged_fmt, fmt, percent - fmt); + if (!*percent) + break; + fmt = percent + 1; + switch (*fmt) { + case '%': + strbuf_addstr(&munged_fmt, "%%"); + fmt++; + break; + case 'z': + strbuf_addf(&munged_fmt, "%+05d", tz_offset); + fmt++; + break; + case 'Z': + if (tz_name) { + strbuf_addstr(&munged_fmt, tz_name); + fmt++; + break; + } + /* FALLTHROUGH */ + default: + strbuf_addch(&munged_fmt, '%'); + } + } + fmt = munged_fmt.buf; + strbuf_grow(sb, hint); len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm); @@ -804,17 +838,16 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm) * output contains at least one character, and then drop the extra * character before returning. */ - struct strbuf munged_fmt = STRBUF_INIT; - strbuf_addf(&munged_fmt, "%s ", fmt); + strbuf_addch(&munged_fmt, ' '); while (!len) { hint *= 2; strbuf_grow(sb, hint); len = strftime(sb->buf + sb->len, sb->alloc - sb->len, munged_fmt.buf, tm); } - strbuf_release(&munged_fmt); len--; /* drop munged space */ } + strbuf_release(&munged_fmt); strbuf_setlen(sb, sb->len + len); } diff --git a/strbuf.h b/strbuf.h index 80047b1bb7..4559035c47 100644 --- a/strbuf.h +++ b/strbuf.h @@ -340,8 +340,14 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); /** * Add the time specified by `tm`, as formatted by `strftime`. + * `tz_name` is used to expand %Z internally unless it's NULL. + * `tz_offset` is in decimal hhmm format, e.g. -600 means six hours west + * of Greenwich, and it's used to expand %z internally. However, tokens + * with modifiers (e.g. %Ez) are passed to `strftime`. */ -extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm); +extern void strbuf_addftime(struct strbuf *sb, const char *fmt, + const struct tm *tm, int tz_offset, + const char *tz_name); /** * Read a given size of data from a FILE* pointer to the buffer. diff --git a/t/t0006-date.sh b/t/t0006-date.sh index c0c910867d..fded9dbd68 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -51,6 +51,12 @@ check_show iso-local "$TIME" '2016-06-15 14:13:20 +0000' check_show raw-local "$TIME" '1466000000 +0000' check_show unix-local "$TIME" '1466000000' +check_show 'format:%z' "$TIME" '+0200' +check_show 'format-local:%z' "$TIME" '+0000' +check_show 'format:%Z' "$TIME" '' +check_show 'format:%%z' "$TIME" '%z' +check_show 'format-local:%%z' "$TIME" '%z' + # arbitrary time absurdly far in the future FUTURE="5758122296 -0400" check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT From 22280d7ee7a4286542e14dd0784cb29703ebb389 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 15 Jun 2017 09:51:22 -0400 Subject: [PATCH 537/566] t0006: check --date=format zone offsets We already test that "%z" and "%Z" show the right thing, but we don't actually check that the time we display is the correct one. Let's add two new tests: 1. Test that "format:" shows the time in the author's timezone, just like the other time formats. 2. Test that "format-local:" shows time in the local timezone. We don't want to use our normal UTC for this, because its offset is zero (so the result would be "correct" even if the code forgot to apply the offset or applied it in the wrong direction). We'll use the EST5 zone, which is already used elsewhere in the script (and so is assumed to be available everywhere). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t0006-date.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/t/t0006-date.sh b/t/t0006-date.sh index fded9dbd68..4b789c00f1 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -31,9 +31,11 @@ check_show () { format=$1 time=$2 expect=$3 - test_expect_success $4 "show date ($format:$time)" ' + prereqs=$4 + zone=$5 + test_expect_success $prereqs "show date ($format:$time)" ' echo "$time -> $expect" >expect && - test-date show:$format "$time" >actual && + TZ=${zone:-$TZ} test-date show:"$format" "$time" >actual && test_cmp expect actual ' } @@ -57,6 +59,9 @@ check_show 'format:%Z' "$TIME" '' check_show 'format:%%z' "$TIME" '%z' check_show 'format-local:%%z' "$TIME" '%z' +check_show 'format:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 16:13:20' +check_show 'format-local:%Y-%m-%d %H:%M:%S' "$TIME" '2016-06-15 09:13:20' '' EST5 + # arbitrary time absurdly far in the future FUTURE="5758122296 -0400" check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT From 6eced3ec5e5d7fbe61de2791e2627b1acf1246b3 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 15 Jun 2017 09:52:17 -0400 Subject: [PATCH 538/566] date: use localtime() for "-local" time formats When we convert seconds-since-epochs timestamps into a broken-down "struct tm", we do so by adjusting the timestamp according to the known offset and then using gmtime() to break down the result. This means that the resulting struct "knows" that it's in GMT, even though the time it represents is adjusted for a different zone. The fields where it stores this data are not portably accessible, so we have no way to override them to tell them the real zone info. For the most part, this works. Our date-formatting routines don't pay attention to these inaccessible fields, and use the same tz info we provided for adjustment. The one exception is when we call strftime(), whose %Z format reveals this hidden timezone data. We solved that by always showing the empty string for %Z. This is allowed by POSIX, but not very helpful to the user. We can't make this work in the general case, as there's no portable function for setting an arbitrary timezone (and anyway, we don't have the zone name for the author zones, only their offsets). But for the special case of the "-local" formats, we can just skip the adjustment and use localtime() instead of gmtime(). This makes --date=format-local:%Z work correctly, showing the local timezone instead of an empty string. The new test checks the result for "UTC", our default test-lib value for $TZ. Using something like EST5 might be more interesting, but the actual zone string is system-dependent (for instance, on my system it expands to just EST). Hopefully "UTC" is vanilla enough that every system treats it the same. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- date.c | 14 ++++++++++++-- t/t0006-date.sh | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/date.c b/date.c index 6eccbd8546..5580c40ac0 100644 --- a/date.c +++ b/date.c @@ -60,6 +60,12 @@ static struct tm *time_to_tm(unsigned long time, int tz) return gmtime(&t); } +static struct tm *time_to_tm_local(unsigned long time) +{ + time_t t = time; + return localtime(&t); +} + /* * What value of "tz" was in effect back then at "time" in the * local timezone? @@ -201,7 +207,10 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) return timebuf.buf; } - tm = time_to_tm(time, tz); + if (mode->local) + tm = time_to_tm_local(time); + else + tm = time_to_tm(time, tz); if (!tm) { tm = time_to_tm(0, 0); tz = 0; @@ -233,7 +242,8 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) month_names[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, tz); else if (mode->type == DATE_STRFTIME) - strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, ""); + strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, + mode->local ? NULL : ""); else strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", weekday_names[tm->tm_wday], diff --git a/t/t0006-date.sh b/t/t0006-date.sh index 4b789c00f1..8e219b7c51 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -56,6 +56,7 @@ check_show unix-local "$TIME" '1466000000' check_show 'format:%z' "$TIME" '+0200' check_show 'format-local:%z' "$TIME" '+0000' check_show 'format:%Z' "$TIME" '' +check_show 'format-local:%Z' "$TIME" 'UTC' check_show 'format:%%z' "$TIME" '%z' check_show 'format-local:%%z' "$TIME" '%z' From 8abc09c0e38659bb438475eca768a035ed8d85b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 15 Jun 2017 12:36:07 +0200 Subject: [PATCH 539/566] docs/pretty-formats: stress that %- removes all preceding line-feeds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- Documentation/pretty-formats.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 47b286b33e..32533b8980 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -213,8 +213,8 @@ If you add a `+` (plus sign) after '%' of a placeholder, a line-feed is inserted immediately before the expansion if and only if the placeholder expands to a non-empty string. -If you add a `-` (minus sign) after '%' of a placeholder, line-feeds that -immediately precede the expansion are deleted if and only if the +If you add a `-` (minus sign) after '%' of a placeholder, all consecutive +line-feeds immediately preceding the expansion are deleted if and only if the placeholder expands to an empty string. If you add a ` ` (space) after '%' of a placeholder, a space From 481df65f4f72582369eea8d002070c5dff38f8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 21:06:59 +0000 Subject: [PATCH 540/566] git-compat-util: add a FREE_AND_NULL() wrapper around free(ptr); ptr = NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a FREE_AND_NULL() wrapper marco for the common pattern of freeing a pointer and assigning NULL to it right afterwards. The implementation is similar to the (currently unused) XDL_PTRFREE macro in xdiff/xmacros.h added in commit 3443546f6e ("Use a *real* built-in diff generator", 2006-03-24). The only difference is that free() is called unconditionally, see [1]. See [2] for a suggested alternative which does this via a function instead of a macro. As covered in replies to that message, while it's a viable approach, it would introduce caveats which this approach doesn't have, so that potential change is left to a future follow-up change. This merely allows us to translate exactly what we're doing now to a less verbose & idiomatic form using a macro, while guaranteeing that we don't introduce any functional changes. 1. (http://public-inbox.org/git/alpine.DEB.2.20.1608301948310.129229@virtualbox/) 2. <20170610032143.GA7880@starla> (https://public-inbox.org/git/20170610032143.GA7880@starla/) Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-compat-util.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index 51ba4e6b3b..047172d173 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -808,6 +808,12 @@ extern char *xgetcwd(void); extern FILE *fopen_for_writing(const char *path); extern FILE *fopen_or_warn(const char *path, const char *mode); +/* + * FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note + * that ptr is used twice, so don't pass e.g. ptr++. + */ +#define FREE_AND_NULL(p) do { free(p); (p) = NULL; } while (0) + #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc))) #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc))) From d8604c747b2c9c2b61f1257577c2939e5b07c664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 21:10:41 +0000 Subject: [PATCH 541/566] wildmatch test: cover a blind spot in "/" matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A negated character class that does not include '/', e.g. [^a-z]: - Should match '/' when doing "wildmatch" - Should not match '/' when doing "pathmatch" Add two tests to cover these cases. Helped-by: Junio C Hamano Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 7ca69f4bed..8fd70d3aa2 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -82,6 +82,7 @@ match 1 0 'foo/bar' 'foo/**/bar' match 1 0 'foo/bar' 'foo/**/**/bar' match 0 0 'foo/bar' 'foo?bar' match 0 0 'foo/bar' 'foo[/]bar' +match 0 0 'foo/bar' 'foo[^a-z]bar' match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 1 0 'foo' '**/foo' @@ -225,6 +226,7 @@ pathmatch 0 foo/bba/arr 'foo/*z' pathmatch 0 foo/bba/arr 'foo/**z' pathmatch 1 foo/bar 'foo?bar' pathmatch 1 foo/bar 'foo[/]bar' +pathmatch 1 foo/bar 'foo[^a-z]bar' pathmatch 0 foo '*/*/*' pathmatch 0 foo/bar '*/*/*' pathmatch 1 foo/bba/arr '*/*/*' From cf9f49ea4817b36e93e64909a918a912cc32c28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 23:15:45 +0000 Subject: [PATCH 542/566] coccinelle: add a rule to make "type" code use FREE_AND_NULL() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- contrib/coccinelle/free.cocci | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci index c03ba737e5..35fb992621 100644 --- a/contrib/coccinelle/free.cocci +++ b/contrib/coccinelle/free.cocci @@ -9,3 +9,11 @@ expression E; @@ - if (!E) free(E); + +@@ +type T; +T *ptr; +@@ +- free(ptr); +- ptr = NULL; ++ FREE_AND_NULL(ptr); From 6a83d902073803c4141e02c53decf8c03e35da27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 23:15:46 +0000 Subject: [PATCH 543/566] coccinelle: make use of the "type" FREE_AND_NULL() rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the result of the just-added coccinelle rule. This manually excludes a few occurrences, mostly things that resulted in many FREE_AND_NULL() on one line, that'll be manually fixed in a subsequent change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- alias.c | 6 ++---- apply.c | 3 +-- attr.c | 6 ++---- branch.c | 3 +-- builtin/am.c | 3 +-- builtin/clean.c | 6 ++---- builtin/config.c | 6 ++---- builtin/index-pack.c | 6 ++---- builtin/pack-objects.c | 12 ++++-------- builtin/unpack-objects.c | 3 +-- commit.c | 3 +-- config.c | 3 +-- credential.c | 3 +-- diff-lib.c | 3 +-- diff.c | 6 ++---- diffcore-rename.c | 6 ++---- dir.c | 9 +++------ fast-import.c | 6 ++---- grep.c | 3 +-- http-push.c | 24 ++++++++---------------- http.c | 15 +++++---------- imap-send.c | 3 +-- line-log.c | 3 +-- mailinfo.c | 3 +-- object.c | 3 +-- pathspec.c | 3 +-- read-cache.c | 6 ++---- ref-filter.c | 3 +-- refs/files-backend.c | 3 +-- remote-testsvn.c | 3 +-- sequencer.c | 3 +-- sha1-array.c | 3 +-- sha1_file.c | 3 +-- transport-helper.c | 27 +++++++++------------------ transport.c | 3 +-- tree-diff.c | 6 ++---- tree.c | 3 +-- 37 files changed, 71 insertions(+), 142 deletions(-) diff --git a/alias.c b/alias.c index 3b90397a99..911481855f 100644 --- a/alias.c +++ b/alias.c @@ -47,8 +47,7 @@ int split_cmdline(char *cmdline, const char ***argv) src++; c = cmdline[src]; if (!c) { - free(*argv); - *argv = NULL; + FREE_AND_NULL(*argv); return -SPLIT_CMDLINE_BAD_ENDING; } } @@ -60,8 +59,7 @@ int split_cmdline(char *cmdline, const char ***argv) cmdline[dst] = 0; if (quoted) { - free(*argv); - *argv = NULL; + FREE_AND_NULL(*argv); return -SPLIT_CMDLINE_UNCLOSED_QUOTE; } diff --git a/apply.c b/apply.c index 854faa6779..e78de0affa 100644 --- a/apply.c +++ b/apply.c @@ -3705,8 +3705,7 @@ static int check_preimage(struct apply_state *state, is_new: patch->is_new = 1; patch->is_delete = 0; - free(patch->old_name); - patch->old_name = NULL; + FREE_AND_NULL(patch->old_name); return 0; } diff --git a/attr.c b/attr.c index 821203e2a9..ebdcfb0b8a 100644 --- a/attr.c +++ b/attr.c @@ -638,13 +638,11 @@ void attr_check_reset(struct attr_check *check) void attr_check_clear(struct attr_check *check) { - free(check->items); - check->items = NULL; + FREE_AND_NULL(check->items); check->alloc = 0; check->nr = 0; - free(check->all_attrs); - check->all_attrs = NULL; + FREE_AND_NULL(check->all_attrs); check->all_attrs_nr = 0; drop_attr_stack(&check->stack); diff --git a/branch.c b/branch.c index 985316eb76..2347cb8649 100644 --- a/branch.c +++ b/branch.c @@ -24,8 +24,7 @@ static int find_tracked_branch(struct remote *remote, void *priv) } else { free(tracking->spec.src); if (tracking->src) { - free(tracking->src); - tracking->src = NULL; + FREE_AND_NULL(tracking->src); } } tracking->spec.src = NULL; diff --git a/builtin/am.c b/builtin/am.c index 8881d73615..138fb98537 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -483,8 +483,7 @@ static int run_applypatch_msg_hook(struct am_state *state) ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL); if (!ret) { - free(state->msg); - state->msg = NULL; + FREE_AND_NULL(state->msg); if (read_commit_msg(state) < 0) die(_("'%s' was deleted by the applypatch-msg hook"), am_path(state, "final-commit")); diff --git a/builtin/clean.c b/builtin/clean.c index 142bf668cf..bff5a07330 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -837,8 +837,7 @@ static void interactive_main_loop(void) int ret; ret = menus[*chosen].fn(); if (ret != MENU_RETURN_NO_LOOP) { - free(chosen); - chosen = NULL; + FREE_AND_NULL(chosen); if (!del_list.nr) { clean_print_color(CLEAN_COLOR_ERROR); printf_ln(_("No more files to clean, exiting.")); @@ -851,8 +850,7 @@ static void interactive_main_loop(void) quit_cmd(); } - free(chosen); - chosen = NULL; + FREE_AND_NULL(chosen); break; } } diff --git a/builtin/config.c b/builtin/config.c index 7f6c25d4d9..18c2158630 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -214,8 +214,7 @@ static int get_value(const char *key_, const char *regex_) key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(key_regexp, key, REG_EXTENDED)) { error("invalid key pattern: %s", key_); - free(key_regexp); - key_regexp = NULL; + FREE_AND_NULL(key_regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; } @@ -235,8 +234,7 @@ static int get_value(const char *key_, const char *regex_) regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(regexp, regex_, REG_EXTENDED)) { error("invalid pattern: %s", regex_); - free(regexp); - regexp = NULL; + FREE_AND_NULL(regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 04b9dcaf0f..f3b16520c2 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -388,8 +388,7 @@ static struct base_data *alloc_base_data(void) static void free_base_data(struct base_data *c) { if (c->data) { - free(c->data); - c->data = NULL; + FREE_AND_NULL(c->data); get_thread_data()->base_cache_used -= c->size; } } @@ -605,8 +604,7 @@ static void *unpack_data(struct object_entry *obj, git_inflate_end(&stream); free(inbuf); if (consume) { - free(data); - data = NULL; + FREE_AND_NULL(data); } return data; } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index f672225def..790615b2c3 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -264,8 +264,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent * make sure no cached delta data remains from a * previous attempt before a pack split occurred. */ - free(entry->delta_data); - entry->delta_data = NULL; + FREE_AND_NULL(entry->delta_data); entry->z_delta_size = 0; } else if (entry->delta_data) { size = entry->delta_size; @@ -1375,12 +1374,10 @@ static void cleanup_preferred_base(void) if (!pbase_tree_cache[i]) continue; free(pbase_tree_cache[i]->tree_data); - free(pbase_tree_cache[i]); - pbase_tree_cache[i] = NULL; + FREE_AND_NULL(pbase_tree_cache[i]); } - free(done_pbase_paths); - done_pbase_paths = NULL; + FREE_AND_NULL(done_pbase_paths); done_pbase_paths_num = done_pbase_paths_alloc = 0; } @@ -1970,8 +1967,7 @@ static unsigned long free_unpacked(struct unpacked *n) n->index = NULL; if (n->data) { freed_mem += n->entry->size; - free(n->data); - n->data = NULL; + FREE_AND_NULL(n->data); } n->entry = NULL; n->depth = 0; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 8bc9997767..60bfe48789 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -112,8 +112,7 @@ static void *get_data(unsigned long size) break; if (ret != Z_OK) { error("inflate returned %d", ret); - free(buf); - buf = NULL; + FREE_AND_NULL(buf); if (!recover) exit(1); has_errors = 1; diff --git a/commit.c b/commit.c index 99846d9bf4..cbfd689939 100644 --- a/commit.c +++ b/commit.c @@ -287,8 +287,7 @@ void free_commit_buffer(struct commit *commit) { struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit); if (v) { - free(v->buffer); - v->buffer = NULL; + FREE_AND_NULL(v->buffer); v->size = 0; } } diff --git a/config.c b/config.c index 34a139c40b..0501a60dd2 100644 --- a/config.c +++ b/config.c @@ -395,8 +395,7 @@ static int git_config_parse_key_1(const char *key, char **store_key, int *basele out_free_ret_1: if (store_key) { - free(*store_key); - *store_key = NULL; + FREE_AND_NULL(*store_key); } return -CONFIG_INVALID_KEY; } diff --git a/credential.c b/credential.c index aa996669fc..0ab247ff40 100644 --- a/credential.c +++ b/credential.c @@ -93,8 +93,7 @@ static void credential_apply_config(struct credential *c) c->configured = 1; if (!c->use_http_path && proto_is_http(c->protocol)) { - free(c->path); - c->path = NULL; + FREE_AND_NULL(c->path); } } diff --git a/diff-lib.c b/diff-lib.c index 76c8f185cd..7528f4163f 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -179,8 +179,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) free(dpath); continue; } - free(dpath); - dpath = NULL; + FREE_AND_NULL(dpath); /* * Show the diff for the 'ce' if we found the one diff --git a/diff.c b/diff.c index e35cf6c704..3561adf49f 100644 --- a/diff.c +++ b/diff.c @@ -1218,8 +1218,7 @@ static void free_diff_words_data(struct emit_callback *ecbdata) regfree(ecbdata->diff_words->word_regex); free(ecbdata->diff_words->word_regex); } - free(ecbdata->diff_words); - ecbdata->diff_words = NULL; + FREE_AND_NULL(ecbdata->diff_words); } } @@ -2951,8 +2950,7 @@ void diff_free_filespec_blob(struct diff_filespec *s) void diff_free_filespec_data(struct diff_filespec *s) { diff_free_filespec_blob(s); - free(s->cnt_data); - s->cnt_data = NULL; + FREE_AND_NULL(s->cnt_data); } static void prep_temp_blob(const char *path, struct diff_tempfile *temp, diff --git a/diffcore-rename.c b/diffcore-rename.c index f7444c86bd..6d8daf9c00 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -667,11 +667,9 @@ void diffcore_rename(struct diff_options *options) for (i = 0; i < rename_dst_nr; i++) free_filespec(rename_dst[i].two); - free(rename_dst); - rename_dst = NULL; + FREE_AND_NULL(rename_dst); rename_dst_nr = rename_dst_alloc = 0; - free(rename_src); - rename_src = NULL; + FREE_AND_NULL(rename_src); rename_src_nr = rename_src_alloc = 0; return; } diff --git a/dir.c b/dir.c index 1759063817..7dc168b806 100644 --- a/dir.c +++ b/dir.c @@ -2117,8 +2117,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate, for (i = j = 0; j < dir->nr; j++) { if (i && check_dir_entry_contains(dir->entries[i - 1], dir->entries[j])) { - free(dir->entries[j]); - dir->entries[j] = NULL; + FREE_AND_NULL(dir->entries[j]); } else { dir->entries[i++] = dir->entries[j]; } @@ -2144,8 +2143,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate, dir->untracked->dir_invalidated)) istate->cache_changed |= UNTRACKED_CHANGED; if (dir->untracked != istate->untracked) { - free(dir->untracked); - dir->untracked = NULL; + FREE_AND_NULL(dir->untracked); } } return dir->nr; @@ -2488,8 +2486,7 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra strbuf_addbuf(out, &untracked->ident); strbuf_add(out, ouc, ouc_size(len)); - free(ouc); - ouc = NULL; + FREE_AND_NULL(ouc); if (!untracked->root) { varint_len = encode_varint(0, varbuf); diff --git a/fast-import.c b/fast-import.c index 9a22fc92c0..eeab927d5a 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1063,8 +1063,7 @@ discard_pack: close(pack_data->pack_fd); unlink_or_warn(pack_data->pack_name); } - free(pack_data); - pack_data = NULL; + FREE_AND_NULL(pack_data); running = 0; /* We can't carry a delta across packfiles. */ @@ -1149,8 +1148,7 @@ static int store_object( /* We cannot carry a delta into the new pack. */ if (delta) { - free(delta); - delta = NULL; + FREE_AND_NULL(delta); git_deflate_init(&s, pack_compression_level); s.next_in = (void *)dat->buf; diff --git a/grep.c b/grep.c index d03d424e5c..11513ec06d 100644 --- a/grep.c +++ b/grep.c @@ -1778,8 +1778,7 @@ void grep_source_clear_data(struct grep_source *gs) case GREP_SOURCE_FILE: case GREP_SOURCE_SHA1: case GREP_SOURCE_SUBMODULE: - free(gs->buf); - gs->buf = NULL; + FREE_AND_NULL(gs->buf); gs->size = 0; break; case GREP_SOURCE_BUF: diff --git a/http-push.c b/http-push.c index 67c4d4b472..c91f40a610 100644 --- a/http-push.c +++ b/http-push.c @@ -291,8 +291,7 @@ static void start_mkcol(struct transfer_request *request) request->state = RUN_MKCOL; } else { request->state = ABORTED; - free(request->url); - request->url = NULL; + FREE_AND_NULL(request->url); } } #endif @@ -409,8 +408,7 @@ static void start_put(struct transfer_request *request) request->state = RUN_PUT; } else { request->state = ABORTED; - free(request->url); - request->url = NULL; + FREE_AND_NULL(request->url); } } @@ -432,8 +430,7 @@ static void start_move(struct transfer_request *request) request->state = RUN_MOVE; } else { request->state = ABORTED; - free(request->url); - request->url = NULL; + FREE_AND_NULL(request->url); } } @@ -526,8 +523,7 @@ static void finish_request(struct transfer_request *request) /* URL is reused for MOVE after PUT */ if (request->state != RUN_PUT) { - free(request->url); - request->url = NULL; + FREE_AND_NULL(request->url); } if (request->state == RUN_MKCOL) { @@ -803,8 +799,7 @@ xml_start_tag(void *userData, const char *name, const char **atts) } xsnprintf(ctx->name + old_namelen, ctx->len - old_namelen, ".%s", c); - free(ctx->cdata); - ctx->cdata = NULL; + FREE_AND_NULL(ctx->cdata); ctx->userFunc(ctx, 0); } @@ -932,8 +927,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout) free(lock->token); free(lock->owner); free(url); - free(lock); - lock = NULL; + FREE_AND_NULL(lock); } else { lock->url = url; lock->start_time = time(NULL); @@ -1105,8 +1099,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed) ls->dentry_flags |= IS_DIR; } } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) { - free(ls->dentry_name); - ls->dentry_name = NULL; + FREE_AND_NULL(ls->dentry_name); ls->dentry_flags = 0; } } @@ -1547,8 +1540,7 @@ static void fetch_symref(const char *path, char **symref, struct object_id *oid) curl_errorstr); free(url); - free(*symref); - *symref = NULL; + FREE_AND_NULL(*symref); oidclr(oid); if (buffer.len == 0) diff --git a/http.c b/http.c index d2e11ec6f0..77f46bef0f 100644 --- a/http.c +++ b/http.c @@ -1026,8 +1026,7 @@ void http_cleanup(void) if (proxy_auth.password) { memset(proxy_auth.password, 0, strlen(proxy_auth.password)); - free(proxy_auth.password); - proxy_auth.password = NULL; + FREE_AND_NULL(proxy_auth.password); } free((void *)curl_proxyuserpwd); @@ -1038,13 +1037,11 @@ void http_cleanup(void) if (cert_auth.password != NULL) { memset(cert_auth.password, 0, strlen(cert_auth.password)); - free(cert_auth.password); - cert_auth.password = NULL; + FREE_AND_NULL(cert_auth.password); } ssl_cert_password_required = 0; - free(cached_accept_language); - cached_accept_language = NULL; + FREE_AND_NULL(cached_accept_language); } struct active_request_slot *get_active_slot(void) @@ -1896,8 +1893,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url) if (http_get_file(url, tmp, NULL) != HTTP_OK) { error("Unable to get pack index %s", url); - free(tmp); - tmp = NULL; + FREE_AND_NULL(tmp); } free(url); @@ -2328,8 +2324,7 @@ void release_http_object_request(struct http_object_request *freq) freq->localfile = -1; } if (freq->url != NULL) { - free(freq->url); - freq->url = NULL; + FREE_AND_NULL(freq->url); } if (freq->slot != NULL) { freq->slot->callback_func = NULL; diff --git a/imap-send.c b/imap-send.c index 857591660f..40ddb496e6 100644 --- a/imap-send.c +++ b/imap-send.c @@ -776,8 +776,7 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) offsetof(struct imap_cmd, next)); if (cmdp->cb.data) { n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen); - free(cmdp->cb.data); - cmdp->cb.data = NULL; + FREE_AND_NULL(cmdp->cb.data); if (n != (int)cmdp->cb.dlen) return RESP_BAD; } else if (cmdp->cb.cont) { diff --git a/line-log.c b/line-log.c index b9087814b8..79eded7363 100644 --- a/line-log.c +++ b/line-log.c @@ -610,8 +610,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args) line_log_data_insert(&ranges, full_name, begin, end); free_filespec(spec); - free(ends); - ends = NULL; + FREE_AND_NULL(ends); } for (p = ranges; p; p = p->next) diff --git a/mailinfo.c b/mailinfo.c index f92cb9f729..44f829edb1 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -919,8 +919,7 @@ again: /* we hit an end boundary */ /* pop the current boundary off the stack */ strbuf_release(*(mi->content_top)); - free(*(mi->content_top)); - *(mi->content_top) = NULL; + FREE_AND_NULL(*(mi->content_top)); /* technically won't happen as is_multipart_boundary() will fail first. But just in case.. diff --git a/object.c b/object.c index 06ba3a11d8..f818777412 100644 --- a/object.c +++ b/object.c @@ -377,8 +377,7 @@ void object_array_clear(struct object_array *array) int i; for (i = 0; i < array->nr; i++) object_array_release_entry(&array->objects[i]); - free(array->objects); - array->objects = NULL; + FREE_AND_NULL(array->objects); array->nr = array->alloc = 0; } diff --git a/pathspec.c b/pathspec.c index 828405021f..54aab8bc01 100644 --- a/pathspec.c +++ b/pathspec.c @@ -662,7 +662,6 @@ void clear_pathspec(struct pathspec *pathspec) attr_check_free(pathspec->items[i].attr_check); } - free(pathspec->items); - pathspec->items = NULL; + FREE_AND_NULL(pathspec->items); pathspec->nr = 0; } diff --git a/read-cache.c b/read-cache.c index bc156a133e..66c413657e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1888,8 +1888,7 @@ int discard_index(struct index_state *istate) free_name_hash(istate); cache_tree_free(&(istate->cache_tree)); istate->initialized = 0; - free(istate->cache); - istate->cache = NULL; + FREE_AND_NULL(istate->cache); istate->cache_alloc = 0; discard_split_index(istate); free_untracked_cache(istate->untracked); @@ -2603,8 +2602,7 @@ void *read_blob_data_from_index(const struct index_state *istate, void stat_validity_clear(struct stat_validity *sv) { - free(sv->sd); - sv->sd = NULL; + FREE_AND_NULL(sv->sd); } int stat_validity_check(struct stat_validity *sv, const char *path) diff --git a/ref-filter.c b/ref-filter.c index ab32bc9c31..72e6cb8ecc 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1891,8 +1891,7 @@ void ref_array_clear(struct ref_array *array) for (i = 0; i < array->nr; i++) free_array_item(array->items[i]); - free(array->items); - array->items = NULL; + FREE_AND_NULL(array->items); array->nr = array->alloc = 0; } diff --git a/refs/files-backend.c b/refs/files-backend.c index d8b3f73147..c1bd99d60f 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2944,8 +2944,7 @@ static int files_transaction_prepare(struct ref_store *ref_store, head_oid.hash, &head_type); if (head_ref && !(head_type & REF_ISSYMREF)) { - free(head_ref); - head_ref = NULL; + FREE_AND_NULL(head_ref); } /* diff --git a/remote-testsvn.c b/remote-testsvn.c index 50404ef343..078b0c3139 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -66,8 +66,7 @@ static char *read_ref_note(const unsigned char sha1[20]) else if (!msglen || type != OBJ_BLOB) { error("Note contains unusable content. " "Is something else using this notes tree? %s", notes_ref); - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } free_notes(NULL); return msg; diff --git a/sequencer.c b/sequencer.c index 5282fb849c..070b42f240 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1211,8 +1211,7 @@ struct todo_list { static void todo_list_release(struct todo_list *todo_list) { strbuf_release(&todo_list->buf); - free(todo_list->items); - todo_list->items = NULL; + FREE_AND_NULL(todo_list->items); todo_list->nr = todo_list->alloc = 0; } diff --git a/sha1-array.c b/sha1-array.c index 7d646ab5b8..838b3bf847 100644 --- a/sha1-array.c +++ b/sha1-array.c @@ -35,8 +35,7 @@ int oid_array_lookup(struct oid_array *array, const struct object_id *oid) void oid_array_clear(struct oid_array *array) { - free(array->oid); - array->oid = NULL; + FREE_AND_NULL(array->oid); array->nr = 0; array->alloc = 0; array->sorted = 0; diff --git a/sha1_file.c b/sha1_file.c index 59a4ed2ed3..d67c88bb8d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -610,8 +610,7 @@ char *compute_alternate_path(const char *path, struct strbuf *err) out: if (seen_error) { - free(ref_git); - ref_git = NULL; + FREE_AND_NULL(ref_git); } return ref_git; diff --git a/transport-helper.c b/transport-helper.c index 36408046eb..33cff38cc0 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -242,8 +242,7 @@ static int disconnect_helper(struct transport *transport) close(data->helper->out); fclose(data->out); res = finish_command(data->helper); - free(data->helper); - data->helper = NULL; + FREE_AND_NULL(data->helper); } return res; } @@ -711,43 +710,35 @@ static int push_update_ref_status(struct strbuf *buf, if (!strcmp(msg, "no match")) { status = REF_STATUS_NONE; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "up to date")) { status = REF_STATUS_UPTODATE; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "non-fast forward")) { status = REF_STATUS_REJECT_NONFASTFORWARD; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "already exists")) { status = REF_STATUS_REJECT_ALREADY_EXISTS; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "fetch first")) { status = REF_STATUS_REJECT_FETCH_FIRST; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "needs force")) { status = REF_STATUS_REJECT_NEEDS_FORCE; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "stale info")) { status = REF_STATUS_REJECT_STALE; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } else if (!strcmp(msg, "forced update")) { forced = 1; - free(msg); - msg = NULL; + FREE_AND_NULL(msg); } } diff --git a/transport.c b/transport.c index 9bfcf870f9..cf35088001 100644 --- a/transport.c +++ b/transport.c @@ -1145,8 +1145,7 @@ void transport_unlock_pack(struct transport *transport) { if (transport->pack_lockfile) { unlink_or_warn(transport->pack_lockfile); - free(transport->pack_lockfile); - transport->pack_lockfile = NULL; + FREE_AND_NULL(transport->pack_lockfile); } } diff --git a/tree-diff.c b/tree-diff.c index e164e532b2..1e1b393d08 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -140,8 +140,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last, /* if last->next is !NULL - it is a pre-allocated memory, we can reuse */ p = last->next; if (p && (alloclen > (intptr_t)p->next)) { - free(p); - p = NULL; + FREE_AND_NULL(p); } if (!p) { @@ -559,8 +558,7 @@ struct combine_diff_path *diff_tree_paths( * (see path_appendnew() for details about why) */ if (p->next) { - free(p->next); - p->next = NULL; + FREE_AND_NULL(p->next); } return p; diff --git a/tree.c b/tree.c index 603b29ee80..614f91586d 100644 --- a/tree.c +++ b/tree.c @@ -226,8 +226,7 @@ int parse_tree_gently(struct tree *item, int quiet_on_missing) void free_tree_buffer(struct tree *tree) { - free(tree->buffer); - tree->buffer = NULL; + FREE_AND_NULL(tree->buffer); tree->size = 0; tree->object.parsed = 0; } From 1b83d1251edbfb49feb84c0c32d241a63e2118a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 23:15:47 +0000 Subject: [PATCH 544/566] coccinelle: add a rule to make "expression" code use FREE_AND_NULL() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A follow-up to the existing "type" rule added in an earlier change. This catches some occurrences that are missed by the previous rule. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- contrib/coccinelle/free.cocci | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci index 35fb992621..f2d97e755b 100644 --- a/contrib/coccinelle/free.cocci +++ b/contrib/coccinelle/free.cocci @@ -17,3 +17,10 @@ T *ptr; - free(ptr); - ptr = NULL; + FREE_AND_NULL(ptr); + +@@ +expression E; +@@ +- free(E); +- E = NULL; ++ FREE_AND_NULL(E); From e140f7afddcdce2bae062ea1578eac38c744e3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 23:15:48 +0000 Subject: [PATCH 545/566] coccinelle: make use of the "expression" FREE_AND_NULL() rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A follow-up to the existing "expression" rule added in an earlier change. This manually excludes a few occurrences, mostly things that resulted in many FREE_AND_NULL() on one line, that'll be manually fixed in a subsequent change. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- blame.c | 3 +-- ll-merge.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/blame.c b/blame.c index 843c845cba..1183943960 100644 --- a/blame.c +++ b/blame.c @@ -314,8 +314,7 @@ static void fill_origin_blob(struct diff_options *opt, static void drop_origin_blob(struct blame_origin *o) { if (o->file.ptr) { - free(o->file.ptr); - o->file.ptr = NULL; + FREE_AND_NULL(o->file.ptr); } } diff --git a/ll-merge.c b/ll-merge.c index ac0d4a5d78..91ff519cef 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -209,8 +209,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, result->size = st.st_size; result->ptr = xmallocz(result->size); if (read_in_full(fd, result->ptr, result->size) != result->size) { - free(result->ptr); - result->ptr = NULL; + FREE_AND_NULL(result->ptr); result->size = 0; } close_bad: From 88ce3ef636b1385e861ec0e9e2155248b999b032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Thu, 15 Jun 2017 23:15:49 +0000 Subject: [PATCH 546/566] *.[ch] refactoring: make use of the FREE_AND_NULL() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace occurrences of `free(ptr); ptr = NULL` which weren't caught by the coccinelle rule. These fall into two categories: - free/NULL assignments one after the other which coccinelle all put on one line, which is functionally equivalent code, but very ugly. - manually spotted occurrences where the NULL assignment isn't right after the free() call. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/am.c | 15 ++++----------- builtin/worktree.c | 6 ++---- commit-slab.h | 3 +-- credential.c | 6 ++---- gpg-interface.c | 15 +++++---------- grep.c | 9 +++------ help.c | 3 +-- line-log.c | 3 +-- prio-queue.c | 3 +-- refs/ref-cache.c | 3 +-- rerere.c | 3 +-- split-index.c | 3 +-- 12 files changed, 23 insertions(+), 49 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 138fb98537..80368b6fe6 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1072,17 +1072,10 @@ static void am_next(struct am_state *state) { struct object_id head; - free(state->author_name); - state->author_name = NULL; - - free(state->author_email); - state->author_email = NULL; - - free(state->author_date); - state->author_date = NULL; - - free(state->msg); - state->msg = NULL; + FREE_AND_NULL(state->author_name); + FREE_AND_NULL(state->author_email); + FREE_AND_NULL(state->author_date); + FREE_AND_NULL(state->msg); state->msg_len = 0; unlink(am_path(state, "author-script")); diff --git a/builtin/worktree.c b/builtin/worktree.c index 793306ea51..41d1c007a4 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -299,10 +299,8 @@ static int add_worktree(const char *path, const char *refname, } is_junk = 0; - free(junk_work_tree); - free(junk_git_dir); - junk_work_tree = NULL; - junk_git_dir = NULL; + FREE_AND_NULL(junk_work_tree); + FREE_AND_NULL(junk_git_dir); done: if (ret || !opts->keep_locked) { diff --git a/commit-slab.h b/commit-slab.h index 42d16dcded..333d81e370 100644 --- a/commit-slab.h +++ b/commit-slab.h @@ -82,8 +82,7 @@ static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s) \ for (i = 0; i < s->slab_count; i++) \ free(s->slab[i]); \ s->slab_count = 0; \ - free(s->slab); \ - s->slab = NULL; \ + FREE_AND_NULL(s->slab); \ } \ \ static MAYBE_UNUSED elemtype *slabname## _at_peek(struct slabname *s, \ diff --git a/credential.c b/credential.c index 0ab247ff40..924ab58538 100644 --- a/credential.c +++ b/credential.c @@ -313,10 +313,8 @@ void credential_reject(struct credential *c) for (i = 0; i < c->helpers.nr; i++) credential_do(c, c->helpers.items[i].string, "erase"); - free(c->username); - c->username = NULL; - free(c->password); - c->password = NULL; + FREE_AND_NULL(c->username); + FREE_AND_NULL(c->password); c->approved = 0; } diff --git a/gpg-interface.c b/gpg-interface.c index e44cc27da1..3f377f89d0 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -13,16 +13,11 @@ static const char *gpg_program = "gpg"; void signature_check_clear(struct signature_check *sigc) { - free(sigc->payload); - free(sigc->gpg_output); - free(sigc->gpg_status); - free(sigc->signer); - free(sigc->key); - sigc->payload = NULL; - sigc->gpg_output = NULL; - sigc->gpg_status = NULL; - sigc->signer = NULL; - sigc->key = NULL; + FREE_AND_NULL(sigc->payload); + FREE_AND_NULL(sigc->gpg_output); + FREE_AND_NULL(sigc->gpg_status); + FREE_AND_NULL(sigc->signer); + FREE_AND_NULL(sigc->key); } static struct { diff --git a/grep.c b/grep.c index 11513ec06d..909f35a984 100644 --- a/grep.c +++ b/grep.c @@ -1763,12 +1763,9 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type, void grep_source_clear(struct grep_source *gs) { - free(gs->name); - gs->name = NULL; - free(gs->path); - gs->path = NULL; - free(gs->identifier); - gs->identifier = NULL; + FREE_AND_NULL(gs->name); + FREE_AND_NULL(gs->path); + FREE_AND_NULL(gs->identifier); grep_source_clear_data(gs); } diff --git a/help.c b/help.c index db7f3d79a0..a2f48c0df3 100644 --- a/help.c +++ b/help.c @@ -267,9 +267,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) for (i = 0; i < old->cnt; i++) cmds->names[cmds->cnt++] = old->names[i]; - free(old->names); + FREE_AND_NULL(old->names); old->cnt = 0; - old->names = NULL; } /* An empirically derived magic number */ diff --git a/line-log.c b/line-log.c index 79eded7363..2d60c347ca 100644 --- a/line-log.c +++ b/line-log.c @@ -34,9 +34,8 @@ void range_set_init(struct range_set *rs, size_t prealloc) void range_set_release(struct range_set *rs) { - free(rs->ranges); + FREE_AND_NULL(rs->ranges); rs->alloc = rs->nr = 0; - rs->ranges = NULL; } /* dst must be uninitialized! */ diff --git a/prio-queue.c b/prio-queue.c index fc3860fdcb..126d096727 100644 --- a/prio-queue.c +++ b/prio-queue.c @@ -27,10 +27,9 @@ void prio_queue_reverse(struct prio_queue *queue) void clear_prio_queue(struct prio_queue *queue) { - free(queue->array); + FREE_AND_NULL(queue->array); queue->nr = 0; queue->alloc = 0; - queue->array = NULL; queue->insertion_ctr = 0; } diff --git a/refs/ref-cache.c b/refs/ref-cache.c index af2fcb2c12..76bb723c86 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -82,9 +82,8 @@ static void clear_ref_dir(struct ref_dir *dir) int i; for (i = 0; i < dir->nr; i++) free_ref_entry(dir->entries[i]); - free(dir->entries); + FREE_AND_NULL(dir->entries); dir->sorted = dir->nr = dir->alloc = 0; - dir->entries = NULL; } struct ref_entry *create_dir_entry(struct ref_cache *cache, diff --git a/rerere.c b/rerere.c index c26c29f87a..61f3804a1e 100644 --- a/rerere.c +++ b/rerere.c @@ -39,9 +39,8 @@ static void free_rerere_dirs(void) free(rerere_dir[i]->status); free(rerere_dir[i]); } - free(rerere_dir); + FREE_AND_NULL(rerere_dir); rerere_dir_nr = rerere_dir_alloc = 0; - rerere_dir = NULL; } static void free_rerere_id(struct string_list_item *item) diff --git a/split-index.c b/split-index.c index 49bd197f71..20477c6a48 100644 --- a/split-index.c +++ b/split-index.c @@ -174,10 +174,9 @@ void merge_base_index(struct index_state *istate) ewah_free(si->delete_bitmap); ewah_free(si->replace_bitmap); - free(si->saved_cache); + FREE_AND_NULL(si->saved_cache); si->delete_bitmap = NULL; si->replace_bitmap = NULL; - si->saved_cache = NULL; si->saved_cache_nr = 0; } From 0af85f84bd6d1755b3b8559ccfd99383c2928ac3 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 18 Jun 2017 19:10:33 -0700 Subject: [PATCH 547/566] mergetools/meld: improve compatibiilty with Meld on macOS X The macOS X fork of Meld[1] requires a "=" in the "--output" argument, as it uses a wrapper[2] script that munges the "--output" argument before calling into the common "meld" script. The macOS X wrapper script[2] accepts "--output=" only, despite the fact that the underlying meld code accepts both "--output "[3]. All versions of meld which accept "--output" accept it in the "--output=" form, so use "--output=" for maximum compatibility. [1] https://github.com/yousseb/meld [2] https://github.com/yousseb/meld/blob/master/osx/Meld [3] https://github.com/yousseb/meld/issues/42 Reported-by: Matthew Groth Helped-by: Samuel Lijin Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- mergetools/meld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mergetools/meld b/mergetools/meld index bc178e8882..7a08470f88 100644 --- a/mergetools/meld +++ b/mergetools/meld @@ -10,7 +10,7 @@ merge_cmd () { if test "$meld_has_output_option" = true then - "$merge_tool_path" --output "$MERGED" \ + "$merge_tool_path" --output="$MERGED" \ "$LOCAL" "$BASE" "$REMOTE" else "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE" From 03df567fbf6afeca32f6a27d04656c1a3a162453 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 18 Jun 2017 15:39:41 +0200 Subject: [PATCH 548/566] for_each_bisect_ref(): don't trim refnames `for_each_bisect_ref()` is called by `for_each_bad_bisect_ref()` with a term "bad". This used to make it call `for_each_ref_in_submodule()` with a prefix "refs/bisect/bad". But the latter is the name of the reference that is being sought, so the empty string was being passed to the callback as the trimmed refname. Moreover, this questionable practice was turned into an error by b9c8e7f2fb prefix_ref_iterator: don't trim too much, 2017-05-22 It makes more sense (and agrees better with the documentation of `--bisect`) for the callers to receive the full reference names. So * Add a new function, `for_each_fullref_in_submodule()`, to the refs API. This plugs a gap in the existing functionality, analogous to `for_each_fullref_in()` but accepting a `submodule` argument. * Change `for_each_bad_bisect_ref()` to call the new function rather than `for_each_ref_in_submodule()`. * Add a test. Signed-off-by: Michael Haggerty Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- refs.c | 12 ++++++++++++ refs.h | 5 ++++- revision.c | 2 +- t/t6002-rev-list-bisect.sh | 14 ++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/refs.c b/refs.c index f0685c9251..32177969f0 100644 --- a/refs.c +++ b/refs.c @@ -1341,6 +1341,18 @@ int for_each_ref_in_submodule(const char *submodule, const char *prefix, prefix, fn, cb_data); } +int for_each_fullref_in_submodule(const char *submodule, const char *prefix, + each_ref_fn fn, void *cb_data, + unsigned int broken) +{ + unsigned int flag = 0; + + if (broken) + flag = DO_FOR_EACH_INCLUDE_BROKEN; + return do_for_each_ref(get_submodule_ref_store(submodule), + prefix, fn, 0, flag, cb_data); +} + int for_each_replace_ref(each_ref_fn fn, void *cb_data) { return do_for_each_ref(get_main_ref_store(), diff --git a/refs.h b/refs.h index 4be14c4b3c..aa4ecc83d0 100644 --- a/refs.h +++ b/refs.h @@ -303,7 +303,10 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); int for_each_ref_in_submodule(const char *submodule, const char *prefix, - each_ref_fn fn, void *cb_data); + each_ref_fn fn, void *cb_data); +int for_each_fullref_in_submodule(const char *submodule, const char *prefix, + each_ref_fn fn, void *cb_data, + unsigned int broken); int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); int for_each_branch_ref_submodule(const char *submodule, diff --git a/revision.c b/revision.c index 9c67cb6026..50039c92d6 100644 --- a/revision.c +++ b/revision.c @@ -2044,7 +2044,7 @@ static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_d struct strbuf bisect_refs = STRBUF_INIT; int status; strbuf_addf(&bisect_refs, "refs/bisect/%s", term); - status = for_each_ref_in_submodule(submodule, bisect_refs.buf, fn, cb_data); + status = for_each_fullref_in_submodule(submodule, bisect_refs.buf, fn, cb_data, 0); strbuf_release(&bisect_refs); return status; } diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh index 3bf2759eae..534903bbd2 100755 --- a/t/t6002-rev-list-bisect.sh +++ b/t/t6002-rev-list-bisect.sh @@ -235,4 +235,18 @@ test_sequence "--bisect" # # + +test_expect_success '--bisect can default to good/bad refs' ' + git update-ref refs/bisect/bad c3 && + good=$(git rev-parse b1) && + git update-ref refs/bisect/good-$good $good && + good=$(git rev-parse c1) && + git update-ref refs/bisect/good-$good $good && + + # the only thing between c3 and c1 is c2 + git rev-parse c2 >expect && + git rev-list --bisect >actual && + test_cmp expect actual +' + test_done From e01db917d8e5c66f9f90bf8c44995cf47200273a Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sat, 17 Jun 2017 18:30:50 -0400 Subject: [PATCH 549/566] stash: update documentation to use 'stash entry' Most of the time, a 'stash entry' is called a 'stash'. Lets try to make this more consistent and use 'stash entry' instead. Signed-off-by: Liam Beguin Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 ++-- Documentation/git-pull.txt | 2 +- Documentation/git-rebase.txt | 2 +- Documentation/git-stash.txt | 60 +++++++++++++++++++----------------- Documentation/gitcli.txt | 2 +- git-stash.sh | 6 ++-- 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index f6278a5ae6..23b807065d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2620,7 +2620,7 @@ rebase.autoSquash:: If set to true enable `--autosquash` option by default. rebase.autoStash:: - When set to true, automatically create a temporary stash + When set to true, automatically create a temporary stash entry before the operation begins, and apply it after the operation ends. This means that you can run rebase on a dirty worktree. However, use with care: the final stash application after a @@ -3029,12 +3029,12 @@ status.submoduleSummary:: stash.showPatch:: If this is set to true, the `git stash show` command without an - option will show the stash in patch form. Defaults to false. + option will show the stash entry in patch form. Defaults to false. See description of 'show' command in linkgit:git-stash[1]. stash.showStat:: If this is set to true, the `git stash show` command without an - option will show diffstat of the stash. Defaults to true. + option will show diffstat of the stash entry. Defaults to true. See description of 'show' command in linkgit:git-stash[1]. submodule..url:: diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index e414185f5a..9db5e08f4a 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -131,7 +131,7 @@ unless you have read linkgit:git-rebase[1] carefully. --autostash:: --no-autostash:: Before starting rebase, stash local modifications away (see - linkgit:git-stash[1]) if needed, and apply the stash when + linkgit:git-stash[1]) if needed, and apply the stash entry when done. `--no-autostash` is useful to override the `rebase.autoStash` configuration variable (see linkgit:git-config[1]). + diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 53f4e14444..a5afd602d8 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -446,7 +446,7 @@ used to override and disable this setting. --autostash:: --no-autostash:: - Automatically create a temporary stash before the operation + Automatically create a temporary stash entry before the operation begins, and apply it after the operation ends. This means that you can run rebase on a dirty worktree. However, use with care: the final stash application after a successful diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 70191d06b6..00f95fee1f 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -51,18 +51,18 @@ OPTIONS save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] []:: push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message ] [--] [...]:: - Save your local modifications to a new 'stash' and roll them + Save your local modifications to a new 'stash entry' and roll them back to HEAD (in the working tree and in the index). The part is optional and gives the description along with the stashed state. + For quickly making a snapshot, you can omit "push". In this mode, non-option arguments are not allowed to prevent a misspelled -subcommand from making an unwanted stash. The two exceptions to this +subcommand from making an unwanted stash entry. The two exceptions to this are `stash -p` which acts as alias for `stash push -p` and pathspecs, which are allowed after a double hyphen `--` for disambiguation. + -When pathspec is given to 'git stash push', the new stash records the +When pathspec is given to 'git stash push', the new stash entry records the modified states only for the files that match the pathspec. The index entries and working tree files are then rolled back to the state in HEAD only for these files, too, leaving files that do not match the @@ -89,10 +89,10 @@ The `--patch` option implies `--keep-index`. You can use list []:: - List the stashes that you currently have. Each 'stash' is listed - with its name (e.g. `stash@{0}` is the latest stash, `stash@{1}` is + List the stash entries that you currently have. Each 'stash entry' is + listed with its name (e.g. `stash@{0}` is the latest entry, `stash@{1}` is the one before, etc.), the name of the branch that was current when the - stash was made, and a short description of the commit the stash was + entry was made, and a short description of the commit the entry was based on. + ---------------------------------------------------------------- @@ -105,11 +105,12 @@ command to control what is shown and how. See linkgit:git-log[1]. show []:: - Show the changes recorded in the stash as a diff between the - stashed state and its original parent. When no `` is given, - shows the latest one. By default, the command shows the diffstat, but - it will accept any format known to 'git diff' (e.g., `git stash show - -p stash@{1}` to view the second most recent stash in patch form). + Show the changes recorded in the stash entry as a diff between the + stashed contents and the commit back when the stash entry was first + created. When no `` is given, it shows the latest one. + By default, the command shows the diffstat, but it will accept any + format known to 'git diff' (e.g., `git stash show -p stash@{1}` + to view the second most recent entry in patch form). You can use stash.showStat and/or stash.showPatch config variables to change the default behavior. @@ -149,26 +150,27 @@ branch []:: + This is useful if the branch on which you ran `git stash save` has changed enough that `git stash apply` fails due to conflicts. Since -the stash is applied on top of the commit that was HEAD at the time -`git stash` was run, it restores the originally stashed state with -no conflicts. +the stash entry is applied on top of the commit that was HEAD at the +time `git stash` was run, it restores the originally stashed state +with no conflicts. clear:: - Remove all the stashed states. Note that those states will then + Remove all the stash entries. Note that those entries will then be subject to pruning, and may be impossible to recover (see 'Examples' below for a possible strategy). drop [-q|--quiet] []:: - Remove a single stashed state from the stash list. When no `` - is given, it removes the latest one. i.e. `stash@{0}`, otherwise - `` must be a valid stash log reference of the form - `stash@{}`. + Remove a single stash entry from the list of stash entries. + When no `` is given, it removes the latest one. + i.e. `stash@{0}`, otherwise `` must be a valid stash + log reference of the form `stash@{}`. create:: - Create a stash (which is a regular commit object) and return its - object name, without storing it anywhere in the ref namespace. + Create a stash entry (which is a regular commit object) and + return its object name, without storing it anywhere in the ref + namespace. This is intended to be useful for scripts. It is probably not the command you want to use; see "save" above. @@ -182,10 +184,10 @@ store:: DISCUSSION ---------- -A stash is represented as a commit whose tree records the state of the -working directory, and its first parent is the commit at `HEAD` when -the stash was created. The tree of the second parent records the -state of the index when the stash is made, and it is made a child of +A stash entry is represented as a commit whose tree records the state +of the working directory, and its first parent is the commit at `HEAD` +when the entry was created. The tree of the second parent records the +state of the index when the entry is made, and it is made a child of the `HEAD` commit. The ancestry graph looks like this: .----W @@ -269,12 +271,12 @@ $ edit/build/test remaining parts $ git commit foo -m 'Remaining parts' ---------------------------------------------------------------- -Recovering stashes that were cleared/dropped erroneously:: +Recovering stash entries that were cleared/dropped erroneously:: -If you mistakenly drop or clear stashes, they cannot be recovered +If you mistakenly drop or clear stash entries, they cannot be recovered through the normal safety mechanisms. However, you can try the -following incantation to get a list of stashes that are still in your -repository, but not reachable any more: +following incantation to get a list of stash entries that are still in +your repository, but not reachable any more: + ---------------------------------------------------------------- git fsck --unreachable | diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index dfe7d83727..9f13266a68 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -194,7 +194,7 @@ different things. * The `--index` option is used to ask a command that usually works on files in the working tree to *also* affect the index. For example, `git stash apply` usually - merges changes recorded in a stash to the working tree, + merges changes recorded in a stash entry to the working tree, but with the `--index` option, it also merges changes to the index as well. diff --git a/git-stash.sh b/git-stash.sh index 2fb651b2b8..68be4146bd 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -481,7 +481,7 @@ parse_flags_and_rev() case $# in 0) - have_stash || die "$(gettext "No stash found.")" + have_stash || die "$(gettext "No stash entries found.")" set -- ${ref_stash}@{0} ;; 1) @@ -573,7 +573,7 @@ apply_stash () { GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" && GIT_INDEX_FILE="$TMPindex" git checkout-index --all && rm -f "$TMPindex" || - die "$(gettext "Could not restore untracked files from stash")" + die "$(gettext "Could not restore untracked files from stash entry")" fi eval " @@ -627,7 +627,7 @@ pop_stash() { drop_stash "$@" else status=$? - say "$(gettext "The stash is kept in case you need it again.")" + say "$(gettext "The stash entry is kept in case you need it again.")" exit $status fi } From c1b5d0194b98bd3d0acc9cec7070808fbbe6a740 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sat, 17 Jun 2017 18:30:51 -0400 Subject: [PATCH 550/566] status: add optional stash count information Introduce '--show-stash' and its configuration option 'status.showStash' to allow git-status to show information about currently stashed entries. Signed-off-by: Liam Beguin Signed-off-by: Junio C Hamano --- Documentation/config.txt | 5 +++++ Documentation/git-status.txt | 3 +++ builtin/commit.c | 6 ++++++ t/t7508-status.sh | 32 ++++++++++++++++++++++++++++++++ wt-status.c | 24 ++++++++++++++++++++++++ wt-status.h | 1 + 6 files changed, 71 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 23b807065d..e83b0f6415 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2992,6 +2992,11 @@ status.displayCommentPrefix:: behavior of linkgit:git-status[1] in Git 1.8.4 and previous. Defaults to false. +status.showStash:: + If set to true, linkgit:git-status[1] will display the number of + entries currently stashed away. + Defaults to false. + status.showUntrackedFiles:: By default, linkgit:git-status[1] and linkgit:git-commit[1] show files which are not currently tracked by Git. Directories which diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index d70abc6afe..d47f198f15 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -32,6 +32,9 @@ OPTIONS --branch:: Show the branch and tracking info even in short-format. +--show-stash:: + Show the number of entries currently stashed away. + --porcelain[=]:: Give the output in an easy-to-parse format for scripts. This is similar to the short output, but will remain stable diff --git a/builtin/commit.c b/builtin/commit.c index ef52457eff..c089fb87e3 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1295,6 +1295,10 @@ static int git_status_config(const char *k, const char *v, void *cb) status_deferred_config.show_branch = git_config_bool(k, v); return 0; } + if (!strcmp(k, "status.showstash")) { + s->show_stash = git_config_bool(k, v); + return 0; + } if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { s->use_color = git_config_colorbool(k, v); return 0; @@ -1343,6 +1347,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL('b', "branch", &s.show_branch, N_("show branch information")), + OPT_BOOL(0, "show-stash", &s.show_stash, + N_("show stash information")), { OPTION_CALLBACK, 0, "porcelain", &status_format, N_("version"), N_("machine-readable output"), PARSE_OPT_OPTARG, opt_parse_porcelain }, diff --git a/t/t7508-status.sh b/t/t7508-status.sh index 79427840a4..7121a550c7 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1608,4 +1608,36 @@ test_expect_success 'git commit -m will commit a staged but ignored submodule' ' git config -f .gitmodules --remove-section submodule.subname ' +test_expect_success 'show stash info with "--show-stash"' ' + git reset --hard && + git stash clear && + echo 1 >file && + git add file && + git stash && + git status >expected_default && + git status --show-stash >expected_with_stash && + test_i18ngrep "^Your stash currently has 1 entry$" expected_with_stash +' + +test_expect_success 'no stash info with "--show-stash --no-show-stash"' ' + git status --show-stash --no-show-stash >expected_without_stash && + test_cmp expected_default expected_without_stash +' + +test_expect_success '"status.showStash=false" weaker than "--show-stash"' ' + git -c status.showStash=false status --show-stash >actual && + test_cmp expected_with_stash actual +' + +test_expect_success '"status.showStash=true" weaker than "--no-show-stash"' ' + git -c status.showStash=true status --no-show-stash >actual && + test_cmp expected_without_stash actual +' + +test_expect_success 'no additionnal info if no stash entries' ' + git stash clear && + git -c status.showStash=true status >actual && + test_cmp expected_without_stash actual +' + test_done diff --git a/wt-status.c b/wt-status.c index bf651f16fa..7992a73902 100644 --- a/wt-status.c +++ b/wt-status.c @@ -137,6 +137,7 @@ void wt_status_prepare(struct wt_status *s) s->untracked.strdup_strings = 1; s->ignored.strdup_strings = 1; s->show_branch = -1; /* unspecified */ + s->show_stash = 0; s->display_comment_prefix = 0; } @@ -801,6 +802,27 @@ static void wt_longstatus_print_changed(struct wt_status *s) wt_longstatus_print_trailer(s); } +static int stash_count_refs(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, + const char *message, void *cb_data) +{ + int *c = cb_data; + (*c)++; + return 0; +} + +static void wt_longstatus_print_stash_summary(struct wt_status *s) +{ + int stash_count = 0; + + for_each_reflog_ent("refs/stash", stash_count_refs, &stash_count); + if (stash_count > 0) + status_printf_ln(s, GIT_COLOR_NORMAL, + Q_("Your stash currently has %d entry", + "Your stash currently has %d entries", stash_count), + stash_count); +} + static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted) { struct child_process sm_summary = CHILD_PROCESS_INIT; @@ -1642,6 +1664,8 @@ static void wt_longstatus_print(struct wt_status *s) } else printf(_("nothing to commit, working tree clean\n")); } + if(s->show_stash) + wt_longstatus_print_stash_summary(s); } static void wt_shortstatus_unmerged(struct string_list_item *it, diff --git a/wt-status.h b/wt-status.h index 8a3864783b..d8ae2e590d 100644 --- a/wt-status.h +++ b/wt-status.h @@ -77,6 +77,7 @@ struct wt_status { unsigned colopts; int null_termination; int show_branch; + int show_stash; int hints; enum wt_status_format status_format; From 90f64f1cf5fe20bbac7353028e23f28ceca61195 Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Sat, 17 Jun 2017 18:30:52 -0400 Subject: [PATCH 551/566] glossary: define 'stash entry' Signed-off-by: Liam Beguin Signed-off-by: Junio C Hamano --- Documentation/glossary-content.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 6e991c2469..b71b943b12 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -570,6 +570,10 @@ The most notable example is `HEAD`. is created by giving the `--depth` option to linkgit:git-clone[1], and its history can be later deepened with linkgit:git-fetch[1]. +[[def_stash]]stash entry:: + An <> used to temporarily store the contents of a + <> working directory and the index for future reuse. + [[def_submodule]]submodule:: A <> that holds the history of a separate project inside another repository (the latter of From 05ec6e13aaf33b6a647e1321203a770e697eea9a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Jun 2017 12:41:12 -0700 Subject: [PATCH 552/566] Ninth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index 257f1e7d29..a71615a6b4 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -70,6 +70,22 @@ UI, Workflows & Features * Many commands learned to pay attention to submodule.recurse configuration. + * The convention for a command line is to follow "git cmdname + --options" with revisions followed by an optional "--" + disambiguator and then finally pathspecs. When "--" is not there, + we make sure early ones are all interpretable as revs (and do not + look like paths) and later ones are the other way around. A + pathspec with "magic" (e.g. ":/p/a/t/h" that matches p/a/t/h from + the top-level of the working tree, no matter what subdirectory you + are working from) are conservatively judged as "not a path", which + required disambiguation more often. The command line parser + learned to say "it's a pathspec" a bit more often when the syntax + looks like so. + (merge 2cb47ab695 jk/pathspec-magic-disambiguation later to maint). + + * Update "perl-compatible regular expression" support to enable JIT + and also allow linking with the newer PCRE v2 library. + Performance, Internal Implementation, Development Support etc. @@ -146,6 +162,12 @@ Performance, Internal Implementation, Development Support etc. optional, and silently ignore errors from open/fopen; report such errors if they are not due to missing files. + * When an existing repository is used for t/perf testing, we first + create bit-for-bit copy of it, which may grab a transient state of + the repository and freeze it into the repository used for testing, + which then may cause Git operations to fail. Single out "the index + being locked" case and forcibly drop the lock from the copy. + Also contains various documentation updates and code clean-ups. @@ -309,5 +331,12 @@ notes for details). * A flaky test has been corrected. (merge 7c2115aa07 jk/pack-idx-corruption-safety later to maint). + * "git $cmd -h" for builtin commands calls the implementation of the + command (i.e. cmd_$cmd() function) without doing any repository + set-up, and the commands that expect RUN_SETUP is done by the Git + potty needs to be prepared to show the help text without barfing. + (merge d691551192 jk/consistent-h later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 8ba74bfd7c jc/diff-tree-stale-comment later to maint). + (merge 68602c01fd sb/submodule-rm-absorb later to maint). From 674ad936bb40332e016b60c59d1982c832338493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 19 Jun 2017 22:01:48 +0000 Subject: [PATCH 553/566] grep: fix erroneously copy/pasted variable in check/assert pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix an erroneously copy/pasted check for the pcre2_jit_stack variable to check pcre2_match_context instead. The former was already checked in the preceding "if" statement. This is a trivial and obvious error introduced in my commit 94da9193a6 ("grep: add support for PCRE v2", 2017-06-01). In practice if pcre2_match_context_create() returned NULL we were likely in a situation where malloc() was returning NULL, and were thus screwed anyway, but if only the pcre2_match_context_create() call returned NULL (through some transitory bug) PCRE v2 would just allocate and supply its own context object when matching, and we'd run normally at the trivial expense of not getting a slight speedup by sharing the context object between successive matches. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- grep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grep.c b/grep.c index d0bf37858a..b197ad960c 100644 --- a/grep.c +++ b/grep.c @@ -508,7 +508,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt if (!p->pcre2_jit_stack) die("Couldn't allocate PCRE2 JIT stack"); p->pcre2_match_context = pcre2_match_context_create(NULL); - if (!p->pcre2_jit_stack) + if (!p->pcre2_match_context) die("Couldn't allocate PCRE2 match context"); pcre2_jit_stack_assign(p->pcre2_match_context, NULL, p->pcre2_jit_stack); } else if (p->pcre2_jit_on != 0) { From 4fced24712b205fb4998878b9000b9d42dc089b6 Mon Sep 17 00:00:00 2001 From: Kaartic Sivaraam Date: Wed, 21 Jun 2017 18:01:45 +0530 Subject: [PATCH 554/566] t7508: fix a broken indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the indentation from "\t " to "\t". This indenting issue was introduced when the test was added in commit 1d2f393ac9 ("status/commit: show staged submodules regardless of ignore config", 2014-04-05). Helped-by: Ævar Arnfjörð Bjarmason Signed-off-by: Kaartic Sivaraam Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/t7508-status.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t7508-status.sh b/t/t7508-status.sh index fb00e6d9b0..b5860fcb8b 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -1494,7 +1494,7 @@ EOF test_expect_success 'git commit -m will commit a staged but ignored submodule' ' git commit -uno -m message && git status -s --ignore-submodules=dirty >output && - test_i18ngrep ! "^M. sm" output && + test_i18ngrep ! "^M. sm" output && git config --remove-section submodule.subname && git config -f .gitmodules --remove-section submodule.subname ' From b24a8db14a5a94b5ca9de63eae28b8f01e3bc079 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 21 Jun 2017 09:51:30 -0400 Subject: [PATCH 555/566] docs: update 64-bit core.packedGitLimit default We bumped the default in be4ca2905 (Increase core.packedGitLimit, 2017-04-20) but never adjusted the documentation to match. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/config.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 475e874d51..3455412e69 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -662,7 +662,8 @@ core.packedGitLimit:: bytes at once to complete an operation it will unmap existing regions to reclaim virtual address space within the process. + -Default is 256 MiB on 32 bit platforms and 8 GiB on 64 bit platforms. +Default is 256 MiB on 32 bit platforms and 32 TiB (effectively +unlimited) on 64 bit platforms. This should be reasonable for all users/operating systems, except on the largest projects. You probably do not need to adjust this value. + From 968b1fe263f9dfec65be4598e4f0dd76f1692551 Mon Sep 17 00:00:00 2001 From: Marc Branchaud Date: Wed, 21 Jun 2017 09:57:38 -0400 Subject: [PATCH 556/566] auto-correct: tweak phrasing When help.autoCorrect is enabled, an invalid git command prints a warning and a continuation message, which differs depending on whether or not the value of help.autoCorrect is positive or negative. With help.autoCorrect = 15: WARNING: You called a Git command named 'lgo', which does not exist. Continuing under the assumption that you meant 'log' in 1.5 seconds automatically... With help.autoCorrect < 0: WARNING: You called a Git command named 'lgo', which does not exist. Continuing under the assumption that you meant 'log' The continuation message's phrasing is awkward. This commit cleans it up. As a bonus, we now use full-sentence strings which make translation easier. With help.autoCorrect = 15: WARNING: You called a Git command named 'lgo', which does not exist. Continuing in 1.5 seconds, assuming that you meant 'log'. With help.autoCorrect < 0: WARNING: You called a Git command named 'lgo', which does not exist. Continuing under the assumption that you meant 'log'. Signed-off-by: Marc Branchaud Signed-off-by: Junio C Hamano --- help.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/help.c b/help.c index 2ff3b5a774..2a8ae9758d 100644 --- a/help.c +++ b/help.c @@ -386,12 +386,18 @@ const char *help_unknown_cmd(const char *cmd) clean_cmdnames(&main_cmds); fprintf_ln(stderr, _("WARNING: You called a Git command named '%s', " - "which does not exist.\n" - "Continuing under the assumption that you meant '%s'"), - cmd, assumed); - if (autocorrect > 0) { - fprintf_ln(stderr, _("in %0.1f seconds automatically..."), - (float)autocorrect/10.0); + "which does not exist."), + cmd); + if (autocorrect < 0) + fprintf_ln(stderr, + _("Continuing under the assumption that " + "you meant '%s'."), + assumed); + else { + fprintf_ln(stderr, + _("Continuing in %0.1f seconds, " + "assuming that you meant '%s'."), + (float)autocorrect/10.0, assumed); sleep_millisec(autocorrect * 100); } return assumed; From d5addcf522deb05d259ecbc0946584d977879565 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 21 Jun 2017 15:26:36 -0400 Subject: [PATCH 557/566] add--interactive: handle EOF in prompt_yesno The prompt_yesno function loops indefinitely waiting for a "y" or "n" response. But it doesn't handle EOF, meaning that we can end up in an infinite loop of reading EOF from stdin. One way to simulate that is with: echo e | GIT_EDITOR='echo corrupt >' git add -p Let's break out of the loop and propagate the undef to the caller. Without modifying the callers that effectively turns it into a "no" response. This is reasonable for both of the current callers, and it leaves room for any future caller to check for undef explicitly. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 1 + 1 file changed, 1 insertion(+) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 4e0ab5a9bc..7c95324737 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -1152,6 +1152,7 @@ sub prompt_yesno { while (1) { print colored $prompt_color, $prompt; my $line = prompt_single_character; + return undef unless defined $line; return 0 if $line =~ /^n/i; return 1 if $line =~ /^y/i; } From d85d7ecb80ebc93f7380b4196c303756ee051668 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 21 Jun 2017 15:28:59 -0400 Subject: [PATCH 558/566] add--interactive: quote commentChar regex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since c9d961647 (i18n: add--interactive: mark edit_hunk_manually message for translation, 2016-12-14), when the user asks to edit a hunk manually, we respect core.commentChar in generating the edit instructions. However, when we then strip out comment lines, we use a simple regex like: /^$commentChar/ If your chosen comment character is a regex metacharacter, then that will behave in a confusing manner ("$", for instance, would only eliminate blank lines, not actual comment lines). We can fix that by telling perl not to respect metacharacters. Reported-by: Christian Rösch Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-add--interactive.perl | 2 +- t/t3701-add-interactive.sh | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 7c95324737..395dd5eb4b 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -1097,7 +1097,7 @@ EOF2 open $fh, '<', $hunkfile or die sprintf(__("failed to open hunk edit file for reading: %s"), $!); - my @newtext = grep { !/^$comment_line_char/ } <$fh>; + my @newtext = grep { !/^\Q$comment_line_char\E/ } <$fh>; close $fh; unlink $hunkfile; diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index deae948c76..2bfd41f06e 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -380,4 +380,12 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_cmp expected diff ' +test_expect_success 'hunk-editing handles custom comment char' ' + git reset --hard && + echo change >>file && + test_config core.commentChar "\$" && + echo e | GIT_EDITOR=true git add -p && + git diff --exit-code +' + test_done From 5402b1352f5181247405fbff1887008a0cb3b04a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 22 Jun 2017 14:18:02 -0700 Subject: [PATCH 559/566] Tenth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index a71615a6b4..9c252cdc4f 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -86,6 +86,10 @@ UI, Workflows & Features * Update "perl-compatible regular expression" support to enable JIT and also allow linking with the newer PCRE v2 library. + * "filter-branch" learned a pseudo filter "--setup" that can be used + to define a common function/variable that can be used by other + filters. + Performance, Internal Implementation, Development Support etc. @@ -168,6 +172,13 @@ Performance, Internal Implementation, Development Support etc. which then may cause Git operations to fail. Single out "the index being locked" case and forcibly drop the lock from the copy. + * Three instances of the same helper function have been consolidated + to one. + (merge e0556a928f pc/dir-count-slashes later to maint). + + * "fast-import" uses a default pack chain depth that is consistent + with other parts of the system. + Also contains various documentation updates and code clean-ups. @@ -337,6 +348,23 @@ notes for details). potty needs to be prepared to show the help text without barfing. (merge d691551192 jk/consistent-h later to maint). + * Help contributors that visit us at GitHub. + (merge 0b1bb0c032 ls/github later to maint). + + * "git stash push " did not work from a subdirectory at all. + Bugfix for a topic in v2.13 + (merge 22fc703ec9 ps/stash-push-pathspec-fix later to maint). + + * As there is no portable way to pass timezone information to + strftime, some output format from "git log" and friends are + impossible to produce. Teach our own strbuf_addftime to replace %z + and %Z with caller-supplied values to help working around this. + (merge 6eced3ec5e rs/strbuf-addftime-zZ later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 8ba74bfd7c jc/diff-tree-stale-comment later to maint). (merge 68602c01fd sb/submodule-rm-absorb later to maint). + (merge 68241cb9dd sb/t4005-modernize later to maint). + (merge ae52d57f0b km/test-mailinfo-b-failure later to maint). + (merge 8b1d9136e1 sg/revision-parser-skip-prefix later to maint). + (merge bb8efa1772 sd/t3200-branch-m-test later to maint). From 64719b115d61582fa501690ee6caff4c478b4b1a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 24 Jun 2017 12:02:39 -0700 Subject: [PATCH 560/566] Revert "split-index: add and use unshare_split_index()" This reverts commit f9d7abec2ad2f9eb3d8873169cc28c34273df082; see public-inbox.org/git/CAP8UFD0bOfzY-_hBDKddOcJdPUpP2KEVaX_SrCgvAMYAHtseiQ@mail.gmail.com --- read-cache.c | 10 +++++++-- split-index.c | 57 ++++++++++++--------------------------------------- split-index.h | 1 - 3 files changed, 21 insertions(+), 47 deletions(-) diff --git a/read-cache.c b/read-cache.c index 8da84ae2d1..0d0081a11b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1877,9 +1877,15 @@ int discard_index(struct index_state *istate) { int i; - unshare_split_index(istate, 1); - for (i = 0; i < istate->cache_nr; i++) + for (i = 0; i < istate->cache_nr; i++) { + if (istate->cache[i]->index && + istate->split_index && + istate->split_index->base && + istate->cache[i]->index <= istate->split_index->base->cache_nr && + istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1]) + continue; free(istate->cache[i]); + } resolve_undo_clear_index(istate); istate->cache_nr = 0; istate->cache_changed = 0; diff --git a/split-index.c b/split-index.c index 49bd197f71..f519e60f87 100644 --- a/split-index.c +++ b/split-index.c @@ -73,17 +73,10 @@ void move_cache_to_base_index(struct index_state *istate) int i; /* - * If "si" is shared with another index_state (e.g. by - * unpack-trees code), we will need to duplicate split_index - * struct. It's not happening now though, luckily. + * do not delete old si->base, its index entries may be shared + * with istate->cache[]. Accept a bit of leaking here because + * this code is only used by short-lived update-index. */ - assert(si->refcount <= 1); - - unshare_split_index(istate, 0); - if (si->base) { - discard_index(si->base); - free(si->base); - } si->base = xcalloc(1, sizeof(*si->base)); si->base->version = istate->version; /* zero timestamp disables racy test in ce_write_index() */ @@ -282,41 +275,11 @@ void finish_writing_split_index(struct index_state *istate) istate->cache_nr = si->saved_cache_nr; } -void unshare_split_index(struct index_state *istate, int discard) -{ - struct split_index *si = istate->split_index; - int i; - - if (!si || !si->base) - return; - - for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; - struct cache_entry *new = NULL; - - if (!ce->index || - ce->index > si->base->cache_nr || - ce != si->base->cache[ce->index - 1]) - continue; - - if (!discard) { - int len = ce_namelen(ce); - new = xcalloc(1, cache_entry_size(len)); - copy_cache_entry(new, ce); - memcpy(new->name, ce->name, len); - new->index = 0; - } - istate->cache[i] = new; - } -} - - void discard_split_index(struct index_state *istate) { struct split_index *si = istate->split_index; if (!si) return; - unshare_split_index(istate, 0); istate->split_index = NULL; si->refcount--; if (si->refcount) @@ -365,8 +328,14 @@ void add_split_index(struct index_state *istate) void remove_split_index(struct index_state *istate) { - if (!istate->split_index) - return; - discard_split_index(istate); - istate->cache_changed |= SOMETHING_CHANGED; + if (istate->split_index) { + /* + * can't discard_split_index(&the_index); because that + * will destroy split_index->base->cache[], which may + * be shared with the_index.cache[]. So yeah we're + * leaking a bit here. + */ + istate->split_index = NULL; + istate->cache_changed |= SOMETHING_CHANGED; + } } diff --git a/split-index.h b/split-index.h index 65c0f09b2b..df91c1bda8 100644 --- a/split-index.h +++ b/split-index.h @@ -33,6 +33,5 @@ void finish_writing_split_index(struct index_state *istate); void discard_split_index(struct index_state *istate); void add_split_index(struct index_state *istate); void remove_split_index(struct index_state *istate); -void unshare_split_index(struct index_state *istate, int discard); #endif From a2ba37c57b7e1f72900b0c1183ef38e52c3f9d27 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 24 Jun 2017 14:34:11 -0700 Subject: [PATCH 561/566] Eleventh batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 61 +++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index 9c252cdc4f..96041652f0 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -38,19 +38,16 @@ UI, Workflows & Features * "git archive --format=zip" learned to use zip64 extension when necessary to go beyond the 4GB limit. - (merge 867e40ff3a rs/large-zip later to maint). * "git reset" learned "--recurse-submodules" option. * "git diff --submodule=diff" now recurses into nested submodules. - (merge 5a5221427c jk/diff-submodule-diff-inline later to maint). * "git repack" learned to accept the --threads= option and pass it to pack-objects. * "git send-email" learned to run sendemail-validate hook to inspect and reject a message before sending it out. - (merge 177409e589 jt/send-email-validate-hook later to maint). * There is no good reason why "git fetch $there $sha1" should fail when the $sha1 names an object at the tip of an advertised ref, @@ -81,7 +78,6 @@ UI, Workflows & Features required disambiguation more often. The command line parser learned to say "it's a pathspec" a bit more often when the syntax looks like so. - (merge 2cb47ab695 jk/pathspec-magic-disambiguation later to maint). * Update "perl-compatible regular expression" support to enable JIT and also allow linking with the newer PCRE v2 library. @@ -90,6 +86,11 @@ UI, Workflows & Features to define a common function/variable that can be used by other filters. + * Using "git add d/i/r" when d/i/r is the top of the working tree of + a separate repository would create a gitlink in the index, which + would appear as a not-quite-initialized submodule to others. We + learned to give warnings when this happens. + Performance, Internal Implementation, Development Support etc. @@ -99,7 +100,6 @@ Performance, Internal Implementation, Development Support etc. * Code to update the cache-tree has been tightened so that we won't accidentally write out any 0{40} entry in the tree object. - (merge a96d3cc3f6 jk/no-null-sha1-in-cache-tree later to maint). * Attempt to allow us notice "fishy" situation where we fail to remove the temporary directory used during the test. @@ -122,14 +122,11 @@ Performance, Internal Implementation, Development Support etc. * Simplify parse_pathspec() codepath and stop it from looking at the default in-core index. - (merge 08de9151a8 bw/pathspec-sans-the-index later to maint). * Add perf-test for wildmatch. - (merge 62ca75a6b9 ab/perf-wildmatch later to maint). * Code from "conversion using external process" codepath has been extracted to a separate sub-process.[ch] module. - (merge 4f2a2e9f0e bp/sub-process-convert-filter later to maint). * When "git checkout", "git merge", etc. manipulates the in-core index, various pieces of information in the index extensions are @@ -140,7 +137,6 @@ Performance, Internal Implementation, Development Support etc. cache is properly invalidated). * The internal implementation of "git grep" has seen some clean-up. - (merge 8df4c2953f ab/grep-preparatory-cleanup later to maint). * Update the C style recommendation for notes for translators, as recent versions of gettext tools can work with our style of @@ -174,11 +170,23 @@ Performance, Internal Implementation, Development Support etc. * Three instances of the same helper function have been consolidated to one. - (merge e0556a928f pc/dir-count-slashes later to maint). * "fast-import" uses a default pack chain depth that is consistent with other parts of the system. + * A new test to show the interaction between the pattern [^a-z] + (which matches '/') and a slash in a path has been added. The + pattern should not match the slash with "pathmatch", but should + with "wildmatch". + + * The 'diff-highlight' program (in contrib/) has been restructured + for easier reuse by an external project 'diff-so-fancy'. + (merge 0c977dbc81 jk/diff-highlight-module later to maint). + + * A common pattern to free a piece of memory and assign NULL to the + pointer that used to point at it has been replaced with a new + FREE_AND_NULL() macro. + Also contains various documentation updates and code clean-ups. @@ -227,11 +235,6 @@ notes for details). * "git checkout --recurse-submodules" did not quite work with a submodule that itself has submodules. - * Plug some leaks and updates internal API used to implement the - split index feature to make it easier to avoid such a leak in the - future. - (merge de950c5773 nd/split-index-unshare later to maint). - * "pack-objects" can stream a slice of an existing packfile out when the pack bitmap can tell that the reachable objects are all needed in the output, without inspecting individual objects. This @@ -361,6 +364,32 @@ notes for details). and %Z with caller-supplied values to help working around this. (merge 6eced3ec5e rs/strbuf-addftime-zZ later to maint). + * "git mergetool" learned to work around a wrapper MacOS X adds + around underlying meld. + (merge 0af85f84bd da/mergetools-meld-output-opt-on-macos later to maint). + + * An example in documentation that does not work in multi worktree + configuration has been corrected. + (merge 773a88914f ah/doc-gitattributes-empty-index later to maint). + + * The pretty-format specifiers like '%h', '%t', etc. had an + optimization that no longer works correctly. In preparation/hope + of getting it correctly implemented, first discard the optimization + that is broken. + (merge fe9e2aefd4 rs/pretty-add-again later to maint). + + * The code to pick up and execute command alias definition from the + configuration used to switch to the top of the working tree and + then come back when the expanded alias was executed, which was + unnecessarilyl complex. Attempt to simplify the logic by using the + early-config mechanism that does not chdir around. + (merge a9bcf6586d js/alias-early-config later to maint). + + * Fix configuration codepath to pay proper attention to commondir + that is used in multi-worktree situation, and isolate config API + into its own header file. + (merge dc8441fdb4 bw/config-h later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 8ba74bfd7c jc/diff-tree-stale-comment later to maint). (merge 68602c01fd sb/submodule-rm-absorb later to maint). @@ -368,3 +397,5 @@ notes for details). (merge ae52d57f0b km/test-mailinfo-b-failure later to maint). (merge 8b1d9136e1 sg/revision-parser-skip-prefix later to maint). (merge bb8efa1772 sd/t3200-branch-m-test later to maint). + (merge 8abc09c0e3 sg/doc-pretty-formats later to maint). + (merge 680b469e83 sn/reset-doc-typofix later to maint). From 8c8e978f5719c6a58fb998742207bf907f963143 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 24 Jun 2017 15:31:36 -0700 Subject: [PATCH 562/566] Git 2.13.2 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.13.2.txt | 17 +++++++++++++++++ GIT-VERSION-GEN | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes/2.13.2.txt b/Documentation/RelNotes/2.13.2.txt index c8ba0fa16f..8c2b20071e 100644 --- a/Documentation/RelNotes/2.13.2.txt +++ b/Documentation/RelNotes/2.13.2.txt @@ -34,4 +34,21 @@ Fixes since v2.13.1 * "git pull --rebase --autostash" didn't auto-stash when the local history fast-forwards to the upstream. + * "git describe --contains" penalized light-weight tags so much that + they were almost never considered. Instead, give them about the + same chance to be considered as an annotated tag that is the same + age as the underlying commit would. + + * The result from "git diff" that compares two blobs, e.g. "git diff + $commit1:$path $commit2:$path", used to be shown with the full + object name as given on the command line, but it is more natural to + use the $path in the output and use it to look up .gitattributes. + + * A flaky test has been corrected. + + * Help contributors that visit us at GitHub. + + * "git stash push " did not work from a subdirectory at all. + Bugfix for a topic in v2.13 + Also contains various documentation updates and code clean-ups. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 5b6722840e..c26b01f60b 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.13.1 +DEF_VER=v2.13.2 LF=' ' From e0aaa1b6532cfce93d87af9bc813fb2e7a7ce9d7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 Jun 2017 14:12:46 -0700 Subject: [PATCH 563/566] Twelfth batch for 2.14 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.14.0.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt index f57a7ecbf6..8b80058a85 100644 --- a/Documentation/RelNotes/2.14.0.txt +++ b/Documentation/RelNotes/2.14.0.txt @@ -83,7 +83,7 @@ UI, Workflows & Features and also allow linking with the newer PCRE v2 library. * "filter-branch" learned a pseudo filter "--setup" that can be used - to define a common function/variable that can be used by other + to define common functions/variables that can be used by other filters. * Using "git add d/i/r" when d/i/r is the top of the working tree of @@ -91,6 +91,9 @@ UI, Workflows & Features would appear as a not-quite-initialized submodule to others. We learned to give warnings when this happens. + * "git status" learned to optionally give how many stash entries the + user has in its output. + Performance, Internal Implementation, Development Support etc. @@ -385,5 +388,12 @@ notes for details). into its own header file. (merge dc8441fdb4 bw/config-h later to maint). + * "git add -p" were updated in 2.12 timeframe to cope with custom + core.commentchar but the implementation was buggy and a + metacharacter like $ and * did not work. + (merge d85d7ecb80 jk/add-p-commentchar-fix later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 68241cb9dd sb/t4005-modernize later to maint). + (merge 4fced24712 ks/t7508-indent-fix later to maint). + (merge 968b1fe263 mb/reword-autocomplete-message later to maint). From 7663cdc86c860d5b5293a1dd4b0fb6c4e006d08e Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 30 Jun 2017 12:14:05 -0700 Subject: [PATCH 564/566] hashmap.h: compare function has access to a data field When using the hashmap a common need is to have access to caller provided data in the compare function. A couple of times we abuse the keydata field to pass in the data needed. This happens for example in patch-ids.c. This patch changes the function signature of the compare function to have one more void pointer available. The pointer given for each invocation of the compare function must be defined in the init function of the hashmap and is just passed through. Documentation of this new feature is deferred to a later patch. This is a rather mechanical conversion, just adding the new pass-through parameter. However while at it improve the naming of the fields of all compare functions used by hashmaps by ensuring unused parameters are prefixed with 'unused_' and naming the parameters what they are (instead of 'unused' make it 'unused_keydata'). Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- attr.c | 7 ++++--- builtin/describe.c | 8 +++++--- builtin/difftool.c | 24 +++++++++++++++--------- builtin/fast-export.c | 7 ++++--- config.c | 9 ++++++--- convert.c | 3 ++- diffcore-rename.c | 2 +- hashmap.c | 17 ++++++++++++----- hashmap.h | 12 ++++++++---- name-hash.c | 16 ++++++++++------ oidset.c | 5 +++-- patch-ids.c | 6 ++++-- refs.c | 5 +++-- remote.c | 7 +++++-- sha1_file.c | 5 +++-- sub-process.c | 7 ++++--- sub-process.h | 6 ++++-- submodule-config.c | 14 ++++++++------ t/helper/test-hashmap.c | 19 ++++++++++++------- 19 files changed, 113 insertions(+), 66 deletions(-) diff --git a/attr.c b/attr.c index 37454999d2..56961f0236 100644 --- a/attr.c +++ b/attr.c @@ -76,9 +76,10 @@ struct attr_hash_entry { }; /* attr_hashmap comparison function */ -static int attr_hash_entry_cmp(const struct attr_hash_entry *a, +static int attr_hash_entry_cmp(void *unused_cmp_data, + const struct attr_hash_entry *a, const struct attr_hash_entry *b, - void *unused) + void *unused_keydata) { return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen); } @@ -86,7 +87,7 @@ static int attr_hash_entry_cmp(const struct attr_hash_entry *a, /* Initialize an 'attr_hashmap' object */ static void attr_hashmap_init(struct attr_hashmap *map) { - hashmap_init(&map->map, (hashmap_cmp_fn) attr_hash_entry_cmp, 0); + hashmap_init(&map->map, (hashmap_cmp_fn) attr_hash_entry_cmp, NULL, 0); } /* diff --git a/builtin/describe.c b/builtin/describe.c index 70eb144608..8868f00ed0 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -54,8 +54,10 @@ static const char *prio_names[] = { N_("head"), N_("lightweight"), N_("annotated"), }; -static int commit_name_cmp(const struct commit_name *cn1, - const struct commit_name *cn2, const void *peeled) +static int commit_name_cmp(const void *unused_cmp_data, + const struct commit_name *cn1, + const struct commit_name *cn2, + const void *peeled) { return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled); } @@ -501,7 +503,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) return cmd_name_rev(args.argc, args.argv, prefix); } - hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, 0); + hashmap_init(&names, (hashmap_cmp_fn) commit_name_cmp, NULL, 0); for_each_rawref(get_name, NULL); if (!names.size && !always) die(_("No names found, cannot describe anything.")); diff --git a/builtin/difftool.c b/builtin/difftool.c index 9199227f6e..a1a26ba891 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -130,8 +130,10 @@ struct working_tree_entry { char path[FLEX_ARRAY]; }; -static int working_tree_entry_cmp(struct working_tree_entry *a, - struct working_tree_entry *b, void *keydata) +static int working_tree_entry_cmp(const void *unused_cmp_data, + struct working_tree_entry *a, + struct working_tree_entry *b, + void *unused_keydata) { return strcmp(a->path, b->path); } @@ -146,7 +148,9 @@ struct pair_entry { const char path[FLEX_ARRAY]; }; -static int pair_cmp(struct pair_entry *a, struct pair_entry *b, void *keydata) +static int pair_cmp(const void *unused_cmp_data, + struct pair_entry *a, struct pair_entry *b, + void *unused_keydata) { return strcmp(a->path, b->path); } @@ -174,7 +178,9 @@ struct path_entry { char path[FLEX_ARRAY]; }; -static int path_entry_cmp(struct path_entry *a, struct path_entry *b, void *key) +static int path_entry_cmp(const void *unused_cmp_data, + struct path_entry *a, struct path_entry *b, + void *key) { return strcmp(a->path, key ? key : b->path); } @@ -367,9 +373,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, wtdir_len = wtdir.len; hashmap_init(&working_tree_dups, - (hashmap_cmp_fn)working_tree_entry_cmp, 0); - hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, 0); - hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, 0); + (hashmap_cmp_fn)working_tree_entry_cmp, NULL, 0); + hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, NULL, 0); + hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, NULL, 0); child.no_stdin = 1; child.git_cmd = 1; @@ -580,9 +586,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, * files through the symlink. */ hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp, - wtindex.cache_nr); + NULL, wtindex.cache_nr); hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp, - wtindex.cache_nr); + NULL, wtindex.cache_nr); for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 12d501bfde..d412c0a8f3 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -93,8 +93,9 @@ struct anonymized_entry { size_t anon_len; }; -static int anonymized_entry_cmp(const void *va, const void *vb, - const void *data) +static int anonymized_entry_cmp(const void *unused_cmp_data, + const void *va, const void *vb, + const void *unused_keydata) { const struct anonymized_entry *a = va, *b = vb; return a->orig_len != b->orig_len || @@ -113,7 +114,7 @@ static const void *anonymize_mem(struct hashmap *map, struct anonymized_entry key, *ret; if (!map->cmpfn) - hashmap_init(map, anonymized_entry_cmp, 0); + hashmap_init(map, anonymized_entry_cmp, NULL, 0); hashmap_entry_init(&key, memhash(orig, *len)); key.orig = orig; diff --git a/config.c b/config.c index 1cd40a5fe6..4a31e31ac3 100644 --- a/config.c +++ b/config.c @@ -1753,15 +1753,18 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha return 0; } -static int config_set_element_cmp(const struct config_set_element *e1, - const struct config_set_element *e2, const void *unused) +static int config_set_element_cmp(const void *unused_cmp_data, + const struct config_set_element *e1, + const struct config_set_element *e2, + const void *unused_keydata) { return strcmp(e1->key, e2->key); } void git_configset_init(struct config_set *cs) { - hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0); + hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, + NULL, 0); cs->hash_initialized = 1; cs->list.nr = 0; cs->list.alloc = 0; diff --git a/convert.c b/convert.c index 7d2a519daf..deaf0ba7b3 100644 --- a/convert.c +++ b/convert.c @@ -583,7 +583,8 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len if (!subprocess_map_initialized) { subprocess_map_initialized = 1; - hashmap_init(&subprocess_map, (hashmap_cmp_fn) cmd2process_cmp, 0); + hashmap_init(&subprocess_map, (hashmap_cmp_fn) cmd2process_cmp, + NULL, 0); entry = NULL; } else { entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd); diff --git a/diffcore-rename.c b/diffcore-rename.c index 1e4678b7d7..786f389498 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -341,7 +341,7 @@ static int find_exact_renames(struct diff_options *options) /* Add all sources to the hash table in reverse order, because * later on they will be retrieved in LIFO order. */ - hashmap_init(&file_table, NULL, rename_src_nr); + hashmap_init(&file_table, NULL, NULL, rename_src_nr); for (i = rename_src_nr-1; i >= 0; i--) insert_file_table(&file_table, i, rename_src[i].p->one); diff --git a/hashmap.c b/hashmap.c index 7d1044eb5d..9b6a12859b 100644 --- a/hashmap.c +++ b/hashmap.c @@ -95,7 +95,9 @@ static inline int entry_equals(const struct hashmap *map, const struct hashmap_entry *e1, const struct hashmap_entry *e2, const void *keydata) { - return (e1 == e2) || (e1->hash == e2->hash && !map->cmpfn(e1, e2, keydata)); + return (e1 == e2) || + (e1->hash == e2->hash && + !map->cmpfn(map->cmpfn_data, e1, e2, keydata)); } static inline unsigned int bucket(const struct hashmap *map, @@ -140,19 +142,23 @@ static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map, return e; } -static int always_equal(const void *unused1, const void *unused2, const void *unused3) +static int always_equal(const void *unused_cmp_data, + const void *unused1, + const void *unused2, + const void *unused_keydata) { return 0; } void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, - size_t initial_size) + const void *cmpfn_data, size_t initial_size) { unsigned int size = HASHMAP_INITIAL_SIZE; memset(map, 0, sizeof(*map)); map->cmpfn = equals_function ? equals_function : always_equal; + map->cmpfn_data = cmpfn_data; /* calculate initial table size and allocate the table */ initial_size = (unsigned int) ((uint64_t) initial_size * 100 @@ -260,7 +266,8 @@ struct pool_entry { unsigned char data[FLEX_ARRAY]; }; -static int pool_entry_cmp(const struct pool_entry *e1, +static int pool_entry_cmp(const void *unused_cmp_data, + const struct pool_entry *e1, const struct pool_entry *e2, const unsigned char *keydata) { @@ -275,7 +282,7 @@ const void *memintern(const void *data, size_t len) /* initialize string pool hashmap */ if (!map.tablesize) - hashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, 0); + hashmap_init(&map, (hashmap_cmp_fn) pool_entry_cmp, NULL, 0); /* lookup interned string in pool */ hashmap_entry_init(&key, memhash(data, len)); diff --git a/hashmap.h b/hashmap.h index de6022a3a9..aaf09e047e 100644 --- a/hashmap.h +++ b/hashmap.h @@ -32,12 +32,14 @@ struct hashmap_entry { unsigned int hash; }; -typedef int (*hashmap_cmp_fn)(const void *entry, const void *entry_or_key, - const void *keydata); +typedef int (*hashmap_cmp_fn)(const void *hashmap_cmp_fn_data, + const void *entry, const void *entry_or_key, + const void *keydata); struct hashmap { struct hashmap_entry **table; hashmap_cmp_fn cmpfn; + const void *cmpfn_data; unsigned int size, tablesize, grow_at, shrink_at; unsigned disallow_rehash : 1; }; @@ -50,8 +52,10 @@ struct hashmap_iter { /* hashmap functions */ -extern void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, - size_t initial_size); +extern void hashmap_init(struct hashmap *map, + hashmap_cmp_fn equals_function, + const void *equals_function_data, + size_t initial_size); extern void hashmap_free(struct hashmap *map, int free_entries); /* hashmap_entry functions */ diff --git a/name-hash.c b/name-hash.c index 39309efb7f..0e10f3eab8 100644 --- a/name-hash.c +++ b/name-hash.c @@ -16,8 +16,10 @@ struct dir_entry { char name[FLEX_ARRAY]; }; -static int dir_entry_cmp(const struct dir_entry *e1, - const struct dir_entry *e2, const char *name) +static int dir_entry_cmp(const void *unused_cmp_data, + const struct dir_entry *e1, + const struct dir_entry *e2, + const char *name) { return e1->namelen != e2->namelen || strncasecmp(e1->name, name ? name : e2->name, e1->namelen); @@ -107,8 +109,10 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce) add_dir_entry(istate, ce); } -static int cache_entry_cmp(const struct cache_entry *ce1, - const struct cache_entry *ce2, const void *remove) +static int cache_entry_cmp(const void *unused_cmp_data, + const struct cache_entry *ce1, + const struct cache_entry *ce2, + const void *remove) { /* * For remove_name_hash, find the exact entry (pointer equality); for @@ -571,9 +575,9 @@ static void lazy_init_name_hash(struct index_state *istate) if (istate->name_hash_initialized) return; hashmap_init(&istate->name_hash, (hashmap_cmp_fn) cache_entry_cmp, - istate->cache_nr); + NULL, istate->cache_nr); hashmap_init(&istate->dir_hash, (hashmap_cmp_fn) dir_entry_cmp, - istate->cache_nr); + NULL, istate->cache_nr); if (lookup_lazy_params(istate)) { hashmap_disallow_rehash(&istate->dir_hash, 1); diff --git a/oidset.c b/oidset.c index ac169f05d3..a6a08ba52a 100644 --- a/oidset.c +++ b/oidset.c @@ -6,7 +6,8 @@ struct oidset_entry { struct object_id oid; }; -static int oidset_hashcmp(const void *va, const void *vb, +static int oidset_hashcmp(const void *unused_cmp_data, + const void *va, const void *vb, const void *vkey) { const struct oidset_entry *a = va, *b = vb; @@ -30,7 +31,7 @@ int oidset_insert(struct oidset *set, const struct object_id *oid) struct oidset_entry *entry; if (!set->map.cmpfn) - hashmap_init(&set->map, oidset_hashcmp, 0); + hashmap_init(&set->map, oidset_hashcmp, NULL, 0); if (oidset_contains(set, oid)) return 1; diff --git a/patch-ids.c b/patch-ids.c index 9c0ab9e67a..815c115811 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -35,7 +35,8 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, * the side of safety. The actual value being negative does not have * any significance; only that it is non-zero matters. */ -static int patch_id_cmp(struct patch_id *a, +static int patch_id_cmp(const void *unused_cmp_data, + struct patch_id *a, struct patch_id *b, struct diff_options *opt) { @@ -57,7 +58,8 @@ int init_patch_ids(struct patch_ids *ids) ids->diffopts.detect_rename = 0; DIFF_OPT_SET(&ids->diffopts, RECURSIVE); diff_setup_done(&ids->diffopts); - hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp, 256); + hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp, + NULL, 256); return 0; } diff --git a/refs.c b/refs.c index 88658ba769..eef1a7b67d 100644 --- a/refs.c +++ b/refs.c @@ -1525,7 +1525,8 @@ struct ref_store_hash_entry char name[FLEX_ARRAY]; }; -static int ref_store_hash_cmp(const void *entry, const void *entry_or_key, +static int ref_store_hash_cmp(const void *unused_cmp_data, + const void *entry, const void *entry_or_key, const void *keydata) { const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key; @@ -1608,7 +1609,7 @@ static void register_ref_store_map(struct hashmap *map, const char *name) { if (!map->tablesize) - hashmap_init(map, ref_store_hash_cmp, 0); + hashmap_init(map, ref_store_hash_cmp, NULL, 0); if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs))) die("BUG: %s ref_store '%s' initialized twice", type, name); diff --git a/remote.c b/remote.c index d87482573d..60d0043921 100644 --- a/remote.c +++ b/remote.c @@ -133,7 +133,10 @@ struct remotes_hash_key { int len; }; -static int remotes_hash_cmp(const struct remote *a, const struct remote *b, const struct remotes_hash_key *key) +static int remotes_hash_cmp(const void *unused_cmp_data, + const struct remote *a, + const struct remote *b, + const struct remotes_hash_key *key) { if (key) return strncmp(a->name, key->str, key->len) || a->name[key->len]; @@ -144,7 +147,7 @@ static int remotes_hash_cmp(const struct remote *a, const struct remote *b, cons static inline void init_remotes_hash(void) { if (!remotes_hash.cmpfn) - hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, 0); + hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, NULL, 0); } static struct remote *make_remote(const char *name, int len) diff --git a/sha1_file.c b/sha1_file.c index fb1fd809dc..8c523a5b74 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2389,7 +2389,8 @@ static int delta_base_cache_key_eq(const struct delta_base_cache_key *a, return a->p == b->p && a->base_offset == b->base_offset; } -static int delta_base_cache_hash_cmp(const void *va, const void *vb, +static int delta_base_cache_hash_cmp(const void *unused_cmp_data, + const void *va, const void *vb, const void *vkey) { const struct delta_base_cache_entry *a = va, *b = vb; @@ -2472,7 +2473,7 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset, list_add_tail(&ent->lru, &delta_base_cache_lru); if (!delta_base_cache.cmpfn) - hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, 0); + hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0); hashmap_entry_init(ent, pack_entry_hash(p, base_offset)); hashmap_add(&delta_base_cache, ent); } diff --git a/sub-process.c b/sub-process.c index 92f8aea70a..a3cfab1a9d 100644 --- a/sub-process.c +++ b/sub-process.c @@ -5,9 +5,10 @@ #include "sigchain.h" #include "pkt-line.h" -int cmd2process_cmp(const struct subprocess_entry *e1, - const struct subprocess_entry *e2, - const void *unused) +int cmd2process_cmp(const void *unused_cmp_data, + const struct subprocess_entry *e1, + const struct subprocess_entry *e2, + const void *unused_keydata) { return strcmp(e1->cmd, e2->cmd); } diff --git a/sub-process.h b/sub-process.h index d9a45cd359..96a2cca360 100644 --- a/sub-process.h +++ b/sub-process.h @@ -20,8 +20,10 @@ struct subprocess_entry { /* subprocess functions */ -int cmd2process_cmp(const struct subprocess_entry *e1, - const struct subprocess_entry *e2, const void *unused); +extern int cmd2process_cmp(const void *unused_cmp_data, + const struct subprocess_entry *e1, + const struct subprocess_entry *e2, + const void *unused_keydata); typedef int(*subprocess_start_fn)(struct subprocess_entry *entry); int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd, diff --git a/submodule-config.c b/submodule-config.c index d8f8d5ea32..0e1126183d 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -34,17 +34,19 @@ enum lookup_type { static struct submodule_cache the_submodule_cache; static int is_cache_init; -static int config_path_cmp(const struct submodule_entry *a, +static int config_path_cmp(const void *unused_cmp_data, + const struct submodule_entry *a, const struct submodule_entry *b, - const void *unused) + const void *unused_keydata) { return strcmp(a->config->path, b->config->path) || hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1); } -static int config_name_cmp(const struct submodule_entry *a, +static int config_name_cmp(const void *unused_cmp_data, + const struct submodule_entry *a, const struct submodule_entry *b, - const void *unused) + const void *unused_keydata) { return strcmp(a->config->name, b->config->name) || hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1); @@ -52,8 +54,8 @@ static int config_name_cmp(const struct submodule_entry *a, static void cache_init(struct submodule_cache *cache) { - hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0); - hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0); + hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, NULL, 0); + hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, NULL, 0); } static void free_one_config(struct submodule_entry *entry) diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c index 7aa9440e27..095d7395f3 100644 --- a/t/helper/test-hashmap.c +++ b/t/helper/test-hashmap.c @@ -13,14 +13,18 @@ static const char *get_value(const struct test_entry *e) return e->key + strlen(e->key) + 1; } -static int test_entry_cmp(const struct test_entry *e1, - const struct test_entry *e2, const char* key) +static int test_entry_cmp(const void *unused_cmp_data, + const struct test_entry *e1, + const struct test_entry *e2, + const char* key) { return strcmp(e1->key, key ? key : e2->key); } -static int test_entry_cmp_icase(const struct test_entry *e1, - const struct test_entry *e2, const char* key) +static int test_entry_cmp_icase(const void *unused_cmp_data, + const struct test_entry *e1, + const struct test_entry *e2, + const char* key) { return strcasecmp(e1->key, key ? key : e2->key); } @@ -92,7 +96,8 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) if (method & TEST_ADD) { /* test adding to the map */ for (j = 0; j < rounds; j++) { - hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0); + hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, + NULL, 0); /* add entries */ for (i = 0; i < TEST_SIZE; i++) { @@ -104,7 +109,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) } } else { /* test map lookups */ - hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, 0); + hashmap_init(&map, (hashmap_cmp_fn) test_entry_cmp, NULL, 0); /* fill the map (sparsely if specified) */ j = (method & TEST_SPARSE) ? TEST_SIZE / 10 : TEST_SIZE; @@ -147,7 +152,7 @@ int cmd_main(int argc, const char **argv) /* init hash map */ icase = argc > 1 && !strcmp("ignorecase", argv[1]); hashmap_init(&map, (hashmap_cmp_fn) (icase ? test_entry_cmp_icase - : test_entry_cmp), 0); + : test_entry_cmp), NULL, 0); /* process commands from stdin */ while (fgets(line, sizeof(line), stdin)) { From 3da492f8088413c9db2da6c489e117d4808859e9 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 30 Jun 2017 12:14:06 -0700 Subject: [PATCH 565/566] patch-ids.c: use hashmap correctly As alluded to in the previous patch, the code in patch-ids.c is using the hashmaps API wrong. Luckily we do not have a bug, as all hashmap functionality that we use here (hashmap_get) passes through the keydata. If hashmap_get_next were to be used, a bug would occur as that passes NULL for the key_data. So instead use the hashmap API correctly and provide the caller required data in the compare function via the first argument that always gets passed and was setup via the hashmap_init function. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- patch-ids.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/patch-ids.c b/patch-ids.c index 815c115811..b4166b0f38 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -35,10 +35,10 @@ int commit_patch_id(struct commit *commit, struct diff_options *options, * the side of safety. The actual value being negative does not have * any significance; only that it is non-zero matters. */ -static int patch_id_cmp(const void *unused_cmp_data, +static int patch_id_cmp(struct diff_options *opt, struct patch_id *a, struct patch_id *b, - struct diff_options *opt) + const void *unused_keydata) { if (is_null_oid(&a->patch_id) && commit_patch_id(a->commit, opt, &a->patch_id, 0)) @@ -59,7 +59,7 @@ int init_patch_ids(struct patch_ids *ids) DIFF_OPT_SET(&ids->diffopts, RECURSIVE); diff_setup_done(&ids->diffopts); hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp, - NULL, 256); + &ids->diffopts, 256); return 0; } @@ -95,7 +95,7 @@ struct patch_id *has_commit_patch_id(struct commit *commit, if (init_patch_id_entry(&patch, commit, ids)) return NULL; - return hashmap_get(&ids->patches, &patch, &ids->diffopts); + return hashmap_get(&ids->patches, &patch, NULL); } struct patch_id *add_commit_patch_id(struct commit *commit, From 1ecbf31d0298a1ed952623108e23234d5cf37086 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Fri, 30 Jun 2017 12:14:07 -0700 Subject: [PATCH 566/566] hashmap: migrate documentation from Documentation/technical into header While at it, clarify the use of `key`, `keydata`, `entry_or_key` as well as documenting the new data pointer for the compare function. Rework the example. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- Documentation/technical/api-hashmap.txt | 309 --------------------- hashmap.h | 350 +++++++++++++++++++++--- 2 files changed, 317 insertions(+), 342 deletions(-) delete mode 100644 Documentation/technical/api-hashmap.txt diff --git a/Documentation/technical/api-hashmap.txt b/Documentation/technical/api-hashmap.txt deleted file mode 100644 index ccc634bbd7..0000000000 --- a/Documentation/technical/api-hashmap.txt +++ /dev/null @@ -1,309 +0,0 @@ -hashmap API -=========== - -The hashmap API is a generic implementation of hash-based key-value mappings. - -Data Structures ---------------- - -`struct hashmap`:: - - The hash table structure. Members can be used as follows, but should - not be modified directly: -+ -The `size` member keeps track of the total number of entries (0 means the -hashmap is empty). -+ -`tablesize` is the allocated size of the hash table. A non-0 value indicates -that the hashmap is initialized. It may also be useful for statistical purposes -(i.e. `size / tablesize` is the current load factor). -+ -`cmpfn` stores the comparison function specified in `hashmap_init()`. In -advanced scenarios, it may be useful to change this, e.g. to switch between -case-sensitive and case-insensitive lookup. -+ -When `disallow_rehash` is set, automatic rehashes are prevented during inserts -and deletes. - -`struct hashmap_entry`:: - - An opaque structure representing an entry in the hash table, which must - be used as first member of user data structures. Ideally it should be - followed by an int-sized member to prevent unused memory on 64-bit - systems due to alignment. -+ -The `hash` member is the entry's hash code and the `next` member points to the -next entry in case of collisions (i.e. if multiple entries map to the same -bucket). - -`struct hashmap_iter`:: - - An iterator structure, to be used with hashmap_iter_* functions. - -Types ------ - -`int (*hashmap_cmp_fn)(const void *entry, const void *entry_or_key, const void *keydata)`:: - - User-supplied function to test two hashmap entries for equality. Shall - return 0 if the entries are equal. -+ -This function is always called with non-NULL `entry` / `entry_or_key` -parameters that have the same hash code. When looking up an entry, the `key` -and `keydata` parameters to hashmap_get and hashmap_remove are always passed -as second and third argument, respectively. Otherwise, `keydata` is NULL. - -Functions ---------- - -`unsigned int strhash(const char *buf)`:: -`unsigned int strihash(const char *buf)`:: -`unsigned int memhash(const void *buf, size_t len)`:: -`unsigned int memihash(const void *buf, size_t len)`:: -`unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len)`:: - - Ready-to-use hash functions for strings, using the FNV-1 algorithm (see - http://www.isthe.com/chongo/tech/comp/fnv). -+ -`strhash` and `strihash` take 0-terminated strings, while `memhash` and -`memihash` operate on arbitrary-length memory. -+ -`strihash` and `memihash` are case insensitive versions. -+ -`memihash_cont` is a variant of `memihash` that allows a computation to be -continued with another chunk of data. - -`unsigned int sha1hash(const unsigned char *sha1)`:: - - Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code - for use in hash tables. Cryptographic hashes are supposed to have - uniform distribution, so in contrast to `memhash()`, this just copies - the first `sizeof(int)` bytes without shuffling any bits. Note that - the results will be different on big-endian and little-endian - platforms, so they should not be stored or transferred over the net. - -`void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, size_t initial_size)`:: - - Initializes a hashmap structure. -+ -`map` is the hashmap to initialize. -+ -The `equals_function` can be specified to compare two entries for equality. -If NULL, entries are considered equal if their hash codes are equal. -+ -If the total number of entries is known in advance, the `initial_size` -parameter may be used to preallocate a sufficiently large table and thus -prevent expensive resizing. If 0, the table is dynamically resized. - -`void hashmap_free(struct hashmap *map, int free_entries)`:: - - Frees a hashmap structure and allocated memory. -+ -`map` is the hashmap to free. -+ -If `free_entries` is true, each hashmap_entry in the map is freed as well -(using stdlib's free()). - -`void hashmap_entry_init(void *entry, unsigned int hash)`:: - - Initializes a hashmap_entry structure. -+ -`entry` points to the entry to initialize. -+ -`hash` is the hash code of the entry. -+ -The hashmap_entry structure does not hold references to external resources, -and it is safe to just discard it once you are done with it (i.e. if -your structure was allocated with xmalloc(), you can just free(3) it, -and if it is on stack, you can just let it go out of scope). - -`void *hashmap_get(const struct hashmap *map, const void *key, const void *keydata)`:: - - Returns the hashmap entry for the specified key, or NULL if not found. -+ -`map` is the hashmap structure. -+ -`key` is a hashmap_entry structure (or user data structure that starts with -hashmap_entry) that has at least been initialized with the proper hash code -(via `hashmap_entry_init`). -+ -If an entry with matching hash code is found, `key` and `keydata` are passed -to `hashmap_cmp_fn` to decide whether the entry matches the key. - -`void *hashmap_get_from_hash(const struct hashmap *map, unsigned int hash, const void *keydata)`:: - - Returns the hashmap entry for the specified hash code and key data, - or NULL if not found. -+ -`map` is the hashmap structure. -+ -`hash` is the hash code of the entry to look up. -+ -If an entry with matching hash code is found, `keydata` is passed to -`hashmap_cmp_fn` to decide whether the entry matches the key. The -`entry_or_key` parameter points to a bogus hashmap_entry structure that -should not be used in the comparison. - -`void *hashmap_get_next(const struct hashmap *map, const void *entry)`:: - - Returns the next equal hashmap entry, or NULL if not found. This can be - used to iterate over duplicate entries (see `hashmap_add`). -+ -`map` is the hashmap structure. -+ -`entry` is the hashmap_entry to start the search from, obtained via a previous -call to `hashmap_get` or `hashmap_get_next`. - -`void hashmap_add(struct hashmap *map, void *entry)`:: - - Adds a hashmap entry. This allows to add duplicate entries (i.e. - separate values with the same key according to hashmap_cmp_fn). -+ -`map` is the hashmap structure. -+ -`entry` is the entry to add. - -`void *hashmap_put(struct hashmap *map, void *entry)`:: - - Adds or replaces a hashmap entry. If the hashmap contains duplicate - entries equal to the specified entry, only one of them will be replaced. -+ -`map` is the hashmap structure. -+ -`entry` is the entry to add or replace. -+ -Returns the replaced entry, or NULL if not found (i.e. the entry was added). - -`void *hashmap_remove(struct hashmap *map, const void *key, const void *keydata)`:: - - Removes a hashmap entry matching the specified key. If the hashmap - contains duplicate entries equal to the specified key, only one of - them will be removed. -+ -`map` is the hashmap structure. -+ -`key` is a hashmap_entry structure (or user data structure that starts with -hashmap_entry) that has at least been initialized with the proper hash code -(via `hashmap_entry_init`). -+ -If an entry with matching hash code is found, `key` and `keydata` are -passed to `hashmap_cmp_fn` to decide whether the entry matches the key. -+ -Returns the removed entry, or NULL if not found. - -`void hashmap_disallow_rehash(struct hashmap *map, unsigned value)`:: - - Disallow/allow automatic rehashing of the hashmap during inserts - and deletes. -+ -This is useful if the caller knows that the hashmap will be accessed -by multiple threads. -+ -The caller is still responsible for any necessary locking; this simply -prevents unexpected rehashing. The caller is also responsible for properly -sizing the initial hashmap to ensure good performance. -+ -A call to allow rehashing does not force a rehash; that might happen -with the next insert or delete. - -`void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)`:: -`void *hashmap_iter_next(struct hashmap_iter *iter)`:: -`void *hashmap_iter_first(struct hashmap *map, struct hashmap_iter *iter)`:: - - Used to iterate over all entries of a hashmap. Note that it is - not safe to add or remove entries to the hashmap while - iterating. -+ -`hashmap_iter_init` initializes a `hashmap_iter` structure. -+ -`hashmap_iter_next` returns the next hashmap_entry, or NULL if there are no -more entries. -+ -`hashmap_iter_first` is a combination of both (i.e. initializes the iterator -and returns the first entry, if any). - -`const char *strintern(const char *string)`:: -`const void *memintern(const void *data, size_t len)`:: - - Returns the unique, interned version of the specified string or data, - similar to the `String.intern` API in Java and .NET, respectively. - Interned strings remain valid for the entire lifetime of the process. -+ -Can be used as `[x]strdup()` or `xmemdupz` replacement, except that interned -strings / data must not be modified or freed. -+ -Interned strings are best used for short strings with high probability of -duplicates. -+ -Uses a hashmap to store the pool of interned strings. - -Usage example -------------- - -Here's a simple usage example that maps long keys to double values. ------------- -struct hashmap map; - -struct long2double { - struct hashmap_entry ent; /* must be the first member! */ - long key; - double value; -}; - -static int long2double_cmp(const struct long2double *e1, const struct long2double *e2, const void *unused) -{ - return !(e1->key == e2->key); -} - -void long2double_init(void) -{ - hashmap_init(&map, (hashmap_cmp_fn) long2double_cmp, 0); -} - -void long2double_free(void) -{ - hashmap_free(&map, 1); -} - -static struct long2double *find_entry(long key) -{ - struct long2double k; - hashmap_entry_init(&k, memhash(&key, sizeof(long))); - k.key = key; - return hashmap_get(&map, &k, NULL); -} - -double get_value(long key) -{ - struct long2double *e = find_entry(key); - return e ? e->value : 0; -} - -void set_value(long key, double value) -{ - struct long2double *e = find_entry(key); - if (!e) { - e = malloc(sizeof(struct long2double)); - hashmap_entry_init(e, memhash(&key, sizeof(long))); - e->key = key; - hashmap_add(&map, e); - } - e->value = value; -} ------------- - -Using variable-sized keys -------------------------- - -The `hashmap_entry_get` and `hashmap_entry_remove` functions expect an ordinary -`hashmap_entry` structure as key to find the correct entry. If the key data is -variable-sized (e.g. a FLEX_ARRAY string) or quite large, it is undesirable -to create a full-fledged entry structure on the heap and copy all the key data -into the structure. - -In this case, the `keydata` parameter can be used to pass -variable-sized key data directly to the comparison function, and the `key` -parameter can be a stripped-down, fixed size entry structure allocated on the -stack. - -See test-hashmap.c for an example using arbitrary-length strings as keys. diff --git a/hashmap.h b/hashmap.h index aaf09e047e..7a8fa7fa3d 100644 --- a/hashmap.h +++ b/hashmap.h @@ -3,17 +3,123 @@ /* * Generic implementation of hash-based key-value mappings. - * See Documentation/technical/api-hashmap.txt. + * + * An example that maps long to a string: + * For the sake of the example this allows to lookup exact values, too + * (i.e. it is operated as a set, the value is part of the key) + * ------------------------------------- + * + * struct hashmap map; + * struct long2string { + * struct hashmap_entry ent; // must be the first member! + * long key; + * char value[FLEX_ARRAY]; // be careful with allocating on stack! + * }; + * + * #define COMPARE_VALUE 1 + * + * static int long2string_cmp(const struct long2string *e1, + * const struct long2string *e2, + * const void *keydata, const void *userdata) + * { + * char *string = keydata; + * unsigned *flags = (unsigned*)userdata; + * + * if (flags & COMPARE_VALUE) + * return !(e1->key == e2->key) || (keydata ? + * strcmp(e1->value, keydata) : strcmp(e1->value, e2->value)); + * else + * return !(e1->key == e2->key); + * } + * + * int main(int argc, char **argv) + * { + * long key; + * char *value, *action; + * + * unsigned flags = ALLOW_DUPLICATE_KEYS; + * + * hashmap_init(&map, (hashmap_cmp_fn) long2string_cmp, &flags, 0); + * + * while (scanf("%s %l %s", action, key, value)) { + * + * if (!strcmp("add", action)) { + * struct long2string *e; + * e = malloc(sizeof(struct long2string) + strlen(value)); + * hashmap_entry_init(e, memhash(&key, sizeof(long))); + * e->key = key; + * memcpy(e->value, value, strlen(value)); + * hashmap_add(&map, e); + * } + * + * if (!strcmp("print_all_by_key", action)) { + * flags &= ~COMPARE_VALUE; + * + * struct long2string k; + * hashmap_entry_init(&k, memhash(&key, sizeof(long))); + * k.key = key; + * + * struct long2string *e = hashmap_get(&map, &k, NULL); + * if (e) { + * printf("first: %l %s\n", e->key, e->value); + * while (e = hashmap_get_next(&map, e)) + * printf("found more: %l %s\n", e->key, e->value); + * } + * } + * + * if (!strcmp("has_exact_match", action)) { + * flags |= COMPARE_VALUE; + * + * struct long2string *e; + * e = malloc(sizeof(struct long2string) + strlen(value)); + * hashmap_entry_init(e, memhash(&key, sizeof(long))); + * e->key = key; + * memcpy(e->value, value, strlen(value)); + * + * printf("%s found\n", hashmap_get(&map, e, NULL) ? "" : "not"); + * } + * + * if (!strcmp("has_exact_match_no_heap_alloc", action)) { + * flags |= COMPARE_VALUE; + * + * struct long2string e; + * hashmap_entry_init(e, memhash(&key, sizeof(long))); + * e.key = key; + * + * printf("%s found\n", hashmap_get(&map, e, value) ? "" : "not"); + * } + * + * if (!strcmp("end", action)) { + * hashmap_free(&map, 1); + * break; + * } + * } + * } */ -/* FNV-1 functions */ - +/* + * Ready-to-use hash functions for strings, using the FNV-1 algorithm (see + * http://www.isthe.com/chongo/tech/comp/fnv). + * `strhash` and `strihash` take 0-terminated strings, while `memhash` and + * `memihash` operate on arbitrary-length memory. + * `strihash` and `memihash` are case insensitive versions. + * `memihash_cont` is a variant of `memihash` that allows a computation to be + * continued with another chunk of data. + */ extern unsigned int strhash(const char *buf); extern unsigned int strihash(const char *buf); extern unsigned int memhash(const void *buf, size_t len); extern unsigned int memihash(const void *buf, size_t len); extern unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len); +/* + * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code + * for use in hash tables. Cryptographic hashes are supposed to have + * uniform distribution, so in contrast to `memhash()`, this just copies + * the first `sizeof(int)` bytes without shuffling any bits. Note that + * the results will be different on big-endian and little-endian + * platforms, so they should not be stored or transferred over the net. + */ static inline unsigned int sha1hash(const unsigned char *sha1) { /* @@ -25,90 +131,255 @@ static inline unsigned int sha1hash(const unsigned char *sha1) return hash; } -/* data structures */ - +/* + * struct hashmap_entry is an opaque structure representing an entry in the + * hash table, which must be used as first member of user data structures. + * Ideally it should be followed by an int-sized member to prevent unused + * memory on 64-bit systems due to alignment. + */ struct hashmap_entry { + /* + * next points to the next entry in case of collisions (i.e. if + * multiple entries map to the same bucket) + */ struct hashmap_entry *next; + + /* entry's hash code */ unsigned int hash; }; +/* + * User-supplied function to test two hashmap entries for equality. Shall + * return 0 if the entries are equal. + * + * This function is always called with non-NULL `entry` and `entry_or_key` + * parameters that have the same hash code. + * + * When looking up an entry, the `key` and `keydata` parameters to hashmap_get + * and hashmap_remove are always passed as second `entry_or_key` and third + * argument `keydata`, respectively. Otherwise, `keydata` is NULL. + * + * When it is too expensive to allocate a user entry (either because it is + * large or varialbe sized, such that it is not on the stack), then the + * relevant data to check for equality should be passed via `keydata`. + * In this case `key` can be a stripped down version of the user key data + * or even just a hashmap_entry having the correct hash. + * + * The `hashmap_cmp_fn_data` entry is the pointer given in the init function. + */ typedef int (*hashmap_cmp_fn)(const void *hashmap_cmp_fn_data, const void *entry, const void *entry_or_key, const void *keydata); +/* + * struct hashmap is the hash table structure. Members can be used as follows, + * but should not be modified directly. + */ struct hashmap { struct hashmap_entry **table; + + /* Stores the comparison function specified in `hashmap_init()`. */ hashmap_cmp_fn cmpfn; const void *cmpfn_data; - unsigned int size, tablesize, grow_at, shrink_at; - unsigned disallow_rehash : 1; -}; -struct hashmap_iter { - struct hashmap *map; - struct hashmap_entry *next; - unsigned int tablepos; + /* total number of entries (0 means the hashmap is empty) */ + unsigned int size; + + /* + * tablesize is the allocated size of the hash table. A non-0 value + * indicates that the hashmap is initialized. It may also be useful + * for statistical purposes (i.e. `size / tablesize` is the current + * load factor). + */ + unsigned int tablesize; + + unsigned int grow_at; + unsigned int shrink_at; + + /* See `hashmap_disallow_rehash`. */ + unsigned disallow_rehash : 1; }; /* hashmap functions */ +/* + * Initializes a hashmap structure. + * + * `map` is the hashmap to initialize. + * + * The `equals_function` can be specified to compare two entries for equality. + * If NULL, entries are considered equal if their hash codes are equal. + * + * The `equals_function_data` parameter can be used to provide additional data + * (a callback cookie) that will be passed to `equals_function` each time it + * is called. This allows a single `equals_function` to implement multiple + * comparison functions. + * + * If the total number of entries is known in advance, the `initial_size` + * parameter may be used to preallocate a sufficiently large table and thus + * prevent expensive resizing. If 0, the table is dynamically resized. + */ extern void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function, const void *equals_function_data, size_t initial_size); + +/* + * Frees a hashmap structure and allocated memory. + * + * If `free_entries` is true, each hashmap_entry in the map is freed as well + * using stdlibs free(). + */ extern void hashmap_free(struct hashmap *map, int free_entries); /* hashmap_entry functions */ +/* + * Initializes a hashmap_entry structure. + * + * `entry` points to the entry to initialize. + * `hash` is the hash code of the entry. + * + * The hashmap_entry structure does not hold references to external resources, + * and it is safe to just discard it once you are done with it (i.e. if + * your structure was allocated with xmalloc(), you can just free(3) it, + * and if it is on stack, you can just let it go out of scope). + */ static inline void hashmap_entry_init(void *entry, unsigned int hash) { struct hashmap_entry *e = entry; e->hash = hash; e->next = NULL; } -extern void *hashmap_get(const struct hashmap *map, const void *key, - const void *keydata); -extern void *hashmap_get_next(const struct hashmap *map, const void *entry); -extern void hashmap_add(struct hashmap *map, void *entry); -extern void *hashmap_put(struct hashmap *map, void *entry); -extern void *hashmap_remove(struct hashmap *map, const void *key, - const void *keydata); +/* + * Returns the hashmap entry for the specified key, or NULL if not found. + * + * `map` is the hashmap structure. + * + * `key` is a user data structure that starts with hashmap_entry that has at + * least been initialized with the proper hash code (via `hashmap_entry_init`). + * + * `keydata` is a data structure that holds just enough information to check + * for equality to a given entry. + * + * If the key data is variable-sized (e.g. a FLEX_ARRAY string) or quite large, + * it is undesirable to create a full-fledged entry structure on the heap and + * copy all the key data into the structure. + * + * In this case, the `keydata` parameter can be used to pass + * variable-sized key data directly to the comparison function, and the `key` + * parameter can be a stripped-down, fixed size entry structure allocated on the + * stack. + * + * If an entry with matching hash code is found, `key` and `keydata` are passed + * to `hashmap_cmp_fn` to decide whether the entry matches the key. + */ +extern void *hashmap_get(const struct hashmap *map, const void *key, + const void *keydata); + +/* + * Returns the hashmap entry for the specified hash code and key data, + * or NULL if not found. + * + * `map` is the hashmap structure. + * `hash` is the hash code of the entry to look up. + * + * If an entry with matching hash code is found, `keydata` is passed to + * `hashmap_cmp_fn` to decide whether the entry matches the key. The + * `entry_or_key` parameter of `hashmap_cmp_fn` points to a hashmap_entry + * structure that should not be used in the comparison. + */ static inline void *hashmap_get_from_hash(const struct hashmap *map, - unsigned int hash, const void *keydata) + unsigned int hash, + const void *keydata) { struct hashmap_entry key; hashmap_entry_init(&key, hash); return hashmap_get(map, &key, keydata); } +/* + * Returns the next equal hashmap entry, or NULL if not found. This can be + * used to iterate over duplicate entries (see `hashmap_add`). + * + * `map` is the hashmap structure. + * `entry` is the hashmap_entry to start the search from, obtained via a previous + * call to `hashmap_get` or `hashmap_get_next`. + */ +extern void *hashmap_get_next(const struct hashmap *map, const void *entry); + +/* + * Adds a hashmap entry. This allows to add duplicate entries (i.e. + * separate values with the same key according to hashmap_cmp_fn). + * + * `map` is the hashmap structure. + * `entry` is the entry to add. + */ +extern void hashmap_add(struct hashmap *map, void *entry); + +/* + * Adds or replaces a hashmap entry. If the hashmap contains duplicate + * entries equal to the specified entry, only one of them will be replaced. + * + * `map` is the hashmap structure. + * `entry` is the entry to add or replace. + * Returns the replaced entry, or NULL if not found (i.e. the entry was added). + */ +extern void *hashmap_put(struct hashmap *map, void *entry); + +/* + * Removes a hashmap entry matching the specified key. If the hashmap contains + * duplicate entries equal to the specified key, only one of them will be + * removed. Returns the removed entry, or NULL if not found. + * + * Argument explanation is the same as in `hashmap_get`. + */ +extern void *hashmap_remove(struct hashmap *map, const void *key, + const void *keydata); + +/* + * Returns the `bucket` an entry is stored in. + * Useful for multithreaded read access. + */ int hashmap_bucket(const struct hashmap *map, unsigned int hash); /* * Disallow/allow rehashing of the hashmap. - * This is useful if the caller knows that the hashmap - * needs multi-threaded access. The caller is still - * required to guard/lock searches and inserts in a - * manner appropriate to their usage. This simply - * prevents the table from being unexpectedly re-mapped. + * This is useful if the caller knows that the hashmap needs multi-threaded + * access. The caller is still required to guard/lock searches and inserts + * in a manner appropriate to their usage. This simply prevents the table + * from being unexpectedly re-mapped. * - * If is up to the caller to ensure that the hashmap is - * initialized to a reasonable size to prevent poor - * performance. + * It is up to the caller to ensure that the hashmap is initialized to a + * reasonable size to prevent poor performance. * - * When value=1, prevent future rehashes on adds and deleted. - * When value=0, allow future rehahses. This DOES NOT force - * a rehash now. + * A call to allow rehashing does not force a rehash; that might happen + * with the next insert or delete. */ static inline void hashmap_disallow_rehash(struct hashmap *map, unsigned value) { map->disallow_rehash = value; } -/* hashmap_iter functions */ +/* + * Used to iterate over all entries of a hashmap. Note that it is + * not safe to add or remove entries to the hashmap while + * iterating. + */ +struct hashmap_iter { + struct hashmap *map; + struct hashmap_entry *next; + unsigned int tablepos; +}; +/* Initializes a `hashmap_iter` structure. */ extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter); + +/* Returns the next hashmap_entry, or NULL if there are no more entries. */ extern void *hashmap_iter_next(struct hashmap_iter *iter); + +/* Initializes the iterator and returns the first entry, if any. */ static inline void *hashmap_iter_first(struct hashmap *map, struct hashmap_iter *iter) { @@ -116,8 +387,21 @@ static inline void *hashmap_iter_first(struct hashmap *map, return hashmap_iter_next(iter); } -/* string interning */ +/* String interning */ +/* + * Returns the unique, interned version of the specified string or data, + * similar to the `String.intern` API in Java and .NET, respectively. + * Interned strings remain valid for the entire lifetime of the process. + * + * Can be used as `[x]strdup()` or `xmemdupz` replacement, except that interned + * strings / data must not be modified or freed. + * + * Interned strings are best used for short strings with high probability of + * duplicates. + * + * Uses a hashmap to store the pool of interned strings. + */ extern const void *memintern(const void *data, size_t len); static inline const char *strintern(const char *string) {