mirror of
https://github.com/git/git.git
synced 2026-05-25 11:25:06 +02:00
Merge branch 'master' of https://github.com/git/git
Upstream adds 8 new translatable messages. * 'master' of https://github.com/git/git: (93 commits) A bit more post -rc1 ... Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
This commit is contained in:
@@ -668,6 +668,18 @@ For C programs:
|
||||
unsigned other_field:1;
|
||||
unsigned field_with_longer_name:1;
|
||||
|
||||
- When a function `F` accepts flags, those flags should be defined as `enum
|
||||
F_flags`. Individual flag definitions should start with `F` and be in
|
||||
all-uppercase letters. Flag values should be represented via bit shifts.
|
||||
E.g.
|
||||
|
||||
enum frobnicate_flags {
|
||||
FROBNICATE_FOO = (1 << 0),
|
||||
FROBNICATE_BAR = (1 << 1),
|
||||
};
|
||||
|
||||
int frobnicate(enum frobnicate_flags flags);
|
||||
|
||||
- Array names should be named in the singular form if the individual items are
|
||||
subject of use. E.g.:
|
||||
|
||||
|
||||
@@ -119,6 +119,11 @@ UI, Workflows & Features
|
||||
* "git replay" (experimental) learns, in addition to "pick" and
|
||||
"replay", a new operating mode "revert".
|
||||
|
||||
* git replay now supports replaying down to the root commit.
|
||||
|
||||
* Handling of signed commits and tags in fast-import has been made more
|
||||
configurable.
|
||||
|
||||
|
||||
Performance, Internal Implementation, Development Support etc.
|
||||
--------------------------------------------------------------
|
||||
@@ -277,6 +282,40 @@ Performance, Internal Implementation, Development Support etc.
|
||||
* In case homebrew breaks REG_ENHANCED again, leave a in-code comment
|
||||
to suggest use of our replacement regex as a workaround.
|
||||
|
||||
* MinGW build updates.
|
||||
|
||||
* The way dash 0.5.13 handles non-ASCII contents in here-doc
|
||||
is buggy and breaks our existing tests, which unfortunately
|
||||
have been rewritten to avoid triggering the bug.
|
||||
|
||||
* Object name handling (disambiguation and abbreviation) has been
|
||||
refactored to be backend-generic, moving logic into the respective
|
||||
object database backends.
|
||||
|
||||
* pack-objects's --stdin-packs=follow mode learns to handle
|
||||
excluded-but-open packs.
|
||||
|
||||
* A few code paths that spawned child processes for network
|
||||
connection weren't wait(2)ing for their children and letting "init"
|
||||
reap them instead; they have been tightened.
|
||||
|
||||
* Adjust the codebase for C23 that changes functions like strchr()
|
||||
that discarded constness when they return a pointer into a const
|
||||
string to preserve constness.
|
||||
|
||||
* A handful of inappropriate uses of the_repository have been
|
||||
rewritten to use the right repository structure instance in the
|
||||
read-cache.c codepath.
|
||||
|
||||
* Internals of "git fsck" have been refactored to not depend on the
|
||||
global `the_repository` variable.
|
||||
|
||||
* Reduce dependency on `the_repository` in add-patch.c file.
|
||||
|
||||
* The way the "git log -L<range>:<file>" feature is bolted onto the
|
||||
log/diff machinery is being reworked a bit to make the feature
|
||||
compatible with more diff options, like -S/G.
|
||||
|
||||
|
||||
Fixes since v2.53
|
||||
-----------------
|
||||
@@ -455,6 +494,27 @@ Fixes since v2.53
|
||||
refspec is a single-object refspec, which has been corrected.
|
||||
(merge 4e5dc601dd kj/refspec-parsing-outside-repository later to maint).
|
||||
|
||||
* Fix a regression in writing the commit-graph where commits with dates
|
||||
exceeding 34 bits (beyond year 2514) could cause an underflow and
|
||||
crash Git during the generation data overflow chunk writing.
|
||||
|
||||
* The value of a wrong pointer variable was referenced in an error
|
||||
message that reported that it shouldn't be NULL.
|
||||
(merge 753ecf4205 yc/path-walk-fix-error-reporting later to maint).
|
||||
|
||||
* The check in "receive-pack" to prevent a checked out branch from
|
||||
getting updated via updateInstead mechanism has been corrected.
|
||||
|
||||
* "git backfill" is capable of auto-detecting a sparsely checked out
|
||||
working tree, which was broken.
|
||||
(merge 339eba65a7 th/backfill-auto-detect-sparseness-fix later to maint).
|
||||
|
||||
* add_files_to_cache() used diff_files() to detect only the paths that
|
||||
are different between the index and the working tree and add them,
|
||||
which does not need rename detection, which interfered with unnecessary
|
||||
conflicts.
|
||||
(merge c0ce43376b ng/add-files-to-cache-wo-rename later to maint).
|
||||
|
||||
* Other code cleanup, docfix, build fix, etc.
|
||||
(merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint).
|
||||
(merge 7a747f972d dd/t5403-modernise later to maint).
|
||||
@@ -506,3 +566,6 @@ Fixes since v2.53
|
||||
(merge 37182267a0 kh/doc-interpret-trailers-1 later to maint).
|
||||
(merge f64c50e768 jc/rerere-modern-strbuf-handling later to maint).
|
||||
(merge 699248d89e th/t8003-unhide-git-failures later to maint).
|
||||
(merge d8e34f971b za/t2000-modernise later to maint).
|
||||
(merge 849988bc74 th/t6101-unhide-git-failures later to maint).
|
||||
(merge 0f0ce07625 sp/doc-gitignore-oowt later to maint).
|
||||
|
||||
@@ -66,11 +66,10 @@ fast-import stream! This option is enabled automatically for
|
||||
remote-helpers that use the `import` capability, as they are
|
||||
already trusted to run their own code.
|
||||
|
||||
`--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)`::
|
||||
`--signed-tags=<mode>`::
|
||||
Specify how to handle signed tags. Behaves in the same way as
|
||||
the `--signed-commits=<mode>` below, except that the
|
||||
`strip-if-invalid` mode is not yet supported. Like for signed
|
||||
commits, the default mode is `verbatim`.
|
||||
the `--signed-commits=<mode>` below. Like for signed commits,
|
||||
the default mode is `verbatim`.
|
||||
|
||||
`--signed-commits=<mode>`::
|
||||
Specify how to handle signed commits. The following <mode>s
|
||||
@@ -90,6 +89,8 @@ already trusted to run their own code.
|
||||
commit signatures and replaces invalid signatures with newly created ones.
|
||||
Valid signatures are left unchanged. If `<keyid>` is provided, that key is
|
||||
used for signing; otherwise the configured default signing key is used.
|
||||
* `abort-if-invalid` will make this program die when encountering a signed
|
||||
commit that is unable to be verified.
|
||||
|
||||
Options for Frontends
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -94,13 +94,24 @@ base-name::
|
||||
included packs (those not beginning with `^`), excluding any
|
||||
objects listed in the excluded packs (beginning with `^`).
|
||||
+
|
||||
When `mode` is "follow", objects from packs not listed on stdin receive
|
||||
special treatment. Objects within unlisted packs will be included if
|
||||
those objects are (1) reachable from the included packs, and (2) not
|
||||
found in any excluded packs. This mode is useful, for example, to
|
||||
resurrect once-unreachable objects found in cruft packs to generate
|
||||
packs which are closed under reachability up to the boundary set by the
|
||||
excluded packs.
|
||||
When `mode` is "follow" packs may additionally be prefixed with `!`,
|
||||
indicating that they are excluded but not necessarily closed under
|
||||
reachability. In addition to objects in included packs, the resulting
|
||||
pack may include additional objects based on the following:
|
||||
+
|
||||
--
|
||||
* If any packs are marked with `!`, then objects reachable from such
|
||||
packs or included ones via objects outside of excluded-closed packs
|
||||
will be included. In this case, all `^` packs are treated as closed
|
||||
under reachability.
|
||||
* Otherwise (if there are no `!` packs), objects within unlisted packs
|
||||
will be included if those objects are (1) reachable from the
|
||||
included packs, and (2) not found in any excluded packs.
|
||||
--
|
||||
+
|
||||
This mode is useful, for example, to resurrect once-unreachable
|
||||
objects found in cruft packs to generate packs which are closed under
|
||||
reachability up to the boundary set by the excluded packs.
|
||||
+
|
||||
Incompatible with `--revs`, or options that imply `--revs` (such as
|
||||
`--all`), with the exception of `--unpacked`, which is compatible.
|
||||
|
||||
@@ -9,7 +9,8 @@ git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos t
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>
|
||||
(EXPERIMENTAL!) 'git replay' ([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)
|
||||
[--ref=<ref>] [--ref-action=<mode>] <revision-range>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -26,7 +27,7 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
--onto <newbase>::
|
||||
--onto=<newbase>::
|
||||
Starting point at which to create the new commits. May be any
|
||||
valid commit, and not just an existing branch name.
|
||||
+
|
||||
@@ -34,7 +35,7 @@ When `--onto` is specified, the branch(es) in the revision range will be
|
||||
updated to point at the new commits, similar to the way `git rebase --update-refs`
|
||||
updates multiple branches in the affected range.
|
||||
|
||||
--advance <branch>::
|
||||
--advance=<branch>::
|
||||
Starting point at which to create the new commits; must be a
|
||||
branch name.
|
||||
+
|
||||
@@ -42,7 +43,7 @@ The history is replayed on top of the <branch> and <branch> is updated to
|
||||
point at the tip of the resulting history. This is different from `--onto`,
|
||||
which uses the target only as a starting point without updating it.
|
||||
|
||||
--revert <branch>::
|
||||
--revert=<branch>::
|
||||
Starting point at which to create the reverted commits; must be a
|
||||
branch name.
|
||||
+
|
||||
@@ -65,6 +66,16 @@ incompatible with `--contained` (which is a modifier for `--onto` only).
|
||||
Update all branches that point at commits in
|
||||
<revision-range>. Requires `--onto`.
|
||||
|
||||
--ref=<ref>::
|
||||
Override which reference is updated with the result of the replay.
|
||||
The ref must be fully qualified.
|
||||
When used with `--onto`, the `<revision-range>` should have a
|
||||
single tip and only the specified reference is updated instead of
|
||||
inferring refs from the revision range.
|
||||
When used with `--advance` or `--revert`, the specified reference is
|
||||
updated instead of the branch given to those options.
|
||||
This option is incompatible with `--contained`.
|
||||
|
||||
--ref-action[=<mode>]::
|
||||
Control how references are updated. The mode can be:
|
||||
+
|
||||
@@ -79,8 +90,8 @@ The default mode can be configured via the `replay.refAction` configuration vari
|
||||
|
||||
<revision-range>::
|
||||
Range of commits to replay; see "Specifying Ranges" in
|
||||
linkgit:git-rev-parse[1]. In `--advance <branch>` or
|
||||
`--revert <branch>` mode, the range should have a single tip,
|
||||
linkgit:git-rev-parse[1]. In `--advance=<branch>` or
|
||||
`--revert=<branch>` mode, the range should have a single tip,
|
||||
so that it's clear to which tip the advanced or reverted
|
||||
<branch> should point. Any commits in the range whose changes
|
||||
are already present in the branch the commits are being
|
||||
@@ -127,7 +138,7 @@ EXAMPLES
|
||||
To simply rebase `mybranch` onto `target`:
|
||||
|
||||
------------
|
||||
$ git replay --onto target origin/main..mybranch
|
||||
$ git replay --onto=target origin/main..mybranch
|
||||
------------
|
||||
|
||||
The refs are updated atomically and no output is produced on success.
|
||||
@@ -135,14 +146,14 @@ The refs are updated atomically and no output is produced on success.
|
||||
To see what would be updated without actually updating:
|
||||
|
||||
------------
|
||||
$ git replay --ref-action=print --onto target origin/main..mybranch
|
||||
$ git replay --ref-action=print --onto=target origin/main..mybranch
|
||||
update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
|
||||
------------
|
||||
|
||||
To cherry-pick the commits from mybranch onto target:
|
||||
|
||||
------------
|
||||
$ git replay --advance target origin/main..mybranch
|
||||
$ git replay --advance=target origin/main..mybranch
|
||||
------------
|
||||
|
||||
Note that the first two examples replay the exact same commits and on
|
||||
@@ -154,7 +165,7 @@ What if you have a stack of branches, one depending upon another, and
|
||||
you'd really like to rebase the whole set?
|
||||
|
||||
------------
|
||||
$ git replay --contained --onto origin/main origin/main..tipbranch
|
||||
$ git replay --contained --onto=origin/main origin/main..tipbranch
|
||||
------------
|
||||
|
||||
All three branches (`branch1`, `branch2`, and `tipbranch`) are updated
|
||||
@@ -165,7 +176,7 @@ commits to replay using the syntax `A..B`; any range expression will
|
||||
do:
|
||||
|
||||
------------
|
||||
$ git replay --onto origin/main ^base branch1 branch2 branch3
|
||||
$ git replay --onto=origin/main ^base branch1 branch2 branch3
|
||||
------------
|
||||
|
||||
This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
|
||||
@@ -176,7 +187,7 @@ that they have in common, but that does not need to be the case.
|
||||
To revert commits on a branch:
|
||||
|
||||
------------
|
||||
$ git replay --revert main topic~2..topic
|
||||
$ git replay --revert=main topic~2..topic
|
||||
------------
|
||||
|
||||
This reverts the last two commits from `topic`, creating revert commits on
|
||||
@@ -188,6 +199,16 @@ NOTE: For reverting an entire merge request as a single commit (rather than
|
||||
commit-by-commit), consider using `git merge-tree --merge-base $TIP HEAD $BASE`
|
||||
which can avoid unnecessary merge conflicts.
|
||||
|
||||
To replay onto a specific commit while updating a different reference:
|
||||
|
||||
------------
|
||||
$ git replay --onto=112233 --ref=refs/heads/mybranch aabbcc..ddeeff
|
||||
------------
|
||||
|
||||
This replays the range `aabbcc..ddeeff` onto commit `112233` and updates
|
||||
`refs/heads/mybranch` to point at the result. This can be useful when you want
|
||||
to use bare commit IDs instead of branch names.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
|
||||
@@ -14,10 +14,10 @@ git stash drop [-q | --quiet] [<stash>]
|
||||
git stash pop [--index] [-q | --quiet] [<stash>]
|
||||
git stash apply [--index] [-q | --quiet] [<stash>]
|
||||
git stash branch <branchname> [<stash>]
|
||||
git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
|
||||
git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
|
||||
[-u | --include-untracked] [-a | --all] [(-m | --message) <message>]
|
||||
[--pathspec-from-file=<file> [--pathspec-file-nul]]
|
||||
[--] [<pathspec>...]]
|
||||
[--] [<pathspec>...]
|
||||
git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
|
||||
[-u | --include-untracked] [-a | --all] [<message>]
|
||||
git stash clear
|
||||
@@ -60,10 +60,8 @@ COMMANDS
|
||||
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 entry. The two exceptions to this
|
||||
are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
|
||||
which are allowed after a double hyphen `--` for disambiguation.
|
||||
pathspec elements are only allowed after a double hyphen `--`
|
||||
to prevent a misspelled subcommand from making an unwanted stash entry.
|
||||
|
||||
`save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-u | --include-untracked] [-a | --all] [-q | --quiet] [<message>]`::
|
||||
|
||||
|
||||
@@ -96,6 +96,11 @@ PATTERN FORMAT
|
||||
particular `.gitignore` file itself. Otherwise the pattern may also
|
||||
match at any level below the `.gitignore` level.
|
||||
|
||||
- Patterns read from exclude sources that are outside the working tree,
|
||||
such as $GIT_DIR/info/exclude and core.excludesFile, are treated as if
|
||||
they are specified at the root of the working tree, i.e. a leading "/"
|
||||
in such patterns anchors the match at the root of the repository.
|
||||
|
||||
- If there is a separator at the end of the pattern then the pattern
|
||||
will only match directories, otherwise the pattern can match both
|
||||
files and directories.
|
||||
|
||||
@@ -12,4 +12,8 @@
|
||||
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
|
||||
`--name-only`, `--name-status`, `--check`) are not currently implemented.
|
||||
+
|
||||
Patch formatting options such as `--word-diff`, `--color-moved`,
|
||||
`--no-prefix`, and whitespace options (`-w`, `-b`) are supported,
|
||||
as are pickaxe options (`-S`, `-G`).
|
||||
+
|
||||
include::line-range-format.adoc[]
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
DEF_VER=v2.54.0-rc0
|
||||
DEF_VER=v2.54.0-rc1
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
+4
-4
@@ -558,8 +558,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
strvec_push(&args,
|
||||
/* could be on an unborn branch */
|
||||
!strcmp("HEAD", s->revision) &&
|
||||
repo_get_oid(the_repository, "HEAD", &oid) ?
|
||||
empty_tree_oid_hex(the_repository->hash_algo) : s->revision);
|
||||
repo_get_oid(s->r, "HEAD", &oid) ?
|
||||
empty_tree_oid_hex(s->r->hash_algo) : s->revision);
|
||||
}
|
||||
color_arg_index = args.nr;
|
||||
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
|
||||
@@ -1271,7 +1271,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
|
||||
"removed, then the edit is\n"
|
||||
"aborted and the hunk is left unchanged.\n"));
|
||||
|
||||
if (strbuf_edit_interactively(the_repository, &s->buf,
|
||||
if (strbuf_edit_interactively(s->r, &s->buf,
|
||||
"addp-hunk-edit.diff", NULL) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -1679,7 +1679,7 @@ static size_t patch_update_file(struct add_p_state *s,
|
||||
if (file_diff->hunk_nr) {
|
||||
if (rendered_hunk_index != hunk_index) {
|
||||
if (use_pager) {
|
||||
setup_pager(the_repository);
|
||||
setup_pager(s->r);
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
render_hunk(s, hunk, 0, colored, &s->buf);
|
||||
|
||||
@@ -1840,8 +1840,16 @@ static int parse_fragment(struct apply_state *state,
|
||||
trailing++;
|
||||
check_old_for_crlf(patch, line, len);
|
||||
if (!state->apply_in_reverse &&
|
||||
state->ws_error_action == correct_ws_error)
|
||||
check_whitespace(state, line, len, patch->ws_rule);
|
||||
state->ws_error_action == correct_ws_error) {
|
||||
const char *test_line = line;
|
||||
int test_len = len;
|
||||
if (*line == '\n') {
|
||||
test_line = " \n";
|
||||
test_len = 2;
|
||||
}
|
||||
check_whitespace(state, test_line, test_len,
|
||||
patch->ws_rule);
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
if (!state->apply_in_reverse)
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
|
||||
.repo = repo,
|
||||
.current_batch = OID_ARRAY_INIT,
|
||||
.min_batch_size = 50000,
|
||||
.sparse = 0,
|
||||
.sparse = -1,
|
||||
.revs = REV_INFO_INIT,
|
||||
};
|
||||
struct option options[] = {
|
||||
|
||||
+6
-3
@@ -161,7 +161,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
|
||||
|
||||
case 'e':
|
||||
ret = !odb_has_object(the_repository->objects, &oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR);
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR);
|
||||
goto cleanup;
|
||||
|
||||
case 'w':
|
||||
@@ -848,6 +848,9 @@ static void batch_each_object(struct batch_options *opt,
|
||||
.callback = callback,
|
||||
.payload = _payload,
|
||||
};
|
||||
struct odb_for_each_object_options opts = {
|
||||
.flags = flags,
|
||||
};
|
||||
struct bitmap_index *bitmap = NULL;
|
||||
struct odb_source *source;
|
||||
|
||||
@@ -860,7 +863,7 @@ static void batch_each_object(struct batch_options *opt,
|
||||
odb_prepare_alternates(the_repository->objects);
|
||||
for (source = the_repository->objects->sources; source; source = source->next) {
|
||||
int ret = odb_source_loose_for_each_object(source, NULL, batch_one_object_oi,
|
||||
&payload, flags);
|
||||
&payload, &opts);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@@ -884,7 +887,7 @@ static void batch_each_object(struct batch_options *opt,
|
||||
for (source = the_repository->objects->sources; source; source = source->next) {
|
||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
||||
int ret = packfile_store_for_each_object(files->packed, &oi,
|
||||
batch_one_object_oi, &payload, flags);
|
||||
batch_one_object_oi, &payload, &opts);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
+4
-3
@@ -838,6 +838,7 @@ static int get_urlmatch(const struct config_location_options *opts,
|
||||
const char *var, const char *url)
|
||||
{
|
||||
int ret;
|
||||
char *section;
|
||||
char *section_tail;
|
||||
struct config_display_options display_opts = *_display_opts;
|
||||
struct string_list_item *item;
|
||||
@@ -851,8 +852,8 @@ static int get_urlmatch(const struct config_location_options *opts,
|
||||
if (!url_normalize(url, &config.url))
|
||||
die("%s", config.url.err);
|
||||
|
||||
config.section = xstrdup_tolower(var);
|
||||
section_tail = strchr(config.section, '.');
|
||||
config.section = section = xstrdup_tolower(var);
|
||||
section_tail = strchr(section, '.');
|
||||
if (section_tail) {
|
||||
*section_tail = '\0';
|
||||
config.key = section_tail + 1;
|
||||
@@ -886,7 +887,7 @@ static int get_urlmatch(const struct config_location_options *opts,
|
||||
string_list_clear(&values, 1);
|
||||
free(config.url.url);
|
||||
|
||||
free((void *)config.section);
|
||||
free(section);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+2
-13
@@ -64,7 +64,8 @@ static int parse_opt_sign_mode(const struct option *opt,
|
||||
if (unset)
|
||||
return 0;
|
||||
|
||||
if (parse_sign_mode(arg, val, NULL))
|
||||
if (parse_sign_mode(arg, val, NULL) || (*val == SIGN_STRIP_IF_INVALID) ||
|
||||
(*val == SIGN_SIGN_IF_INVALID) || (*val == SIGN_ABORT_IF_INVALID))
|
||||
return error(_("unknown %s mode: %s"), opt->long_name, arg);
|
||||
|
||||
return 0;
|
||||
@@ -822,12 +823,6 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
|
||||
die(_("encountered signed commit %s; use "
|
||||
"--signed-commits=<mode> to handle it"),
|
||||
oid_to_hex(&commit->object.oid));
|
||||
case SIGN_STRIP_IF_INVALID:
|
||||
die(_("'strip-if-invalid' is not a valid mode for "
|
||||
"git fast-export with --signed-commits=<mode>"));
|
||||
case SIGN_SIGN_IF_INVALID:
|
||||
die(_("'sign-if-invalid' is not a valid mode for "
|
||||
"git fast-export with --signed-commits=<mode>"));
|
||||
default:
|
||||
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
|
||||
}
|
||||
@@ -970,12 +965,6 @@ static void handle_tag(const char *name, struct tag *tag)
|
||||
die(_("encountered signed tag %s; use "
|
||||
"--signed-tags=<mode> to handle it"),
|
||||
oid_to_hex(&tag->object.oid));
|
||||
case SIGN_STRIP_IF_INVALID:
|
||||
die(_("'strip-if-invalid' is not a valid mode for "
|
||||
"git fast-export with --signed-tags=<mode>"));
|
||||
case SIGN_SIGN_IF_INVALID:
|
||||
die(_("'sign-if-invalid' is not a valid mode for "
|
||||
"git fast-export with --signed-tags=<mode>"));
|
||||
default:
|
||||
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
|
||||
}
|
||||
|
||||
+60
-11
@@ -191,6 +191,7 @@ static const char *global_prefix;
|
||||
static enum sign_mode signed_tag_mode = SIGN_VERBATIM;
|
||||
static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
|
||||
static const char *signed_commit_keyid;
|
||||
static const char *signed_tag_keyid;
|
||||
|
||||
/* Memory pools */
|
||||
static struct mem_pool fi_mem_pool = {
|
||||
@@ -2892,6 +2893,9 @@ static void handle_signature_if_invalid(struct strbuf *new_data,
|
||||
ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check);
|
||||
|
||||
if (ret) {
|
||||
if (mode == SIGN_ABORT_IF_INVALID)
|
||||
die(_("aborting due to invalid signature"));
|
||||
|
||||
warn_invalid_signature(&signature_check, msg->buf, mode);
|
||||
|
||||
if (mode == SIGN_SIGN_IF_INVALID) {
|
||||
@@ -2983,6 +2987,7 @@ static void parse_new_commit(const char *arg)
|
||||
case SIGN_VERBATIM:
|
||||
case SIGN_STRIP_IF_INVALID:
|
||||
case SIGN_SIGN_IF_INVALID:
|
||||
case SIGN_ABORT_IF_INVALID:
|
||||
import_one_signature(&sig_sha1, &sig_sha256, v);
|
||||
break;
|
||||
|
||||
@@ -3068,7 +3073,8 @@ static void parse_new_commit(const char *arg)
|
||||
encoding);
|
||||
|
||||
if ((signed_commit_mode == SIGN_STRIP_IF_INVALID ||
|
||||
signed_commit_mode == SIGN_SIGN_IF_INVALID) &&
|
||||
signed_commit_mode == SIGN_SIGN_IF_INVALID ||
|
||||
signed_commit_mode == SIGN_ABORT_IF_INVALID) &&
|
||||
(sig_sha1.hash_algo || sig_sha256.hash_algo))
|
||||
handle_signature_if_invalid(&new_data, &sig_sha1, &sig_sha256,
|
||||
&msg, signed_commit_mode);
|
||||
@@ -3084,7 +3090,50 @@ static void parse_new_commit(const char *arg)
|
||||
b->last_commit = object_count_by_type[OBJ_COMMIT];
|
||||
}
|
||||
|
||||
static void handle_tag_signature(struct strbuf *msg, const char *name)
|
||||
static void handle_tag_signature_if_invalid(struct strbuf *buf,
|
||||
struct strbuf *msg,
|
||||
size_t sig_offset)
|
||||
{
|
||||
struct strbuf signature = STRBUF_INIT;
|
||||
struct strbuf payload = STRBUF_INIT;
|
||||
struct signature_check sigc = { 0 };
|
||||
|
||||
strbuf_addbuf(&payload, buf);
|
||||
strbuf_addch(&payload, '\n');
|
||||
strbuf_add(&payload, msg->buf, sig_offset);
|
||||
strbuf_add(&signature, msg->buf + sig_offset, msg->len - sig_offset);
|
||||
|
||||
sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
|
||||
sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
|
||||
|
||||
if (!check_signature(&sigc, signature.buf, signature.len))
|
||||
goto out;
|
||||
|
||||
if (signed_tag_mode == SIGN_ABORT_IF_INVALID)
|
||||
die(_("aborting due to invalid signature"));
|
||||
|
||||
strbuf_setlen(msg, sig_offset);
|
||||
|
||||
if (signed_tag_mode == SIGN_SIGN_IF_INVALID) {
|
||||
strbuf_attach(&payload, sigc.payload, sigc.payload_len,
|
||||
sigc.payload_len + 1);
|
||||
sigc.payload = NULL;
|
||||
strbuf_reset(&signature);
|
||||
|
||||
if (sign_buffer(&payload, &signature, signed_tag_keyid,
|
||||
SIGN_BUFFER_USE_DEFAULT_KEY))
|
||||
die(_("failed to sign tag object"));
|
||||
|
||||
strbuf_addbuf(msg, &signature);
|
||||
}
|
||||
|
||||
out:
|
||||
signature_check_clear(&sigc);
|
||||
strbuf_release(&signature);
|
||||
strbuf_release(&payload);
|
||||
}
|
||||
|
||||
static void handle_tag_signature(struct strbuf *buf, struct strbuf *msg, const char *name)
|
||||
{
|
||||
size_t sig_offset = parse_signed_buffer(msg->buf, msg->len);
|
||||
|
||||
@@ -3110,17 +3159,16 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
|
||||
/* Truncate the buffer to remove the signature */
|
||||
strbuf_setlen(msg, sig_offset);
|
||||
break;
|
||||
case SIGN_ABORT_IF_INVALID:
|
||||
case SIGN_SIGN_IF_INVALID:
|
||||
case SIGN_STRIP_IF_INVALID:
|
||||
handle_tag_signature_if_invalid(buf, msg, sig_offset);
|
||||
break;
|
||||
|
||||
/* Third, aborting modes */
|
||||
case SIGN_ABORT:
|
||||
die(_("encountered signed tag; use "
|
||||
"--signed-tags=<mode> to handle it"));
|
||||
case SIGN_STRIP_IF_INVALID:
|
||||
die(_("'strip-if-invalid' is not a valid mode for "
|
||||
"git fast-import with --signed-tags=<mode>"));
|
||||
case SIGN_SIGN_IF_INVALID:
|
||||
die(_("'sign-if-invalid' is not a valid mode for "
|
||||
"git fast-import with --signed-tags=<mode>"));
|
||||
default:
|
||||
BUG("invalid signed_tag_mode value %d from tag '%s'",
|
||||
signed_tag_mode, name);
|
||||
@@ -3190,8 +3238,6 @@ static void parse_new_tag(const char *arg)
|
||||
/* tag payload/message */
|
||||
parse_data(&msg, 0, NULL);
|
||||
|
||||
handle_tag_signature(&msg, t->name);
|
||||
|
||||
/* build the tag object */
|
||||
strbuf_reset(&new_data);
|
||||
|
||||
@@ -3203,6 +3249,9 @@ static void parse_new_tag(const char *arg)
|
||||
if (tagger)
|
||||
strbuf_addf(&new_data,
|
||||
"tagger %s\n", tagger);
|
||||
|
||||
handle_tag_signature(&new_data, &msg, t->name);
|
||||
|
||||
strbuf_addch(&new_data, '\n');
|
||||
strbuf_addbuf(&new_data, &msg);
|
||||
free(tagger);
|
||||
@@ -3713,7 +3762,7 @@ static int parse_one_option(const char *option)
|
||||
if (parse_sign_mode(option, &signed_commit_mode, &signed_commit_keyid))
|
||||
usagef(_("unknown --signed-commits mode '%s'"), option);
|
||||
} else if (skip_prefix(option, "signed-tags=", &option)) {
|
||||
if (parse_sign_mode(option, &signed_tag_mode, NULL))
|
||||
if (parse_sign_mode(option, &signed_tag_mode, &signed_tag_keyid))
|
||||
usagef(_("unknown --signed-tags mode '%s'"), option);
|
||||
} else if (!strcmp(option, "quiet")) {
|
||||
show_stats = 0;
|
||||
|
||||
+2
-2
@@ -946,7 +946,7 @@ static int update_local_ref(struct ref *ref,
|
||||
int fast_forward = 0;
|
||||
|
||||
if (!odb_has_object(the_repository->objects, &ref->new_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
die(_("object %s not found"), oid_to_hex(&ref->new_oid));
|
||||
|
||||
if (oideq(&ref->old_oid, &ref->new_oid)) {
|
||||
@@ -1396,7 +1396,7 @@ static int check_exist_and_connected(struct ref *ref_map)
|
||||
*/
|
||||
for (r = rm; r; r = r->next) {
|
||||
if (!odb_has_object(the_repository->objects, &r->old_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED))
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
+150
-125
@@ -1,4 +1,3 @@
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
#include "builtin.h"
|
||||
#include "gettext.h"
|
||||
#include "hex.h"
|
||||
@@ -42,8 +41,8 @@ static int check_full = 1;
|
||||
static int connectivity_only;
|
||||
static int check_strict;
|
||||
static int keep_cache_objects;
|
||||
static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
|
||||
static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
|
||||
static struct fsck_options fsck_walk_options;
|
||||
static struct fsck_options fsck_obj_options;
|
||||
static int errors_found;
|
||||
static int write_lost_and_found;
|
||||
static int verbose;
|
||||
@@ -66,14 +65,14 @@ static const char *describe_object(const struct object_id *oid)
|
||||
return fsck_describe_object(&fsck_walk_options, oid);
|
||||
}
|
||||
|
||||
static const char *printable_type(const struct object_id *oid,
|
||||
static const char *printable_type(struct repository *repo,
|
||||
const struct object_id *oid,
|
||||
enum object_type type)
|
||||
{
|
||||
const char *ret;
|
||||
|
||||
if (type == OBJ_NONE)
|
||||
type = odb_read_object_info(the_repository->objects,
|
||||
oid, NULL);
|
||||
type = odb_read_object_info(repo->objects, oid, NULL);
|
||||
|
||||
ret = type_name(type);
|
||||
if (!ret)
|
||||
@@ -82,17 +81,17 @@ static const char *printable_type(const struct object_id *oid,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int objerror(struct object *obj, const char *err)
|
||||
static int objerror(struct repository *repo, struct object *obj, const char *err)
|
||||
{
|
||||
errors_found |= ERROR_OBJECT;
|
||||
/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
|
||||
fprintf_ln(stderr, _("error in %s %s: %s"),
|
||||
printable_type(&obj->oid, obj->type),
|
||||
printable_type(repo, &obj->oid, obj->type),
|
||||
describe_object(&obj->oid), err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int fsck_objects_error_func(struct fsck_options *o UNUSED,
|
||||
static int fsck_objects_error_func(struct fsck_options *o,
|
||||
void *fsck_report,
|
||||
enum fsck_msg_type msg_type,
|
||||
enum fsck_msg_id msg_id UNUSED,
|
||||
@@ -106,13 +105,13 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
|
||||
case FSCK_WARN:
|
||||
/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
|
||||
fprintf_ln(stderr, _("warning in %s %s: %s"),
|
||||
printable_type(oid, object_type),
|
||||
printable_type(o->repo, oid, object_type),
|
||||
describe_object(oid), message);
|
||||
return 0;
|
||||
case FSCK_ERROR:
|
||||
/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
|
||||
fprintf_ln(stderr, _("error in %s %s: %s"),
|
||||
printable_type(oid, object_type),
|
||||
printable_type(o->repo, oid, object_type),
|
||||
describe_object(oid), message);
|
||||
return 1;
|
||||
default:
|
||||
@@ -124,7 +123,7 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
|
||||
static struct object_array pending;
|
||||
|
||||
static int mark_object(struct object *obj, enum object_type type,
|
||||
void *data, struct fsck_options *options UNUSED)
|
||||
void *data, struct fsck_options *options)
|
||||
{
|
||||
struct object *parent = data;
|
||||
|
||||
@@ -136,7 +135,7 @@ static int mark_object(struct object *obj, enum object_type type,
|
||||
if (!obj) {
|
||||
/* ... these references to parent->fld are safe here */
|
||||
printf_ln(_("broken link from %7s %s"),
|
||||
printable_type(&parent->oid, parent->type),
|
||||
printable_type(options->repo, &parent->oid, parent->type),
|
||||
describe_object(&parent->oid));
|
||||
printf_ln(_("broken link from %7s %s"),
|
||||
(type == OBJ_ANY ? _("unknown") : type_name(type)),
|
||||
@@ -147,13 +146,13 @@ static int mark_object(struct object *obj, enum object_type type,
|
||||
|
||||
if (type != OBJ_ANY && obj->type != type)
|
||||
/* ... and the reference to parent is safe here */
|
||||
objerror(parent, _("wrong object type in link"));
|
||||
objerror(options->repo, parent, _("wrong object type in link"));
|
||||
|
||||
if (obj->flags & REACHABLE)
|
||||
return 0;
|
||||
obj->flags |= REACHABLE;
|
||||
|
||||
if (is_promisor_object(the_repository, &obj->oid))
|
||||
if (is_promisor_object(options->repo, &obj->oid))
|
||||
/*
|
||||
* Further recursion does not need to be performed on this
|
||||
* object since it is a promisor object (so it does not need to
|
||||
@@ -162,13 +161,13 @@ static int mark_object(struct object *obj, enum object_type type,
|
||||
return 0;
|
||||
|
||||
if (!(obj->flags & HAS_OBJ)) {
|
||||
if (parent && !odb_has_object(the_repository->objects, &obj->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED)) {
|
||||
if (parent && !odb_has_object(options->repo->objects, &obj->oid,
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED)) {
|
||||
printf_ln(_("broken link from %7s %s\n"
|
||||
" to %7s %s"),
|
||||
printable_type(&parent->oid, parent->type),
|
||||
printable_type(options->repo, &parent->oid, parent->type),
|
||||
describe_object(&parent->oid),
|
||||
printable_type(&obj->oid, obj->type),
|
||||
printable_type(options->repo, &obj->oid, obj->type),
|
||||
describe_object(&obj->oid));
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
}
|
||||
@@ -181,7 +180,7 @@ static int mark_object(struct object *obj, enum object_type type,
|
||||
|
||||
static void mark_object_reachable(struct object *obj)
|
||||
{
|
||||
mark_object(obj, OBJ_ANY, NULL, NULL);
|
||||
mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options);
|
||||
}
|
||||
|
||||
static int traverse_one_object(struct object *obj)
|
||||
@@ -195,13 +194,13 @@ static int traverse_one_object(struct object *obj)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int traverse_reachable(void)
|
||||
static int traverse_reachable(struct repository *repo)
|
||||
{
|
||||
struct progress *progress = NULL;
|
||||
unsigned int nr = 0;
|
||||
int result = 0;
|
||||
if (show_progress)
|
||||
progress = start_delayed_progress(the_repository,
|
||||
progress = start_delayed_progress(repo,
|
||||
_("Checking connectivity"), 0);
|
||||
while (pending.nr) {
|
||||
result |= traverse_one_object(object_array_pop(&pending));
|
||||
@@ -222,10 +221,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED,
|
||||
|
||||
static int mark_unreachable_referents(const struct object_id *oid,
|
||||
struct object_info *oi UNUSED,
|
||||
void *data UNUSED)
|
||||
void *data)
|
||||
{
|
||||
struct fsck_options options = FSCK_OPTIONS_DEFAULT;
|
||||
struct object *obj = lookup_object(the_repository, oid);
|
||||
struct repository *repo = data;
|
||||
struct fsck_options options;
|
||||
struct object *obj = lookup_object(data, oid);
|
||||
|
||||
if (!obj || !(obj->flags & HAS_OBJ))
|
||||
return 0; /* not part of our original set */
|
||||
@@ -237,12 +237,13 @@ static int mark_unreachable_referents(const struct object_id *oid,
|
||||
* (and we want to avoid parsing blobs).
|
||||
*/
|
||||
if (obj->type == OBJ_NONE) {
|
||||
enum object_type type = odb_read_object_info(the_repository->objects,
|
||||
enum object_type type = odb_read_object_info(repo->objects,
|
||||
&obj->oid, NULL);
|
||||
if (type > 0)
|
||||
object_as_type(obj, type, 0);
|
||||
}
|
||||
|
||||
fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT);
|
||||
options.walk = mark_used;
|
||||
fsck_walk(obj, NULL, &options);
|
||||
if (obj->type == OBJ_TREE)
|
||||
@@ -254,7 +255,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
|
||||
/*
|
||||
* Check a single reachable object
|
||||
*/
|
||||
static void check_reachable_object(struct object *obj)
|
||||
static void check_reachable_object(struct repository *repo, struct object *obj)
|
||||
{
|
||||
/*
|
||||
* We obviously want the object to be parsed,
|
||||
@@ -262,12 +263,12 @@ static void check_reachable_object(struct object *obj)
|
||||
* do a full fsck
|
||||
*/
|
||||
if (!(obj->flags & HAS_OBJ)) {
|
||||
if (is_promisor_object(the_repository, &obj->oid))
|
||||
if (is_promisor_object(repo, &obj->oid))
|
||||
return;
|
||||
if (has_object_pack(the_repository, &obj->oid))
|
||||
if (has_object_pack(repo, &obj->oid))
|
||||
return; /* it is in pack - forget about it */
|
||||
printf_ln(_("missing %s %s"),
|
||||
printable_type(&obj->oid, obj->type),
|
||||
printable_type(repo, &obj->oid, obj->type),
|
||||
describe_object(&obj->oid));
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
return;
|
||||
@@ -277,7 +278,7 @@ static void check_reachable_object(struct object *obj)
|
||||
/*
|
||||
* Check a single unreachable object
|
||||
*/
|
||||
static void check_unreachable_object(struct object *obj)
|
||||
static void check_unreachable_object(struct repository *repo, struct object *obj)
|
||||
{
|
||||
/*
|
||||
* Missing unreachable object? Ignore it. It's not like
|
||||
@@ -294,7 +295,7 @@ static void check_unreachable_object(struct object *obj)
|
||||
*/
|
||||
if (show_unreachable) {
|
||||
printf_ln(_("unreachable %s %s"),
|
||||
printable_type(&obj->oid, obj->type),
|
||||
printable_type(repo, &obj->oid, obj->type),
|
||||
describe_object(&obj->oid));
|
||||
return;
|
||||
}
|
||||
@@ -314,22 +315,22 @@ static void check_unreachable_object(struct object *obj)
|
||||
if (!(obj->flags & USED)) {
|
||||
if (show_dangling)
|
||||
printf_ln(_("dangling %s %s"),
|
||||
printable_type(&obj->oid, obj->type),
|
||||
printable_type(repo, &obj->oid, obj->type),
|
||||
describe_object(&obj->oid));
|
||||
if (write_lost_and_found) {
|
||||
char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
|
||||
char *filename = repo_git_path(repo, "lost-found/%s/%s",
|
||||
obj->type == OBJ_COMMIT ? "commit" : "other",
|
||||
describe_object(&obj->oid));
|
||||
FILE *f;
|
||||
|
||||
if (safe_create_leading_directories_const(the_repository, filename)) {
|
||||
if (safe_create_leading_directories_const(repo, filename)) {
|
||||
error(_("could not create lost-found"));
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
f = xfopen(filename, "w");
|
||||
if (obj->type == OBJ_BLOB) {
|
||||
if (odb_stream_blob_to_fd(the_repository->objects, fileno(f),
|
||||
if (odb_stream_blob_to_fd(repo->objects, fileno(f),
|
||||
&obj->oid, NULL, 1))
|
||||
die_errno(_("could not write '%s'"), filename);
|
||||
} else
|
||||
@@ -349,23 +350,23 @@ static void check_unreachable_object(struct object *obj)
|
||||
*/
|
||||
}
|
||||
|
||||
static void check_object(struct object *obj)
|
||||
static void check_object(struct repository *repo, struct object *obj)
|
||||
{
|
||||
if (verbose)
|
||||
fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid));
|
||||
|
||||
if (obj->flags & REACHABLE)
|
||||
check_reachable_object(obj);
|
||||
check_reachable_object(repo, obj);
|
||||
else
|
||||
check_unreachable_object(obj);
|
||||
check_unreachable_object(repo, obj);
|
||||
}
|
||||
|
||||
static void check_connectivity(void)
|
||||
static void check_connectivity(struct repository *repo)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
/* Traverse the pending reachable objects */
|
||||
traverse_reachable();
|
||||
traverse_reachable(repo);
|
||||
|
||||
/*
|
||||
* With --connectivity-only, we won't have actually opened and marked
|
||||
@@ -383,24 +384,25 @@ static void check_connectivity(void)
|
||||
* and ignore any that weren't present in our earlier
|
||||
* traversal.
|
||||
*/
|
||||
odb_for_each_object(the_repository->objects, NULL,
|
||||
mark_unreachable_referents, NULL, 0);
|
||||
odb_for_each_object(repo->objects, NULL,
|
||||
mark_unreachable_referents, repo, 0);
|
||||
}
|
||||
|
||||
/* Look up all the requirements, warn about missing objects.. */
|
||||
max = get_max_object_index(the_repository);
|
||||
max = get_max_object_index(repo);
|
||||
if (verbose)
|
||||
fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
struct object *obj = get_indexed_object(the_repository, i);
|
||||
struct object *obj = get_indexed_object(repo, i);
|
||||
|
||||
if (obj)
|
||||
check_object(obj);
|
||||
check_object(repo, obj);
|
||||
}
|
||||
}
|
||||
|
||||
static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
|
||||
static int fsck_obj(struct repository *repo,
|
||||
struct object *obj, void *buffer, unsigned long size)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -410,11 +412,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
|
||||
|
||||
if (verbose)
|
||||
fprintf_ln(stderr, _("Checking %s %s"),
|
||||
printable_type(&obj->oid, obj->type),
|
||||
printable_type(repo, &obj->oid, obj->type),
|
||||
describe_object(&obj->oid));
|
||||
|
||||
if (fsck_walk(obj, NULL, &fsck_obj_options))
|
||||
objerror(obj, _("broken links"));
|
||||
objerror(repo, obj, _("broken links"));
|
||||
err = fsck_object(obj, buffer, size, &fsck_obj_options);
|
||||
if (err)
|
||||
goto out;
|
||||
@@ -432,7 +434,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
|
||||
|
||||
if (show_tags && tag->tagged) {
|
||||
printf_ln(_("tagged %s %s (%s) in %s"),
|
||||
printable_type(&tag->tagged->oid, tag->tagged->type),
|
||||
printable_type(repo, &tag->tagged->oid, tag->tagged->type),
|
||||
describe_object(&tag->tagged->oid),
|
||||
tag->tag,
|
||||
describe_object(&tag->object.oid));
|
||||
@@ -446,15 +448,16 @@ out:
|
||||
}
|
||||
|
||||
static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
|
||||
unsigned long size, void *buffer, int *eaten)
|
||||
unsigned long size, void *buffer, int *eaten, void *cb_data)
|
||||
{
|
||||
struct repository *repo = cb_data;
|
||||
struct object *obj;
|
||||
|
||||
/*
|
||||
* Note, buffer may be NULL if type is OBJ_BLOB. See
|
||||
* verify_packfile(), data_valid variable for details.
|
||||
*/
|
||||
struct object *obj;
|
||||
obj = parse_object_buffer(the_repository, oid, type, size, buffer,
|
||||
eaten);
|
||||
obj = parse_object_buffer(repo, oid, type, size, buffer, eaten);
|
||||
if (!obj) {
|
||||
errors_found |= ERROR_OBJECT;
|
||||
return error(_("%s: object corrupt or missing"),
|
||||
@@ -462,18 +465,19 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
|
||||
}
|
||||
obj->flags &= ~(REACHABLE | SEEN);
|
||||
obj->flags |= HAS_OBJ;
|
||||
return fsck_obj(obj, buffer, size);
|
||||
return fsck_obj(repo, obj, buffer, size);
|
||||
}
|
||||
|
||||
static int default_refs;
|
||||
|
||||
static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
|
||||
timestamp_t timestamp)
|
||||
static void fsck_handle_reflog_oid(struct repository *repo,
|
||||
const char *refname, struct object_id *oid,
|
||||
timestamp_t timestamp)
|
||||
{
|
||||
struct object *obj;
|
||||
|
||||
if (!is_null_oid(oid)) {
|
||||
obj = lookup_object(the_repository, oid);
|
||||
obj = lookup_object(repo, oid);
|
||||
if (obj && (obj->flags & HAS_OBJ)) {
|
||||
if (timestamp)
|
||||
fsck_put_object_name(&fsck_walk_options, oid,
|
||||
@@ -481,7 +485,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
|
||||
refname, timestamp);
|
||||
obj->flags |= USED;
|
||||
mark_object_reachable(obj);
|
||||
} else if (!is_promisor_object(the_repository, oid)) {
|
||||
} else if (!is_promisor_object(repo, oid)) {
|
||||
error(_("%s: invalid reflog entry %s"),
|
||||
refname, oid_to_hex(oid));
|
||||
errors_found |= ERROR_REACHABLE;
|
||||
@@ -493,8 +497,10 @@ static int fsck_handle_reflog_ent(const char *refname,
|
||||
struct object_id *ooid, struct object_id *noid,
|
||||
const char *email UNUSED,
|
||||
timestamp_t timestamp, int tz UNUSED,
|
||||
const char *message UNUSED, void *cb_data UNUSED)
|
||||
const char *message UNUSED, void *cb_data)
|
||||
{
|
||||
struct repository *repo = cb_data;
|
||||
|
||||
if (now && timestamp > now)
|
||||
return 0;
|
||||
|
||||
@@ -502,19 +508,20 @@ static int fsck_handle_reflog_ent(const char *refname,
|
||||
fprintf_ln(stderr, _("Checking reflog %s->%s"),
|
||||
oid_to_hex(ooid), oid_to_hex(noid));
|
||||
|
||||
fsck_handle_reflog_oid(refname, ooid, 0);
|
||||
fsck_handle_reflog_oid(refname, noid, timestamp);
|
||||
fsck_handle_reflog_oid(repo, refname, ooid, 0);
|
||||
fsck_handle_reflog_oid(repo, refname, noid, timestamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsck_handle_reflog(const char *logname, void *cb_data)
|
||||
{
|
||||
struct strbuf refname = STRBUF_INIT;
|
||||
struct worktree *wt = cb_data;
|
||||
|
||||
strbuf_worktree_ref(cb_data, &refname, logname);
|
||||
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
||||
strbuf_worktree_ref(wt, &refname, logname);
|
||||
refs_for_each_reflog_ent(get_main_ref_store(wt->repo),
|
||||
refname.buf, fsck_handle_reflog_ent,
|
||||
NULL);
|
||||
wt->repo);
|
||||
strbuf_release(&refname);
|
||||
return 0;
|
||||
}
|
||||
@@ -532,14 +539,20 @@ struct snapshot {
|
||||
/* TODO: Consider also snapshotting the index of each worktree. */
|
||||
};
|
||||
|
||||
struct snapshot_ref_data {
|
||||
struct repository *repo;
|
||||
struct snapshot *snap;
|
||||
};
|
||||
|
||||
static int snapshot_ref(const struct reference *ref, void *cb_data)
|
||||
{
|
||||
struct snapshot *snap = cb_data;
|
||||
struct snapshot_ref_data *data = cb_data;
|
||||
struct snapshot *snap = data->snap;
|
||||
struct object *obj;
|
||||
|
||||
obj = parse_object(the_repository, ref->oid);
|
||||
obj = parse_object(data->repo, ref->oid);
|
||||
if (!obj) {
|
||||
if (is_promisor_object(the_repository, ref->oid)) {
|
||||
if (is_promisor_object(data->repo, ref->oid)) {
|
||||
/*
|
||||
* Increment default_refs anyway, because this is a
|
||||
* valid ref.
|
||||
@@ -567,11 +580,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
|
||||
static int fsck_handle_ref(const struct reference *ref, void *cb_data)
|
||||
{
|
||||
struct repository *repo = cb_data;
|
||||
struct object *obj;
|
||||
|
||||
obj = parse_object(the_repository, ref->oid);
|
||||
obj = parse_object(repo, ref->oid);
|
||||
obj->flags |= USED;
|
||||
fsck_put_object_name(&fsck_walk_options,
|
||||
ref->oid, "%s", ref->name);
|
||||
@@ -580,11 +594,16 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
|
||||
static void snapshot_refs(struct repository *repo,
|
||||
struct snapshot *snap, int argc, const char **argv)
|
||||
{
|
||||
struct refs_for_each_ref_options opts = {
|
||||
.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
|
||||
};
|
||||
struct snapshot_ref_data data = {
|
||||
.repo = repo,
|
||||
.snap = snap,
|
||||
};
|
||||
struct worktree **worktrees, **p;
|
||||
const char *head_points_at;
|
||||
struct object_id head_oid;
|
||||
@@ -592,13 +611,13 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
struct object_id oid;
|
||||
if (!repo_get_oid(the_repository, arg, &oid)) {
|
||||
if (!repo_get_oid(repo, arg, &oid)) {
|
||||
struct reference ref = {
|
||||
.name = arg,
|
||||
.oid = &oid,
|
||||
};
|
||||
|
||||
snapshot_ref(&ref, snap);
|
||||
snapshot_ref(&ref, &data);
|
||||
continue;
|
||||
}
|
||||
error(_("invalid parameter: expected sha1, got '%s'"), arg);
|
||||
@@ -610,8 +629,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
|
||||
return;
|
||||
}
|
||||
|
||||
refs_for_each_ref_ext(get_main_ref_store(the_repository),
|
||||
snapshot_ref, snap, &opts);
|
||||
refs_for_each_ref_ext(get_main_ref_store(repo),
|
||||
snapshot_ref, &data, &opts);
|
||||
|
||||
worktrees = get_worktrees();
|
||||
for (p = worktrees; *p; p++) {
|
||||
@@ -620,7 +639,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
|
||||
|
||||
strbuf_worktree_ref(wt, &refname, "HEAD");
|
||||
|
||||
head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
|
||||
head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),
|
||||
refname.buf, 0, &head_oid, NULL);
|
||||
|
||||
if (head_points_at && !is_null_oid(&head_oid)) {
|
||||
@@ -629,7 +648,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
|
||||
.oid = &head_oid,
|
||||
};
|
||||
|
||||
snapshot_ref(&ref, snap);
|
||||
snapshot_ref(&ref, &data);
|
||||
}
|
||||
strbuf_release(&refname);
|
||||
|
||||
@@ -653,7 +672,7 @@ static void free_snapshot_refs(struct snapshot *snap)
|
||||
free(snap->ref);
|
||||
}
|
||||
|
||||
static void process_refs(struct snapshot *snap)
|
||||
static void process_refs(struct repository *repo, struct snapshot *snap)
|
||||
{
|
||||
struct worktree **worktrees, **p;
|
||||
|
||||
@@ -662,7 +681,7 @@ static void process_refs(struct snapshot *snap)
|
||||
.name = snap->ref[i].refname,
|
||||
.oid = &snap->ref[i].oid,
|
||||
};
|
||||
fsck_handle_ref(&ref, NULL);
|
||||
fsck_handle_ref(&ref, repo);
|
||||
}
|
||||
|
||||
if (include_reflogs) {
|
||||
@@ -694,27 +713,28 @@ static void process_refs(struct snapshot *snap)
|
||||
}
|
||||
}
|
||||
|
||||
struct for_each_loose_cb
|
||||
{
|
||||
struct for_each_loose_cb {
|
||||
struct repository *repo;
|
||||
struct progress *progress;
|
||||
};
|
||||
|
||||
static int fsck_loose(const struct object_id *oid, const char *path,
|
||||
void *data UNUSED)
|
||||
void *cb_data)
|
||||
{
|
||||
struct for_each_loose_cb *data = cb_data;
|
||||
struct object *obj;
|
||||
enum object_type type = OBJ_NONE;
|
||||
unsigned long size;
|
||||
void *contents = NULL;
|
||||
int eaten;
|
||||
struct object_info oi = OBJECT_INFO_INIT;
|
||||
struct object_id real_oid = *null_oid(the_hash_algo);
|
||||
struct object_id real_oid = *null_oid(data->repo->hash_algo);
|
||||
int err = 0;
|
||||
|
||||
oi.sizep = &size;
|
||||
oi.typep = &type;
|
||||
|
||||
if (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) {
|
||||
if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) {
|
||||
if (contents && !oideq(&real_oid, oid))
|
||||
err = error(_("%s: hash-path mismatch, found at: %s"),
|
||||
oid_to_hex(&real_oid), path);
|
||||
@@ -731,7 +751,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
|
||||
if (!contents && type != OBJ_BLOB)
|
||||
BUG("read_loose_object streamed a non-blob");
|
||||
|
||||
obj = parse_object_buffer(the_repository, oid, type, size,
|
||||
obj = parse_object_buffer(data->repo, oid, type, size,
|
||||
contents, &eaten);
|
||||
|
||||
if (!obj) {
|
||||
@@ -745,7 +765,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
|
||||
|
||||
obj->flags &= ~(REACHABLE | SEEN);
|
||||
obj->flags |= HAS_OBJ;
|
||||
if (fsck_obj(obj, contents, size))
|
||||
if (fsck_obj(data->repo, obj, contents, size))
|
||||
errors_found |= ERROR_OBJECT;
|
||||
|
||||
if (!eaten)
|
||||
@@ -769,10 +789,11 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsck_source(struct odb_source *source)
|
||||
static void fsck_source(struct repository *repo, struct odb_source *source)
|
||||
{
|
||||
struct progress *progress = NULL;
|
||||
struct for_each_loose_cb cb_data = {
|
||||
.repo = source->odb->repo,
|
||||
.progress = progress,
|
||||
};
|
||||
|
||||
@@ -780,7 +801,7 @@ static void fsck_source(struct odb_source *source)
|
||||
fprintf_ln(stderr, _("Checking object directory"));
|
||||
|
||||
if (show_progress)
|
||||
progress = start_progress(the_repository,
|
||||
progress = start_progress(repo,
|
||||
_("Checking object directories"), 256);
|
||||
|
||||
for_each_loose_file_in_source(source, fsck_loose,
|
||||
@@ -789,7 +810,7 @@ static void fsck_source(struct odb_source *source)
|
||||
stop_progress(&progress);
|
||||
}
|
||||
|
||||
static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
|
||||
static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path)
|
||||
{
|
||||
int i;
|
||||
int err = 0;
|
||||
@@ -798,7 +819,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
|
||||
fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
|
||||
|
||||
if (0 <= it->entry_count) {
|
||||
struct object *obj = parse_object(the_repository, &it->oid);
|
||||
struct object *obj = parse_object(repo, &it->oid);
|
||||
if (!obj) {
|
||||
error(_("%s: invalid sha1 pointer in cache-tree of %s"),
|
||||
oid_to_hex(&it->oid), index_path);
|
||||
@@ -809,10 +830,10 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
|
||||
fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
|
||||
mark_object_reachable(obj);
|
||||
if (obj->type != OBJ_TREE)
|
||||
err |= objerror(obj, _("non-tree in cache-tree"));
|
||||
err |= objerror(repo, obj, _("non-tree in cache-tree"));
|
||||
}
|
||||
for (i = 0; i < it->subtree_nr; i++)
|
||||
err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
|
||||
err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -838,7 +859,7 @@ static int fsck_resolve_undo(struct index_state *istate,
|
||||
if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
|
||||
continue;
|
||||
|
||||
obj = parse_object(the_repository, &ru->oid[i]);
|
||||
obj = parse_object(istate->repo, &ru->oid[i]);
|
||||
if (!obj) {
|
||||
error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
|
||||
oid_to_hex(&ru->oid[i]),
|
||||
@@ -870,7 +891,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,
|
||||
mode = istate->cache[i]->ce_mode;
|
||||
if (S_ISGITLINK(mode))
|
||||
continue;
|
||||
blob = lookup_blob(the_repository,
|
||||
blob = lookup_blob(istate->repo,
|
||||
&istate->cache[i]->oid);
|
||||
if (!blob)
|
||||
continue;
|
||||
@@ -883,15 +904,16 @@ static void fsck_index(struct index_state *istate, const char *index_path,
|
||||
mark_object_reachable(obj);
|
||||
}
|
||||
if (istate->cache_tree)
|
||||
fsck_cache_tree(istate->cache_tree, index_path);
|
||||
fsck_cache_tree(istate->repo, istate->cache_tree, index_path);
|
||||
fsck_resolve_undo(istate, index_path);
|
||||
}
|
||||
|
||||
static int mark_object_for_connectivity(const struct object_id *oid,
|
||||
struct object_info *oi UNUSED,
|
||||
void *cb_data UNUSED)
|
||||
void *cb_data)
|
||||
{
|
||||
struct object *obj = lookup_unknown_object(the_repository, oid);
|
||||
struct repository *repo = cb_data;
|
||||
struct object *obj = lookup_unknown_object(repo, oid);
|
||||
obj->flags |= HAS_OBJ;
|
||||
return 0;
|
||||
}
|
||||
@@ -906,7 +928,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
|
||||
if (show_progress) {
|
||||
repo_for_each_pack(r, p)
|
||||
pack_count++;
|
||||
progress = start_delayed_progress(the_repository,
|
||||
progress = start_delayed_progress(r,
|
||||
"Verifying reverse pack-indexes", pack_count);
|
||||
pack_count = 0;
|
||||
}
|
||||
@@ -986,7 +1008,7 @@ static struct option fsck_opts[] = {
|
||||
int cmd_fsck(int argc,
|
||||
const char **argv,
|
||||
const char *prefix,
|
||||
struct repository *repo UNUSED)
|
||||
struct repository *repo)
|
||||
{
|
||||
struct odb_source *source;
|
||||
struct snapshot snap = {
|
||||
@@ -1004,7 +1026,10 @@ int cmd_fsck(int argc,
|
||||
|
||||
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
|
||||
|
||||
fsck_options_init(&fsck_walk_options, repo, FSCK_OPTIONS_DEFAULT);
|
||||
fsck_walk_options.walk = mark_object;
|
||||
|
||||
fsck_options_init(&fsck_obj_options, repo, FSCK_OPTIONS_DEFAULT);
|
||||
fsck_obj_options.walk = mark_used;
|
||||
fsck_obj_options.error_func = fsck_objects_error_func;
|
||||
if (check_strict)
|
||||
@@ -1023,11 +1048,11 @@ int cmd_fsck(int argc,
|
||||
if (name_objects)
|
||||
fsck_enable_object_names(&fsck_walk_options);
|
||||
|
||||
repo_config(the_repository, git_fsck_config, &fsck_obj_options);
|
||||
prepare_repo_settings(the_repository);
|
||||
repo_config(repo, git_fsck_config, &fsck_obj_options);
|
||||
prepare_repo_settings(repo);
|
||||
|
||||
if (check_references)
|
||||
fsck_refs(the_repository);
|
||||
fsck_refs(repo);
|
||||
|
||||
/*
|
||||
* Take a snapshot of the refs before walking objects to avoid looking
|
||||
@@ -1035,18 +1060,18 @@ int cmd_fsck(int argc,
|
||||
* objects. We can still walk over new objects that are added during the
|
||||
* execution of fsck but won't miss any objects that were reachable.
|
||||
*/
|
||||
snapshot_refs(&snap, argc, argv);
|
||||
snapshot_refs(repo, &snap, argc, argv);
|
||||
|
||||
/* Ensure we get a "fresh" view of the odb */
|
||||
odb_reprepare(the_repository->objects);
|
||||
odb_reprepare(repo->objects);
|
||||
|
||||
if (connectivity_only) {
|
||||
odb_for_each_object(the_repository->objects, NULL,
|
||||
mark_object_for_connectivity, NULL, 0);
|
||||
odb_for_each_object(repo->objects, NULL,
|
||||
mark_object_for_connectivity, repo, 0);
|
||||
} else {
|
||||
odb_prepare_alternates(the_repository->objects);
|
||||
for (source = the_repository->objects->sources; source; source = source->next)
|
||||
fsck_source(source);
|
||||
odb_prepare_alternates(repo->objects);
|
||||
for (source = repo->objects->sources; source; source = source->next)
|
||||
fsck_source(repo, source);
|
||||
|
||||
if (check_full) {
|
||||
struct packed_git *p;
|
||||
@@ -1054,20 +1079,20 @@ int cmd_fsck(int argc,
|
||||
struct progress *progress = NULL;
|
||||
|
||||
if (show_progress) {
|
||||
repo_for_each_pack(the_repository, p) {
|
||||
repo_for_each_pack(repo, p) {
|
||||
if (open_pack_index(p))
|
||||
continue;
|
||||
total += p->num_objects;
|
||||
}
|
||||
|
||||
progress = start_progress(the_repository,
|
||||
progress = start_progress(repo,
|
||||
_("Checking objects"), total);
|
||||
}
|
||||
|
||||
repo_for_each_pack(the_repository, p) {
|
||||
repo_for_each_pack(repo, p) {
|
||||
/* verify gives error messages itself */
|
||||
if (verify_pack(the_repository,
|
||||
p, fsck_obj_buffer,
|
||||
if (verify_pack(repo,
|
||||
p, fsck_obj_buffer, repo,
|
||||
progress, count))
|
||||
errors_found |= ERROR_PACK;
|
||||
count += p->num_objects;
|
||||
@@ -1080,7 +1105,7 @@ int cmd_fsck(int argc,
|
||||
}
|
||||
|
||||
/* Process the snapshotted refs and the reflogs. */
|
||||
process_refs(&snap);
|
||||
process_refs(repo, &snap);
|
||||
|
||||
/* If not given any explicit objects, process index files too. */
|
||||
if (!argc)
|
||||
@@ -1100,7 +1125,7 @@ int cmd_fsck(int argc,
|
||||
for (p = worktrees; *p; p++) {
|
||||
struct worktree *wt = *p;
|
||||
struct index_state istate =
|
||||
INDEX_STATE_INIT(the_repository);
|
||||
INDEX_STATE_INIT(repo);
|
||||
char *path, *wt_gitdir;
|
||||
|
||||
/*
|
||||
@@ -1121,17 +1146,17 @@ int cmd_fsck(int argc,
|
||||
free_worktrees(worktrees);
|
||||
}
|
||||
|
||||
errors_found |= check_pack_rev_indexes(the_repository, show_progress);
|
||||
if (verify_bitmap_files(the_repository))
|
||||
errors_found |= check_pack_rev_indexes(repo, show_progress);
|
||||
if (verify_bitmap_files(repo))
|
||||
errors_found |= ERROR_BITMAP;
|
||||
|
||||
check_connectivity();
|
||||
check_connectivity(repo);
|
||||
|
||||
if (the_repository->settings.core_commit_graph) {
|
||||
if (repo->settings.core_commit_graph) {
|
||||
struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
|
||||
|
||||
odb_prepare_alternates(the_repository->objects);
|
||||
for (source = the_repository->objects->sources; source; source = source->next) {
|
||||
odb_prepare_alternates(repo->objects);
|
||||
for (source = repo->objects->sources; source; source = source->next) {
|
||||
child_process_init(&commit_graph_verify);
|
||||
commit_graph_verify.git_cmd = 1;
|
||||
strvec_pushl(&commit_graph_verify.args, "commit-graph",
|
||||
@@ -1145,11 +1170,11 @@ int cmd_fsck(int argc,
|
||||
}
|
||||
}
|
||||
|
||||
if (the_repository->settings.core_multi_pack_index) {
|
||||
if (repo->settings.core_multi_pack_index) {
|
||||
struct child_process midx_verify = CHILD_PROCESS_INIT;
|
||||
|
||||
odb_prepare_alternates(the_repository->objects);
|
||||
for (source = the_repository->objects->sources; source; source = source->next) {
|
||||
odb_prepare_alternates(repo->objects);
|
||||
for (source = repo->objects->sources; source; source = source->next) {
|
||||
child_process_init(&midx_verify);
|
||||
midx_verify.git_cmd = 1;
|
||||
strvec_pushl(&midx_verify.args, "multi-pack-index",
|
||||
|
||||
+4
-4
@@ -437,8 +437,8 @@ static int cmd_history_reword(int argc,
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
int dry_run = 0;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, N_("<action>"),
|
||||
N_("control which refs should be updated (branches|head)"),
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
|
||||
N_("control which refs should be updated"),
|
||||
PARSE_OPT_NONEG, parse_ref_action),
|
||||
OPT_BOOL('n', "dry-run", &dry_run,
|
||||
N_("perform a dry-run without updating any refs")),
|
||||
@@ -666,8 +666,8 @@ static int cmd_history_split(int argc,
|
||||
enum ref_action action = REF_ACTION_DEFAULT;
|
||||
int dry_run = 0;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, N_("<refs>"),
|
||||
N_("control ref update behavior (branches|head|print)"),
|
||||
OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
|
||||
N_("control ref update behavior"),
|
||||
PARSE_OPT_NONEG, parse_ref_action),
|
||||
OPT_BOOL('n', "dry-run", &dry_run,
|
||||
N_("perform a dry-run without updating any refs")),
|
||||
|
||||
@@ -136,7 +136,7 @@ static int nr_threads;
|
||||
static int from_stdin;
|
||||
static int strict;
|
||||
static int do_fsck_object;
|
||||
static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
|
||||
static struct fsck_options fsck_options;
|
||||
static int verbose;
|
||||
static const char *progress_title;
|
||||
static int show_resolving_progress;
|
||||
@@ -891,7 +891,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
|
||||
if (startup_info->have_repository) {
|
||||
read_lock();
|
||||
collision_test_needed = odb_has_object(the_repository->objects, oid,
|
||||
HAS_OBJECT_FETCH_PROMISOR);
|
||||
ODB_HAS_OBJECT_FETCH_PROMISOR);
|
||||
read_unlock();
|
||||
}
|
||||
|
||||
@@ -1908,6 +1908,8 @@ int cmd_index_pack(int argc,
|
||||
show_usage_if_asked(argc, argv, index_pack_usage);
|
||||
|
||||
disable_replace_refs();
|
||||
|
||||
fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
|
||||
fsck_options.walk = mark_link;
|
||||
|
||||
reset_pack_idx_option(&opts);
|
||||
|
||||
+3
-2
@@ -16,7 +16,7 @@ static char const * const builtin_mktag_usage[] = {
|
||||
};
|
||||
static int option_strict = 1;
|
||||
|
||||
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
|
||||
static struct fsck_options fsck_options;
|
||||
|
||||
static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
|
||||
void *fsck_report UNUSED,
|
||||
@@ -75,7 +75,7 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
|
||||
int cmd_mktag(int argc,
|
||||
const char **argv,
|
||||
const char *prefix,
|
||||
struct repository *repo UNUSED)
|
||||
struct repository *repo)
|
||||
{
|
||||
static struct option builtin_mktag_options[] = {
|
||||
OPT_BOOL(0, "strict", &option_strict,
|
||||
@@ -94,6 +94,7 @@ int cmd_mktag(int argc,
|
||||
if (strbuf_read(&buf, 0, 0) < 0)
|
||||
die_errno(_("could not read from stdin"));
|
||||
|
||||
fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
|
||||
fsck_options.error_func = mktag_fsck_error_func;
|
||||
fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY,
|
||||
FSCK_WARN);
|
||||
|
||||
+213
-98
@@ -28,6 +28,7 @@
|
||||
#include "reachable.h"
|
||||
#include "oid-array.h"
|
||||
#include "strvec.h"
|
||||
#include "strmap.h"
|
||||
#include "list.h"
|
||||
#include "packfile.h"
|
||||
#include "object-file.h"
|
||||
@@ -217,6 +218,7 @@ static int have_non_local_packs;
|
||||
static int incremental;
|
||||
static int ignore_packed_keep_on_disk;
|
||||
static int ignore_packed_keep_in_core;
|
||||
static int ignore_packed_keep_in_core_open;
|
||||
static int ignore_packed_keep_in_core_has_cruft;
|
||||
static int allow_ofs_delta;
|
||||
static struct pack_idx_option pack_idx_opts;
|
||||
@@ -1632,7 +1634,8 @@ static int want_found_object(const struct object_id *oid, int exclude,
|
||||
/*
|
||||
* Then handle .keep first, as we have a fast(er) path there.
|
||||
*/
|
||||
if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core) {
|
||||
if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core ||
|
||||
ignore_packed_keep_in_core_open) {
|
||||
/*
|
||||
* Set the flags for the kept-pack cache to be the ones we want
|
||||
* to ignore.
|
||||
@@ -1646,6 +1649,8 @@ static int want_found_object(const struct object_id *oid, int exclude,
|
||||
flags |= KEPT_PACK_ON_DISK;
|
||||
if (ignore_packed_keep_in_core)
|
||||
flags |= KEPT_PACK_IN_CORE;
|
||||
if (ignore_packed_keep_in_core_open)
|
||||
flags |= KEPT_PACK_IN_CORE_OPEN;
|
||||
|
||||
/*
|
||||
* If the object is in a pack that we want to ignore, *and* we
|
||||
@@ -1657,6 +1662,8 @@ static int want_found_object(const struct object_id *oid, int exclude,
|
||||
return 0;
|
||||
if (ignore_packed_keep_in_core && p->pack_keep_in_core)
|
||||
return 0;
|
||||
if (ignore_packed_keep_in_core_open && p->pack_keep_in_core_open)
|
||||
return 0;
|
||||
if (has_object_kept_pack(p->repo, oid, flags))
|
||||
return 0;
|
||||
} else {
|
||||
@@ -3756,6 +3763,7 @@ static int add_object_entry_from_pack(const struct object_id *oid,
|
||||
void *_data)
|
||||
{
|
||||
off_t ofs;
|
||||
struct object_info oi = OBJECT_INFO_INIT;
|
||||
enum object_type type = OBJ_NONE;
|
||||
|
||||
display_progress(progress_state, ++nr_seen);
|
||||
@@ -3763,29 +3771,34 @@ static int add_object_entry_from_pack(const struct object_id *oid,
|
||||
if (have_duplicate_entry(oid, 0))
|
||||
return 0;
|
||||
|
||||
stdin_packs_found_nr++;
|
||||
|
||||
ofs = nth_packed_object_offset(p, pos);
|
||||
|
||||
oi.typep = &type;
|
||||
if (packed_object_info(p, ofs, &oi) < 0) {
|
||||
die(_("could not get type of object %s in pack %s"),
|
||||
oid_to_hex(oid), p->pack_name);
|
||||
} else if (type == OBJ_COMMIT) {
|
||||
struct rev_info *revs = _data;
|
||||
/*
|
||||
* commits in included packs are used as starting points
|
||||
* for the subsequent revision walk
|
||||
*
|
||||
* Note that we do want to walk through commits that are
|
||||
* present in excluded-open ('!') packs to pick up any
|
||||
* objects reachable from them not present in the
|
||||
* excluded-closed ('^') packs.
|
||||
*
|
||||
* However, we'll only add those objects to the packing
|
||||
* list after checking `want_object_in_pack()` below.
|
||||
*/
|
||||
add_pending_oid(revs, NULL, oid, 0);
|
||||
}
|
||||
|
||||
if (!want_object_in_pack(oid, 0, &p, &ofs))
|
||||
return 0;
|
||||
|
||||
if (p) {
|
||||
struct object_info oi = OBJECT_INFO_INIT;
|
||||
|
||||
oi.typep = &type;
|
||||
if (packed_object_info(p, ofs, &oi) < 0) {
|
||||
die(_("could not get type of object %s in pack %s"),
|
||||
oid_to_hex(oid), p->pack_name);
|
||||
} else if (type == OBJ_COMMIT) {
|
||||
struct rev_info *revs = _data;
|
||||
/*
|
||||
* commits in included packs are used as starting points for the
|
||||
* subsequent revision walk
|
||||
*/
|
||||
add_pending_oid(revs, NULL, oid, 0);
|
||||
}
|
||||
|
||||
stdin_packs_found_nr++;
|
||||
}
|
||||
|
||||
create_object_entry(oid, type, 0, 0, 0, p, ofs);
|
||||
|
||||
return 0;
|
||||
@@ -3835,87 +3848,78 @@ static void show_commit_pack_hint(struct commit *commit, void *data)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* stdin_pack_info_kind specifies how a pack specified over stdin
|
||||
* should be treated when pack-objects is invoked with --stdin-packs.
|
||||
*
|
||||
* - STDIN_PACK_INCLUDE: objects in any packs with this flag bit set
|
||||
* should be included in the output pack, unless they appear in an
|
||||
* excluded pack.
|
||||
*
|
||||
* - STDIN_PACK_EXCLUDE_CLOSED: objects in any packs with this flag
|
||||
* bit set should be excluded from the output pack.
|
||||
*
|
||||
* - STDIN_PACK_EXCLUDE_OPEN: objects in any packs with this flag
|
||||
* bit set should be excluded from the output pack, but are not
|
||||
* guaranteed to be closed under reachability.
|
||||
*
|
||||
* Objects in packs whose 'kind' bits include STDIN_PACK_INCLUDE or
|
||||
* STDIN_PACK_EXCLUDE_OPEN are used as traversal tips when invoked
|
||||
* with --stdin-packs=follow.
|
||||
*/
|
||||
enum stdin_pack_info_kind {
|
||||
STDIN_PACK_INCLUDE = (1<<0),
|
||||
STDIN_PACK_EXCLUDE_CLOSED = (1<<1),
|
||||
STDIN_PACK_EXCLUDE_OPEN = (1<<2),
|
||||
};
|
||||
|
||||
struct stdin_pack_info {
|
||||
struct packed_git *p;
|
||||
enum stdin_pack_info_kind kind;
|
||||
};
|
||||
|
||||
static int pack_mtime_cmp(const void *_a, const void *_b)
|
||||
{
|
||||
struct packed_git *a = ((const struct string_list_item*)_a)->util;
|
||||
struct packed_git *b = ((const struct string_list_item*)_b)->util;
|
||||
struct stdin_pack_info *a = ((const struct string_list_item*)_a)->util;
|
||||
struct stdin_pack_info *b = ((const struct string_list_item*)_b)->util;
|
||||
|
||||
/*
|
||||
* order packs by descending mtime so that objects are laid out
|
||||
* roughly as newest-to-oldest
|
||||
*/
|
||||
if (a->mtime < b->mtime)
|
||||
if (a->p->mtime < b->p->mtime)
|
||||
return 1;
|
||||
else if (b->mtime < a->mtime)
|
||||
else if (b->p->mtime < a->p->mtime)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_packs_list_from_stdin(struct rev_info *revs)
|
||||
static int stdin_packs_include_check_obj(struct object *obj, void *data UNUSED)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct string_list include_packs = STRING_LIST_INIT_DUP;
|
||||
struct string_list exclude_packs = STRING_LIST_INIT_DUP;
|
||||
struct string_list_item *item = NULL;
|
||||
struct packed_git *p;
|
||||
return !has_object_kept_pack(to_pack.repo, &obj->oid,
|
||||
KEPT_PACK_IN_CORE);
|
||||
}
|
||||
|
||||
while (strbuf_getline(&buf, stdin) != EOF) {
|
||||
if (!buf.len)
|
||||
continue;
|
||||
static int stdin_packs_include_check(struct commit *commit, void *data)
|
||||
{
|
||||
return stdin_packs_include_check_obj((struct object *)commit, data);
|
||||
}
|
||||
|
||||
if (*buf.buf == '^')
|
||||
string_list_append(&exclude_packs, buf.buf + 1);
|
||||
else
|
||||
string_list_append(&include_packs, buf.buf);
|
||||
static void stdin_packs_add_pack_entries(struct strmap *packs,
|
||||
struct rev_info *revs)
|
||||
{
|
||||
struct string_list keys = STRING_LIST_INIT_NODUP;
|
||||
struct string_list_item *item;
|
||||
struct hashmap_iter iter;
|
||||
struct strmap_entry *entry;
|
||||
|
||||
strbuf_reset(&buf);
|
||||
}
|
||||
strmap_for_each_entry(packs, &iter, entry) {
|
||||
struct stdin_pack_info *info = entry->value;
|
||||
if (!info->p)
|
||||
die(_("could not find pack '%s'"), entry->key);
|
||||
|
||||
string_list_sort_u(&include_packs, 0);
|
||||
string_list_sort_u(&exclude_packs, 0);
|
||||
|
||||
repo_for_each_pack(the_repository, p) {
|
||||
const char *pack_name = pack_basename(p);
|
||||
|
||||
if ((item = string_list_lookup(&include_packs, pack_name))) {
|
||||
if (exclude_promisor_objects && p->pack_promisor)
|
||||
die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name);
|
||||
item->util = p;
|
||||
}
|
||||
if ((item = string_list_lookup(&exclude_packs, pack_name)))
|
||||
item->util = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arguments we got on stdin may not even be packs. First
|
||||
* check that to avoid segfaulting later on in
|
||||
* e.g. pack_mtime_cmp(), excluded packs are handled below.
|
||||
*
|
||||
* Since we first parsed our STDIN and then sorted the input
|
||||
* lines the pack we error on will be whatever line happens to
|
||||
* sort first. This is lazy, it's enough that we report one
|
||||
* bad case here, we don't need to report the first/last one,
|
||||
* or all of them.
|
||||
*/
|
||||
for_each_string_list_item(item, &include_packs) {
|
||||
struct packed_git *p = item->util;
|
||||
if (!p)
|
||||
die(_("could not find pack '%s'"), item->string);
|
||||
if (!is_pack_valid(p))
|
||||
die(_("packfile %s cannot be accessed"), p->pack_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Then, handle all of the excluded packs, marking them as
|
||||
* kept in-core so that later calls to add_object_entry()
|
||||
* discards any objects that are also found in excluded packs.
|
||||
*/
|
||||
for_each_string_list_item(item, &exclude_packs) {
|
||||
struct packed_git *p = item->util;
|
||||
if (!p)
|
||||
die(_("could not find pack '%s'"), item->string);
|
||||
p->pack_keep_in_core = 1;
|
||||
string_list_append(&keys, entry->key)->util = info;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3923,19 +3927,118 @@ static void read_packs_list_from_stdin(struct rev_info *revs)
|
||||
* string_list_item's ->util pointer, which string_list_sort() does not
|
||||
* provide.
|
||||
*/
|
||||
QSORT(include_packs.items, include_packs.nr, pack_mtime_cmp);
|
||||
QSORT(keys.items, keys.nr, pack_mtime_cmp);
|
||||
|
||||
for_each_string_list_item(item, &include_packs) {
|
||||
struct packed_git *p = item->util;
|
||||
for_each_object_in_pack(p,
|
||||
add_object_entry_from_pack,
|
||||
revs,
|
||||
ODB_FOR_EACH_OBJECT_PACK_ORDER);
|
||||
for_each_string_list_item(item, &keys) {
|
||||
struct stdin_pack_info *info = item->util;
|
||||
|
||||
if (info->kind & STDIN_PACK_EXCLUDE_OPEN) {
|
||||
/*
|
||||
* When open-excluded packs ("!") are present, stop
|
||||
* the parent walk at closed-excluded ("^") packs.
|
||||
* Objects behind a "^" boundary are guaranteed to
|
||||
* have closure and should not be rescued.
|
||||
*/
|
||||
revs->include_check = stdin_packs_include_check;
|
||||
revs->include_check_obj = stdin_packs_include_check_obj;
|
||||
}
|
||||
|
||||
if ((info->kind & STDIN_PACK_INCLUDE) ||
|
||||
(info->kind & STDIN_PACK_EXCLUDE_OPEN))
|
||||
for_each_object_in_pack(info->p,
|
||||
add_object_entry_from_pack,
|
||||
revs,
|
||||
ODB_FOR_EACH_OBJECT_PACK_ORDER);
|
||||
}
|
||||
|
||||
string_list_clear(&keys, 0);
|
||||
}
|
||||
|
||||
static void stdin_packs_read_input(struct rev_info *revs,
|
||||
enum stdin_packs_mode mode)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct strmap packs = STRMAP_INIT;
|
||||
struct packed_git *p;
|
||||
|
||||
while (strbuf_getline(&buf, stdin) != EOF) {
|
||||
struct stdin_pack_info *info;
|
||||
enum stdin_pack_info_kind kind = STDIN_PACK_INCLUDE;
|
||||
const char *key = buf.buf;
|
||||
|
||||
if (!*key)
|
||||
continue;
|
||||
else if (*key == '^')
|
||||
kind = STDIN_PACK_EXCLUDE_CLOSED;
|
||||
else if (*key == '!' && mode == STDIN_PACKS_MODE_FOLLOW)
|
||||
kind = STDIN_PACK_EXCLUDE_OPEN;
|
||||
|
||||
if (kind != STDIN_PACK_INCLUDE)
|
||||
key++;
|
||||
|
||||
info = strmap_get(&packs, key);
|
||||
if (!info) {
|
||||
CALLOC_ARRAY(info, 1);
|
||||
strmap_put(&packs, key, info);
|
||||
}
|
||||
|
||||
info->kind |= kind;
|
||||
|
||||
strbuf_reset(&buf);
|
||||
}
|
||||
|
||||
repo_for_each_pack(the_repository, p) {
|
||||
struct stdin_pack_info *info;
|
||||
|
||||
info = strmap_get(&packs, pack_basename(p));
|
||||
if (!info)
|
||||
continue;
|
||||
|
||||
if (info->kind & STDIN_PACK_INCLUDE) {
|
||||
if (exclude_promisor_objects && p->pack_promisor)
|
||||
die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name);
|
||||
|
||||
/*
|
||||
* Arguments we got on stdin may not even be
|
||||
* packs. First check that to avoid segfaulting
|
||||
* later on in e.g. pack_mtime_cmp(), excluded
|
||||
* packs are handled below.
|
||||
*/
|
||||
if (!is_pack_valid(p))
|
||||
die(_("packfile %s cannot be accessed"), p->pack_name);
|
||||
}
|
||||
|
||||
if (info->kind & STDIN_PACK_EXCLUDE_CLOSED) {
|
||||
/*
|
||||
* Marking excluded packs as kept in-core so
|
||||
* that later calls to add_object_entry()
|
||||
* discards any objects that are also found in
|
||||
* excluded packs.
|
||||
*/
|
||||
p->pack_keep_in_core = 1;
|
||||
}
|
||||
|
||||
if (info->kind & STDIN_PACK_EXCLUDE_OPEN) {
|
||||
/*
|
||||
* Marking excluded open packs as kept in-core
|
||||
* (open) for the same reason as we marked
|
||||
* exclude closed packs as kept in-core.
|
||||
*
|
||||
* Use a separate flag here to ensure we don't
|
||||
* halt our traversal at these packs, since they
|
||||
* are not guaranteed to have closure.
|
||||
*
|
||||
*/
|
||||
p->pack_keep_in_core_open = 1;
|
||||
}
|
||||
|
||||
info->p = p;
|
||||
}
|
||||
|
||||
stdin_packs_add_pack_entries(&packs, revs);
|
||||
|
||||
strbuf_release(&buf);
|
||||
string_list_clear(&include_packs, 0);
|
||||
string_list_clear(&exclude_packs, 0);
|
||||
strmap_clear(&packs, 1);
|
||||
}
|
||||
|
||||
static void add_unreachable_loose_objects(struct rev_info *revs);
|
||||
@@ -3972,7 +4075,15 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked)
|
||||
|
||||
/* avoids adding objects in excluded packs */
|
||||
ignore_packed_keep_in_core = 1;
|
||||
read_packs_list_from_stdin(&revs);
|
||||
if (mode == STDIN_PACKS_MODE_FOLLOW) {
|
||||
/*
|
||||
* In '--stdin-packs=follow' mode, additionally ignore
|
||||
* objects in excluded-open packs to prevent them from
|
||||
* appearing in the resulting pack.
|
||||
*/
|
||||
ignore_packed_keep_in_core_open = 1;
|
||||
}
|
||||
stdin_packs_read_input(&revs, mode);
|
||||
if (rev_list_unpacked)
|
||||
add_unreachable_loose_objects(&revs);
|
||||
|
||||
@@ -3983,6 +4094,8 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked)
|
||||
show_object_pack_hint,
|
||||
&mode);
|
||||
|
||||
release_revisions(&revs);
|
||||
|
||||
trace2_data_intmax("pack-objects", the_repository, "stdin_packs_found",
|
||||
stdin_packs_found_nr);
|
||||
trace2_data_intmax("pack-objects", the_repository, "stdin_packs_hints",
|
||||
@@ -4359,6 +4472,12 @@ static void add_objects_in_unpacked_packs(void)
|
||||
{
|
||||
struct odb_source *source;
|
||||
time_t mtime;
|
||||
struct odb_for_each_object_options opts = {
|
||||
.flags = ODB_FOR_EACH_OBJECT_PACK_ORDER |
|
||||
ODB_FOR_EACH_OBJECT_LOCAL_ONLY |
|
||||
ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
|
||||
ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS,
|
||||
};
|
||||
struct object_info oi = {
|
||||
.mtimep = &mtime,
|
||||
};
|
||||
@@ -4371,11 +4490,7 @@ static void add_objects_in_unpacked_packs(void)
|
||||
continue;
|
||||
|
||||
if (packfile_store_for_each_object(files->packed, &oi,
|
||||
add_object_in_unpacked_pack, NULL,
|
||||
ODB_FOR_EACH_OBJECT_PACK_ORDER |
|
||||
ODB_FOR_EACH_OBJECT_LOCAL_ONLY |
|
||||
ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS |
|
||||
ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS))
|
||||
add_object_in_unpacked_pack, NULL, &opts))
|
||||
die(_("cannot open pack index"));
|
||||
}
|
||||
}
|
||||
|
||||
+16
-25
@@ -1384,32 +1384,16 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NEEDSWORK: we should consolidate various implementations of "are we
|
||||
* on an unborn branch?" test into one, and make the unified one more
|
||||
* robust. !get_sha1() based check used here and elsewhere would not
|
||||
* allow us to tell an unborn branch from corrupt ref, for example.
|
||||
* For the purpose of fixing "deploy-to-update does not work when
|
||||
* pushing into an empty repository" issue, this should suffice for
|
||||
* now.
|
||||
*/
|
||||
static int head_has_history(void)
|
||||
{
|
||||
struct object_id oid;
|
||||
|
||||
return !repo_get_oid(the_repository, "HEAD", &oid);
|
||||
}
|
||||
|
||||
static const char *push_to_deploy(unsigned char *sha1,
|
||||
struct strvec *env,
|
||||
const char *work_tree)
|
||||
const struct worktree *worktree)
|
||||
{
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
|
||||
strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
|
||||
"--refresh", NULL);
|
||||
strvec_pushv(&child.env, env->v);
|
||||
child.dir = work_tree;
|
||||
child.dir = worktree->path;
|
||||
child.no_stdin = 1;
|
||||
child.stdout_to_stderr = 1;
|
||||
child.git_cmd = 1;
|
||||
@@ -1421,7 +1405,7 @@ static const char *push_to_deploy(unsigned char *sha1,
|
||||
strvec_pushl(&child.args, "diff-files", "--quiet",
|
||||
"--ignore-submodules", "--", NULL);
|
||||
strvec_pushv(&child.env, env->v);
|
||||
child.dir = work_tree;
|
||||
child.dir = worktree->path;
|
||||
child.no_stdin = 1;
|
||||
child.stdout_to_stderr = 1;
|
||||
child.git_cmd = 1;
|
||||
@@ -1431,9 +1415,16 @@ static const char *push_to_deploy(unsigned char *sha1,
|
||||
child_process_init(&child);
|
||||
strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
|
||||
"--ignore-submodules",
|
||||
/* diff-index with either HEAD or an empty tree */
|
||||
head_has_history() ? "HEAD" : empty_tree_oid_hex(the_repository->hash_algo),
|
||||
"--", NULL);
|
||||
/*
|
||||
* diff-index with either HEAD or an empty tree
|
||||
*
|
||||
* NEEDSWORK: is_null_oid() cannot know whether it's an
|
||||
* unborn HEAD or a corrupt ref. It works for now because
|
||||
* it's only needed to know if we are comparing HEAD or an
|
||||
* empty tree.
|
||||
*/
|
||||
!is_null_oid(&worktree->head_oid) ? "HEAD" :
|
||||
empty_tree_oid_hex(the_repository->hash_algo), "--", NULL);
|
||||
strvec_pushv(&child.env, env->v);
|
||||
child.no_stdin = 1;
|
||||
child.no_stdout = 1;
|
||||
@@ -1446,7 +1437,7 @@ static const char *push_to_deploy(unsigned char *sha1,
|
||||
strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
|
||||
NULL);
|
||||
strvec_pushv(&child.env, env->v);
|
||||
child.dir = work_tree;
|
||||
child.dir = worktree->path;
|
||||
child.no_stdin = 1;
|
||||
child.no_stdout = 1;
|
||||
child.stdout_to_stderr = 0;
|
||||
@@ -1494,7 +1485,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
|
||||
|
||||
retval = push_to_checkout(sha1, &invoked_hook, &env, worktree->path);
|
||||
if (!invoked_hook)
|
||||
retval = push_to_deploy(sha1, &env, worktree->path);
|
||||
retval = push_to_deploy(sha1, &env, worktree);
|
||||
|
||||
strvec_clear(&env);
|
||||
free(git_dir);
|
||||
@@ -1550,7 +1541,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
|
||||
|
||||
if (!is_null_oid(new_oid) &&
|
||||
!odb_has_object(the_repository->objects, new_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
error("unpack should have generated %s, "
|
||||
"but I can't find it!", oid_to_hex(new_oid));
|
||||
ret = "bad pack";
|
||||
|
||||
+4
-2
@@ -78,9 +78,9 @@ out:
|
||||
}
|
||||
|
||||
static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
|
||||
struct repository *repo UNUSED)
|
||||
struct repository *repo)
|
||||
{
|
||||
struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
|
||||
struct fsck_options fsck_refs_options;
|
||||
struct worktree **worktrees;
|
||||
const char * const verify_usage[] = {
|
||||
REFS_VERIFY_USAGE,
|
||||
@@ -93,6 +93,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
|
||||
};
|
||||
int ret = 0;
|
||||
|
||||
fsck_options_init(&fsck_refs_options, repo, FSCK_OPTIONS_REFS);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
|
||||
if (argc)
|
||||
usage(_("'git refs verify' takes no arguments"));
|
||||
|
||||
+1
-1
@@ -473,7 +473,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
|
||||
else if (is_null_oid(&ref->old_oid))
|
||||
info->status = PUSH_STATUS_CREATE;
|
||||
else if (odb_has_object(the_repository->objects, &ref->old_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) &&
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) &&
|
||||
ref_newer(&ref->new_oid, &ref->old_oid))
|
||||
info->status = PUSH_STATUS_FASTFORWARD;
|
||||
else
|
||||
|
||||
+17
-2
@@ -369,8 +369,23 @@ int cmd_repack(int argc,
|
||||
*/
|
||||
for (i = 0; i < geometry.split; i++)
|
||||
fprintf(in, "%s\n", pack_basename(geometry.pack[i]));
|
||||
for (i = geometry.split; i < geometry.pack_nr; i++)
|
||||
fprintf(in, "^%s\n", pack_basename(geometry.pack[i]));
|
||||
for (i = geometry.split; i < geometry.pack_nr; i++) {
|
||||
const char *basename = pack_basename(geometry.pack[i]);
|
||||
char marker = '^';
|
||||
|
||||
if (!midx_must_contain_cruft &&
|
||||
!string_list_has_string(&existing.midx_packs,
|
||||
basename)) {
|
||||
/*
|
||||
* Assume non-MIDX'd packs are not
|
||||
* necessarily closed under
|
||||
* reachability.
|
||||
*/
|
||||
marker = '!';
|
||||
}
|
||||
|
||||
fprintf(in, "%c%s\n", marker, basename);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
|
||||
+24
-14
@@ -84,25 +84,33 @@ int cmd_replay(int argc,
|
||||
|
||||
const char *const replay_usage[] = {
|
||||
N_("(EXPERIMENTAL!) git replay "
|
||||
"([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) "
|
||||
"[--ref-action[=<mode>]] <revision-range>"),
|
||||
"([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)\n"
|
||||
"[--ref=<ref>] [--ref-action=<mode>] <revision-range>"),
|
||||
NULL
|
||||
};
|
||||
struct option replay_options[] = {
|
||||
OPT_STRING(0, "advance", &opts.advance,
|
||||
N_("branch"),
|
||||
N_("make replay advance given branch")),
|
||||
OPT_STRING(0, "onto", &opts.onto,
|
||||
N_("revision"),
|
||||
N_("replay onto given commit")),
|
||||
OPT_BOOL(0, "contained", &opts.contained,
|
||||
N_("update all branches that point at commits in <revision-range>")),
|
||||
OPT_STRING(0, "revert", &opts.revert,
|
||||
N_("branch"),
|
||||
N_("revert commits onto given branch")),
|
||||
OPT_STRING(0, "ref-action", &ref_action,
|
||||
N_("mode"),
|
||||
N_("control ref update behavior (update|print)")),
|
||||
OPT_STRING_F(0, "onto", &opts.onto,
|
||||
N_("revision"),
|
||||
N_("replay onto given commit"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_STRING_F(0, "advance", &opts.advance,
|
||||
N_("branch"),
|
||||
N_("make replay advance given branch"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_STRING_F(0, "revert", &opts.revert,
|
||||
N_("branch"),
|
||||
N_("revert commits onto given branch"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_STRING_F(0, "ref", &opts.ref,
|
||||
N_("branch"),
|
||||
N_("reference to update with result"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_STRING_F(0, "ref-action", &ref_action,
|
||||
N_("mode"),
|
||||
N_("control ref update behavior (update|print)"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -122,6 +130,8 @@ int cmd_replay(int argc,
|
||||
opts.contained, "--contained");
|
||||
die_for_incompatible_opt2(!!opts.revert, "--revert",
|
||||
opts.contained, "--contained");
|
||||
die_for_incompatible_opt2(!!opts.ref, "--ref",
|
||||
!!opts.contained, "--contained");
|
||||
|
||||
/* Parse ref action mode from command line or config */
|
||||
ref_mode = get_ref_action_mode(repo, ref_action);
|
||||
|
||||
+20
-20
@@ -267,21 +267,20 @@ static int show_file(const char *arg, int output_prefix)
|
||||
|
||||
static int try_difference(const char *arg)
|
||||
{
|
||||
char *dotdot;
|
||||
const char *dotdot;
|
||||
struct object_id start_oid;
|
||||
struct object_id end_oid;
|
||||
const char *end;
|
||||
const char *start;
|
||||
char *to_free;
|
||||
int symmetric;
|
||||
static const char head_by_default[] = "HEAD";
|
||||
|
||||
if (!(dotdot = strstr(arg, "..")))
|
||||
return 0;
|
||||
start = to_free = xmemdupz(arg, dotdot - arg);
|
||||
end = dotdot + 2;
|
||||
start = arg;
|
||||
symmetric = (*end == '.');
|
||||
|
||||
*dotdot = 0;
|
||||
end += symmetric;
|
||||
|
||||
if (!*end)
|
||||
@@ -295,7 +294,7 @@ static int try_difference(const char *arg)
|
||||
* Just ".."? That is not a range but the
|
||||
* pathspec for the parent directory.
|
||||
*/
|
||||
*dotdot = '.';
|
||||
free(to_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -308,7 +307,7 @@ static int try_difference(const char *arg)
|
||||
a = lookup_commit_reference(the_repository, &start_oid);
|
||||
b = lookup_commit_reference(the_repository, &end_oid);
|
||||
if (!a || !b) {
|
||||
*dotdot = '.';
|
||||
free(to_free);
|
||||
return 0;
|
||||
}
|
||||
if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0)
|
||||
@@ -318,16 +317,16 @@ static int try_difference(const char *arg)
|
||||
show_rev(REVERSED, &commit->object.oid, NULL);
|
||||
}
|
||||
}
|
||||
*dotdot = '.';
|
||||
free(to_free);
|
||||
return 1;
|
||||
}
|
||||
*dotdot = '.';
|
||||
free(to_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_parent_shorthands(const char *arg)
|
||||
{
|
||||
char *dotdot;
|
||||
const char *mark;
|
||||
struct object_id oid;
|
||||
struct commit *commit;
|
||||
struct commit_list *parents;
|
||||
@@ -335,38 +334,39 @@ static int try_parent_shorthands(const char *arg)
|
||||
int include_rev = 0;
|
||||
int include_parents = 0;
|
||||
int exclude_parent = 0;
|
||||
char *to_free;
|
||||
|
||||
if ((dotdot = strstr(arg, "^!"))) {
|
||||
if ((mark = strstr(arg, "^!"))) {
|
||||
include_rev = 1;
|
||||
if (dotdot[2])
|
||||
if (mark[2])
|
||||
return 0;
|
||||
} else if ((dotdot = strstr(arg, "^@"))) {
|
||||
} else if ((mark = strstr(arg, "^@"))) {
|
||||
include_parents = 1;
|
||||
if (dotdot[2])
|
||||
if (mark[2])
|
||||
return 0;
|
||||
} else if ((dotdot = strstr(arg, "^-"))) {
|
||||
} else if ((mark = strstr(arg, "^-"))) {
|
||||
include_rev = 1;
|
||||
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 0;
|
||||
}
|
||||
} else
|
||||
return 0;
|
||||
|
||||
*dotdot = 0;
|
||||
arg = to_free = xmemdupz(arg, mark - arg);
|
||||
if (repo_get_oid_committish(the_repository, arg, &oid) ||
|
||||
!(commit = lookup_commit_reference(the_repository, &oid))) {
|
||||
*dotdot = '^';
|
||||
free(to_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (exclude_parent &&
|
||||
exclude_parent > commit_list_count(commit->parents)) {
|
||||
*dotdot = '^';
|
||||
free(to_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ static int try_parent_shorthands(const char *arg)
|
||||
free(name);
|
||||
}
|
||||
|
||||
*dotdot = '^';
|
||||
free(to_free);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ static void show_one(const struct show_one_options *opts,
|
||||
struct object_id peeled;
|
||||
|
||||
if (!odb_has_object(the_repository->objects, ref->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
die("git show-ref: bad ref %s (%s)", ref->name,
|
||||
oid_to_hex(ref->oid));
|
||||
|
||||
|
||||
+2
-2
@@ -50,10 +50,10 @@
|
||||
#define BUILTIN_STASH_STORE_USAGE \
|
||||
N_("git stash store [(-m | --message) <message>] [-q | --quiet] <commit>")
|
||||
#define BUILTIN_STASH_PUSH_USAGE \
|
||||
N_("git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
|
||||
N_("git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
|
||||
" [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]\n" \
|
||||
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" \
|
||||
" [--] [<pathspec>...]]")
|
||||
" [--] [<pathspec>...]")
|
||||
#define BUILTIN_STASH_SAVE_USAGE \
|
||||
N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \
|
||||
" [-u | --include-untracked] [-a | --all] [<message>]")
|
||||
|
||||
@@ -29,7 +29,7 @@ static unsigned int offset, len;
|
||||
static off_t consumed_bytes;
|
||||
static off_t max_input_size;
|
||||
static struct git_hash_ctx ctx;
|
||||
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
|
||||
static struct fsck_options fsck_options;
|
||||
static struct progress *progress;
|
||||
|
||||
/*
|
||||
@@ -449,7 +449,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
||||
if (!delta_data)
|
||||
return;
|
||||
if (odb_has_object(the_repository->objects, &base_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
; /* Ok we have this one */
|
||||
else if (resolve_against_held(nr, &base_oid,
|
||||
delta_data, delta_size))
|
||||
@@ -613,7 +613,7 @@ static void unpack_all(void)
|
||||
int cmd_unpack_objects(int argc,
|
||||
const char **argv,
|
||||
const char *prefix UNUSED,
|
||||
struct repository *repo UNUSED)
|
||||
struct repository *repo)
|
||||
{
|
||||
int i;
|
||||
struct object_id oid;
|
||||
@@ -627,6 +627,8 @@ int cmd_unpack_objects(int argc,
|
||||
|
||||
show_usage_if_asked(argc, argv, unpack_usage);
|
||||
|
||||
fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
|
||||
|
||||
for (i = 1 ; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
|
||||
+7
-7
@@ -239,7 +239,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
|
||||
return 0;
|
||||
if (it->entry_count < 0 ||
|
||||
odb_has_object(the_repository->objects, &it->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
return 0;
|
||||
for (i = 0; i < it->subtree_nr; i++) {
|
||||
if (!cache_tree_fully_valid(it->down[i]->cache_tree))
|
||||
@@ -292,7 +292,7 @@ static int update_one(struct cache_tree *it,
|
||||
|
||||
if (0 <= it->entry_count &&
|
||||
odb_has_object(the_repository->objects, &it->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
return it->entry_count;
|
||||
|
||||
/*
|
||||
@@ -400,7 +400,7 @@ static int update_one(struct cache_tree *it,
|
||||
if (is_null_oid(oid) ||
|
||||
(!ce_missing_ok &&
|
||||
!odb_has_object(the_repository->objects, oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))) {
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))) {
|
||||
strbuf_release(&buffer);
|
||||
if (expected_missing)
|
||||
return -1;
|
||||
@@ -448,7 +448,7 @@ static int update_one(struct cache_tree *it,
|
||||
struct object_id oid;
|
||||
hash_object_file(the_hash_algo, buffer.buf, buffer.len,
|
||||
OBJ_TREE, &oid);
|
||||
if (odb_has_object(the_repository->objects, &oid, HAS_OBJECT_RECHECK_PACKED))
|
||||
if (odb_has_object(the_repository->objects, &oid, ODB_HAS_OBJECT_RECHECK_PACKED))
|
||||
oidcpy(&it->oid, &oid);
|
||||
else
|
||||
to_invalidate = 1;
|
||||
@@ -456,7 +456,7 @@ static int update_one(struct cache_tree *it,
|
||||
hash_object_file(the_hash_algo, buffer.buf, buffer.len,
|
||||
OBJ_TREE, &it->oid);
|
||||
} else if (odb_write_object_ext(the_repository->objects, buffer.buf, buffer.len, OBJ_TREE,
|
||||
&it->oid, NULL, flags & WRITE_TREE_SILENT ? WRITE_OBJECT_SILENT : 0)) {
|
||||
&it->oid, NULL, flags & WRITE_TREE_SILENT ? ODB_WRITE_OBJECT_SILENT : 0)) {
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
}
|
||||
@@ -488,12 +488,12 @@ int cache_tree_update(struct index_state *istate, int flags)
|
||||
prefetch_cache_entries(istate, must_check_existence);
|
||||
|
||||
trace_performance_enter();
|
||||
trace2_region_enter("cache_tree", "update", the_repository);
|
||||
trace2_region_enter("cache_tree", "update", istate->repo);
|
||||
transaction = odb_transaction_begin(the_repository->objects);
|
||||
i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
|
||||
"", 0, &skip, flags);
|
||||
odb_transaction_commit(transaction);
|
||||
trace2_region_leave("cache_tree", "update", the_repository);
|
||||
trace2_region_leave("cache_tree", "update", istate->repo);
|
||||
trace_performance_leave("cache_tree_update");
|
||||
if (i < 0)
|
||||
return i;
|
||||
|
||||
@@ -96,26 +96,28 @@ struct cb_node *cb_lookup(struct cb_tree *t, const uint8_t *k, size_t klen)
|
||||
return p && !memcmp(p->k, k, klen) ? p : NULL;
|
||||
}
|
||||
|
||||
static enum cb_next cb_descend(struct cb_node *p, cb_iter fn, void *arg)
|
||||
static int cb_descend(struct cb_node *p, cb_iter fn, void *arg)
|
||||
{
|
||||
if (1 & (uintptr_t)p) {
|
||||
struct cb_node *q = cb_node_of(p);
|
||||
enum cb_next n = cb_descend(q->child[0], fn, arg);
|
||||
|
||||
return n == CB_BREAK ? n : cb_descend(q->child[1], fn, arg);
|
||||
int ret = cb_descend(q->child[0], fn, arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
return cb_descend(q->child[1], fn, arg);
|
||||
} else {
|
||||
return fn(p, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
|
||||
cb_iter fn, void *arg)
|
||||
int cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
|
||||
cb_iter fn, void *arg)
|
||||
{
|
||||
struct cb_node *p = t->root;
|
||||
struct cb_node *top = p;
|
||||
size_t i = 0;
|
||||
|
||||
if (!p) return; /* empty tree */
|
||||
if (!p)
|
||||
return 0; /* empty tree */
|
||||
|
||||
/* Walk tree, maintaining top pointer */
|
||||
while (1 & (uintptr_t)p) {
|
||||
@@ -130,7 +132,8 @@ void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen,
|
||||
|
||||
for (i = 0; i < klen; i++) {
|
||||
if (p->k[i] != kpfx[i])
|
||||
return; /* "best" match failed */
|
||||
return 0; /* "best" match failed */
|
||||
}
|
||||
cb_descend(top, fn, arg);
|
||||
|
||||
return cb_descend(top, fn, arg);
|
||||
}
|
||||
|
||||
@@ -30,11 +30,6 @@ struct cb_tree {
|
||||
struct cb_node *root;
|
||||
};
|
||||
|
||||
enum cb_next {
|
||||
CB_CONTINUE = 0,
|
||||
CB_BREAK = 1
|
||||
};
|
||||
|
||||
#define CBTREE_INIT { 0 }
|
||||
|
||||
static inline void cb_init(struct cb_tree *t)
|
||||
@@ -46,9 +41,15 @@ static inline void cb_init(struct cb_tree *t)
|
||||
struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
|
||||
struct cb_node *cb_insert(struct cb_tree *, struct cb_node *, size_t klen);
|
||||
|
||||
typedef enum cb_next (*cb_iter)(struct cb_node *, void *arg);
|
||||
/*
|
||||
* Callback invoked by `cb_each()` for each node in the critbit tree. A return
|
||||
* value of 0 will cause the iteration to continue, a non-zero return code will
|
||||
* cause iteration to abort. The error code will be relayed back from
|
||||
* `cb_each()` in that case.
|
||||
*/
|
||||
typedef int (*cb_iter)(struct cb_node *, void *arg);
|
||||
|
||||
void cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen,
|
||||
cb_iter, void *arg);
|
||||
int cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen,
|
||||
cb_iter, void *arg);
|
||||
|
||||
#endif /* CBTREE_H */
|
||||
|
||||
+38
-4
@@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the generation offset between the commit date and its generation.
|
||||
* This is what's ultimately stored as generation number in the commit graph.
|
||||
*
|
||||
* Note that the computation of the commit date is more involved than you might
|
||||
* think. Instead of using the full commit date, we're in fact masking bits so
|
||||
* that only the 34 lowest bits are considered. This results from the fact that
|
||||
* commit graphs themselves only ever store 34 bits of the commit date
|
||||
* themselves.
|
||||
*
|
||||
* This means that if we have a commit date that exceeds 34 bits we'll end up
|
||||
* in situations where depending on whether the commit has been parsed from the
|
||||
* object database or the commit graph we'll have different dates, where the
|
||||
* ones parsed from the object database would have full 64 bit precision.
|
||||
*
|
||||
* But ultimately, we only ever want the offset to be relative to what we
|
||||
* actually end up storing on disk, and hence we have to mask all the other
|
||||
* bits.
|
||||
*/
|
||||
static timestamp_t compute_generation_offset(struct commit *c)
|
||||
{
|
||||
timestamp_t masked_date;
|
||||
|
||||
if (sizeof(timestamp_t) > 4)
|
||||
masked_date = c->date & (((timestamp_t) 1 << 34) - 1);
|
||||
else
|
||||
masked_date = c->date;
|
||||
|
||||
return commit_graph_data_at(c)->generation - masked_date;
|
||||
}
|
||||
|
||||
static int write_graph_chunk_generation_data(struct hashfile *f,
|
||||
void *data)
|
||||
{
|
||||
@@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f,
|
||||
struct commit *c = ctx->commits.items[i];
|
||||
timestamp_t offset;
|
||||
repo_parse_commit(ctx->r, c);
|
||||
offset = commit_graph_data_at(c)->generation - c->date;
|
||||
offset = compute_generation_offset(c);
|
||||
display_progress(ctx->progress, ++ctx->progress_cnt);
|
||||
|
||||
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
||||
@@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
|
||||
int i;
|
||||
for (i = 0; i < ctx->commits.nr; i++) {
|
||||
struct commit *c = ctx->commits.items[i];
|
||||
timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
|
||||
timestamp_t offset = compute_generation_offset(c);
|
||||
display_progress(ctx->progress, ++ctx->progress_cnt);
|
||||
|
||||
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
||||
@@ -1741,7 +1772,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
|
||||
|
||||
for (i = 0; i < ctx->commits.nr; i++) {
|
||||
struct commit *c = ctx->commits.items[i];
|
||||
timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
|
||||
timestamp_t offset = compute_generation_offset(c);
|
||||
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
|
||||
ctx->num_generation_data_overflows++;
|
||||
}
|
||||
@@ -1969,6 +2000,9 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
|
||||
{
|
||||
struct odb_source *source;
|
||||
enum object_type type;
|
||||
struct odb_for_each_object_options opts = {
|
||||
.flags = ODB_FOR_EACH_OBJECT_PACK_ORDER,
|
||||
};
|
||||
struct object_info oi = {
|
||||
.typep = &type,
|
||||
};
|
||||
@@ -1983,7 +2017,7 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
|
||||
for (source = ctx->r->objects->sources; source; source = source->next) {
|
||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
||||
packfile_store_for_each_object(files->packed, &oi, add_packed_commits_oi,
|
||||
ctx, ODB_FOR_EACH_OBJECT_PACK_ORDER);
|
||||
ctx, &opts);
|
||||
}
|
||||
|
||||
if (ctx->progress_done < ctx->approx_nr_objects)
|
||||
|
||||
+5
-1
@@ -1394,6 +1394,9 @@ revert_attrs:
|
||||
size_t mingw_strftime(char *s, size_t max,
|
||||
const char *format, const struct tm *tm)
|
||||
{
|
||||
#ifdef _UCRT
|
||||
size_t ret = strftime(s, max, format, tm);
|
||||
#else
|
||||
/* a pointer to the original strftime in case we can't find the UCRT version */
|
||||
static size_t (*fallback)(char *, size_t, const char *, const struct tm *) = strftime;
|
||||
size_t ret;
|
||||
@@ -1404,6 +1407,7 @@ size_t mingw_strftime(char *s, size_t max,
|
||||
ret = strftime(s, max, format, tm);
|
||||
else
|
||||
ret = fallback(s, max, format, tm);
|
||||
#endif
|
||||
|
||||
if (!ret && errno == EINVAL)
|
||||
die("invalid strftime format: '%s'", format);
|
||||
@@ -2460,7 +2464,7 @@ repeat:
|
||||
if (supports_file_rename_info_ex) {
|
||||
/*
|
||||
* Our minimum required Windows version is still set to Windows
|
||||
* Vista. We thus have to declare required infrastructure for
|
||||
* 8.1. We thus have to declare required infrastructure for
|
||||
* FileRenameInfoEx ourselves until we bump _WIN32_WINNT to
|
||||
* 0x0A00. Furthermore, we have to handle cases where the
|
||||
* FileRenameInfoEx call isn't supported yet.
|
||||
|
||||
@@ -500,7 +500,7 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x403
|
||||
#define _WIN32_WINNT 0x603
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#define HAVE_MMAP 1
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@
|
||||
|
||||
#define DISABLE_SIGN_COMPARE_WARNINGS
|
||||
|
||||
/* To bump the minimum Windows version to Windows Vista */
|
||||
/* To bump the minimum Windows version to Windows 8.1 */
|
||||
#include "git-compat-util.h"
|
||||
|
||||
/* Tell gcc not to warn about the (nfd < 0) tests, below. */
|
||||
@@ -41,7 +41,7 @@
|
||||
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
|
||||
# define WIN32_NATIVE
|
||||
# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
|
||||
# define _WIN32_WINNT 0x0502
|
||||
# define _WIN32_WINNT 0x0603
|
||||
# endif
|
||||
# include <winsock2.h>
|
||||
# include <windows.h>
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
|
||||
# if !defined(_WIN32_WINNT)
|
||||
# define _WIN32_WINNT 0x0600
|
||||
# define _WIN32_WINNT 0x0603
|
||||
# endif
|
||||
#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
|
||||
#include <winsock2.h>
|
||||
|
||||
@@ -6,7 +6,9 @@ int win32_fsync_no_flush(int fd)
|
||||
{
|
||||
IO_STATUS_BLOCK io_status;
|
||||
|
||||
#ifndef FLUSH_FLAGS_FILE_DATA_ONLY
|
||||
#define FLUSH_FLAGS_FILE_DATA_ONLY 1
|
||||
#endif
|
||||
|
||||
DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, NtFlushBuffersFileEx,
|
||||
HANDLE FileHandle, ULONG Flags, PVOID Parameters, ULONG ParameterSize,
|
||||
|
||||
+4
-33
@@ -32,47 +32,18 @@ static int non_ascii_used = 0;
|
||||
static HANDLE hthread, hread, hwrite;
|
||||
static HANDLE hconsole1, hconsole2;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 5
|
||||
typedef struct _CONSOLE_FONT_INFOEX {
|
||||
ULONG cbSize;
|
||||
DWORD nFont;
|
||||
COORD dwFontSize;
|
||||
UINT FontFamily;
|
||||
UINT FontWeight;
|
||||
WCHAR FaceName[LF_FACESIZE];
|
||||
} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void warn_if_raster_font(void)
|
||||
{
|
||||
DWORD fontFamily = 0;
|
||||
DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI,
|
||||
GetCurrentConsoleFontEx, HANDLE, BOOL,
|
||||
PCONSOLE_FONT_INFOEX);
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
|
||||
/* don't bother if output was ascii only */
|
||||
if (!non_ascii_used)
|
||||
return;
|
||||
|
||||
/* GetCurrentConsoleFontEx is available since Vista */
|
||||
if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof(cfi);
|
||||
if (GetCurrentConsoleFontEx(console, 0, &cfi))
|
||||
fontFamily = cfi.FontFamily;
|
||||
} else {
|
||||
/* pre-Vista: check default console font in registry */
|
||||
HKEY hkey;
|
||||
if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, "Console",
|
||||
0, KEY_READ, &hkey)) {
|
||||
DWORD size = sizeof(fontFamily);
|
||||
RegQueryValueExA(hkey, "FontFamily", NULL, NULL,
|
||||
(LPVOID) &fontFamily, &size);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
}
|
||||
cfi.cbSize = sizeof(cfi);
|
||||
if (GetCurrentConsoleFontEx(console, 0, &cfi))
|
||||
fontFamily = cfi.FontFamily;
|
||||
|
||||
if (!(fontFamily & TMPF_TRUETYPE)) {
|
||||
const wchar_t *msg = L"\nWarning: Your console font probably "
|
||||
|
||||
@@ -1054,6 +1054,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
|
||||
strvec_push(&proxy->args, port);
|
||||
proxy->in = -1;
|
||||
proxy->out = -1;
|
||||
proxy->clean_on_exit = 1;
|
||||
proxy->wait_after_clean = 1;
|
||||
if (start_command(proxy))
|
||||
die(_("cannot start proxy %s"), git_proxy_command);
|
||||
fd[0] = proxy->out; /* read from proxy stdout */
|
||||
@@ -1515,6 +1517,8 @@ struct child_process *git_connect(int fd[2], const char *url,
|
||||
}
|
||||
strvec_push(&conn->args, cmd.buf);
|
||||
|
||||
conn->clean_on_exit = 1;
|
||||
conn->wait_after_clean = 1;
|
||||
if (start_command(conn))
|
||||
die(_("unable to fork"));
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ endif()
|
||||
#function checks
|
||||
set(function_checks
|
||||
strcasestr memmem strlcpy strtoimax strtoumax strtoull
|
||||
setenv mkdtemp poll pread memmem)
|
||||
setenv mkdtemp poll pread memmem writev)
|
||||
|
||||
#unsetenv,hstrerror are incompatible with windows build
|
||||
if(NOT WIN32)
|
||||
@@ -421,6 +421,10 @@ if(NOT HAVE_MEMMEM)
|
||||
list(APPEND compat_SOURCES compat/memmem.c)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_WRITEV)
|
||||
list(APPEND compat_SOURCES compat/writev.c)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT HAVE_UNSETENV)
|
||||
list(APPEND compat_SOURCES compat/unsetenv.c)
|
||||
|
||||
@@ -608,6 +608,52 @@ struct emit_callback {
|
||||
struct strbuf *header;
|
||||
};
|
||||
|
||||
/*
|
||||
* State for the line-range callback wrappers that sit between
|
||||
* xdi_diff_outf() and fn_out_consume(). xdiff produces a normal,
|
||||
* unfiltered diff; the wrappers intercept each hunk header and line,
|
||||
* track post-image position, and forward only lines that fall within
|
||||
* the requested ranges. Contiguous in-range lines are collected into
|
||||
* range hunks and flushed with a synthetic @@ header so that
|
||||
* fn_out_consume() sees well-formed unified-diff fragments.
|
||||
*
|
||||
* Removal lines ('-') cannot be classified by post-image position, so
|
||||
* they are buffered in pending_rm until the next '+' or ' ' line
|
||||
* reveals whether they precede an in-range line (flush into range hunk) or
|
||||
* an out-of-range line (discard).
|
||||
*/
|
||||
struct line_range_callback {
|
||||
xdiff_emit_line_fn orig_line_fn;
|
||||
void *orig_cb_data;
|
||||
const struct range_set *ranges; /* 0-based [start, end) */
|
||||
unsigned int cur_range; /* index into the range_set */
|
||||
|
||||
/* Post/pre-image line counters (1-based, set from hunk headers) */
|
||||
long lno_post;
|
||||
long lno_pre;
|
||||
|
||||
/*
|
||||
* Function name from most recent xdiff hunk header;
|
||||
* size matches struct func_line.buf in xdiff/xemit.c.
|
||||
*/
|
||||
char func[80];
|
||||
long funclen;
|
||||
|
||||
/* Range hunk being accumulated for the current range */
|
||||
struct strbuf rhunk;
|
||||
long rhunk_old_begin, rhunk_old_count;
|
||||
long rhunk_new_begin, rhunk_new_count;
|
||||
int rhunk_active;
|
||||
int rhunk_has_changes; /* any '+' or '-' lines? */
|
||||
|
||||
/* Removal lines not yet known to be in-range */
|
||||
struct strbuf pending_rm;
|
||||
int pending_rm_count;
|
||||
long pending_rm_pre_begin; /* pre-image line of first pending */
|
||||
|
||||
int ret; /* latched error from orig_line_fn */
|
||||
};
|
||||
|
||||
static int count_lines(const char *data, int size)
|
||||
{
|
||||
int count, ch, completely_empty = 1, nl_just_seen = 0;
|
||||
@@ -2493,6 +2539,188 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void discard_pending_rm(struct line_range_callback *s)
|
||||
{
|
||||
strbuf_reset(&s->pending_rm);
|
||||
s->pending_rm_count = 0;
|
||||
}
|
||||
|
||||
static void flush_rhunk(struct line_range_callback *s)
|
||||
{
|
||||
struct strbuf hdr = STRBUF_INIT;
|
||||
const char *p, *end;
|
||||
|
||||
if (!s->rhunk_active || s->ret)
|
||||
return;
|
||||
|
||||
/* Drain any pending removal lines into the range hunk */
|
||||
if (s->pending_rm_count) {
|
||||
strbuf_addbuf(&s->rhunk, &s->pending_rm);
|
||||
s->rhunk_old_count += s->pending_rm_count;
|
||||
s->rhunk_has_changes = 1;
|
||||
discard_pending_rm(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Suppress context-only hunks: they contain no actual changes
|
||||
* and would just be noise. This can happen when the inflated
|
||||
* ctxlen causes xdiff to emit context covering a range that
|
||||
* has no changes in this commit.
|
||||
*/
|
||||
if (!s->rhunk_has_changes) {
|
||||
s->rhunk_active = 0;
|
||||
strbuf_reset(&s->rhunk);
|
||||
return;
|
||||
}
|
||||
|
||||
strbuf_addf(&hdr, "@@ -%ld,%ld +%ld,%ld @@",
|
||||
s->rhunk_old_begin, s->rhunk_old_count,
|
||||
s->rhunk_new_begin, s->rhunk_new_count);
|
||||
if (s->funclen > 0) {
|
||||
strbuf_addch(&hdr, ' ');
|
||||
strbuf_add(&hdr, s->func, s->funclen);
|
||||
}
|
||||
strbuf_addch(&hdr, '\n');
|
||||
|
||||
s->ret = s->orig_line_fn(s->orig_cb_data, hdr.buf, hdr.len);
|
||||
strbuf_release(&hdr);
|
||||
|
||||
/*
|
||||
* Replay buffered lines one at a time through fn_out_consume.
|
||||
* The cast discards const because xdiff_emit_line_fn takes
|
||||
* char *, though fn_out_consume does not modify the buffer.
|
||||
*/
|
||||
p = s->rhunk.buf;
|
||||
end = p + s->rhunk.len;
|
||||
while (!s->ret && p < end) {
|
||||
const char *eol = memchr(p, '\n', end - p);
|
||||
unsigned long line_len = eol ? (unsigned long)(eol - p + 1)
|
||||
: (unsigned long)(end - p);
|
||||
s->ret = s->orig_line_fn(s->orig_cb_data, (char *)p, line_len);
|
||||
p += line_len;
|
||||
}
|
||||
|
||||
s->rhunk_active = 0;
|
||||
strbuf_reset(&s->rhunk);
|
||||
}
|
||||
|
||||
static void line_range_hunk_fn(void *data,
|
||||
long old_begin, long old_nr UNUSED,
|
||||
long new_begin, long new_nr UNUSED,
|
||||
const char *func, long funclen)
|
||||
{
|
||||
struct line_range_callback *s = data;
|
||||
|
||||
/*
|
||||
* When count > 0, begin is 1-based. When count == 0, begin is
|
||||
* adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of
|
||||
* that type will arrive, so the value is unused.
|
||||
*
|
||||
* Any pending removal lines from the previous xdiff hunk are
|
||||
* intentionally left in pending_rm: the line callback will
|
||||
* flush or discard them when the next content line reveals
|
||||
* whether the removals precede in-range content.
|
||||
*/
|
||||
s->lno_post = new_begin;
|
||||
s->lno_pre = old_begin;
|
||||
|
||||
if (funclen > 0) {
|
||||
if (funclen > (long)sizeof(s->func))
|
||||
funclen = sizeof(s->func);
|
||||
memcpy(s->func, func, funclen);
|
||||
}
|
||||
s->funclen = funclen;
|
||||
}
|
||||
|
||||
static int line_range_line_fn(void *priv, char *line, unsigned long len)
|
||||
{
|
||||
struct line_range_callback *s = priv;
|
||||
const struct range *cur;
|
||||
long lno_0, cur_pre;
|
||||
|
||||
if (s->ret)
|
||||
return s->ret;
|
||||
|
||||
if (line[0] == '-') {
|
||||
if (!s->pending_rm_count)
|
||||
s->pending_rm_pre_begin = s->lno_pre;
|
||||
s->lno_pre++;
|
||||
strbuf_add(&s->pending_rm, line, len);
|
||||
s->pending_rm_count++;
|
||||
return s->ret;
|
||||
}
|
||||
|
||||
if (line[0] == '\\') {
|
||||
if (s->pending_rm_count)
|
||||
strbuf_add(&s->pending_rm, line, len);
|
||||
else if (s->rhunk_active)
|
||||
strbuf_add(&s->rhunk, line, len);
|
||||
/* otherwise outside tracked range; drop silently */
|
||||
return s->ret;
|
||||
}
|
||||
|
||||
if (line[0] != '+' && line[0] != ' ')
|
||||
BUG("unexpected diff line type '%c'", line[0]);
|
||||
|
||||
lno_0 = s->lno_post - 1;
|
||||
cur_pre = s->lno_pre; /* save before advancing for context lines */
|
||||
s->lno_post++;
|
||||
if (line[0] == ' ')
|
||||
s->lno_pre++;
|
||||
|
||||
/* Advance past ranges we've passed */
|
||||
while (s->cur_range < s->ranges->nr &&
|
||||
lno_0 >= s->ranges->ranges[s->cur_range].end) {
|
||||
if (s->rhunk_active)
|
||||
flush_rhunk(s);
|
||||
discard_pending_rm(s);
|
||||
s->cur_range++;
|
||||
}
|
||||
|
||||
/* Past all ranges */
|
||||
if (s->cur_range >= s->ranges->nr) {
|
||||
discard_pending_rm(s);
|
||||
return s->ret;
|
||||
}
|
||||
|
||||
cur = &s->ranges->ranges[s->cur_range];
|
||||
|
||||
/* Before current range */
|
||||
if (lno_0 < cur->start) {
|
||||
discard_pending_rm(s);
|
||||
return s->ret;
|
||||
}
|
||||
|
||||
/* In range so start a new range hunk if needed */
|
||||
if (!s->rhunk_active) {
|
||||
s->rhunk_active = 1;
|
||||
s->rhunk_has_changes = 0;
|
||||
s->rhunk_new_begin = lno_0 + 1;
|
||||
s->rhunk_old_begin = s->pending_rm_count
|
||||
? s->pending_rm_pre_begin : cur_pre;
|
||||
s->rhunk_old_count = 0;
|
||||
s->rhunk_new_count = 0;
|
||||
strbuf_reset(&s->rhunk);
|
||||
}
|
||||
|
||||
/* Flush pending removals into range hunk */
|
||||
if (s->pending_rm_count) {
|
||||
strbuf_addbuf(&s->rhunk, &s->pending_rm);
|
||||
s->rhunk_old_count += s->pending_rm_count;
|
||||
s->rhunk_has_changes = 1;
|
||||
discard_pending_rm(s);
|
||||
}
|
||||
|
||||
strbuf_add(&s->rhunk, line, len);
|
||||
s->rhunk_new_count++;
|
||||
if (line[0] == '+')
|
||||
s->rhunk_has_changes = 1;
|
||||
else
|
||||
s->rhunk_old_count++;
|
||||
|
||||
return s->ret;
|
||||
}
|
||||
|
||||
static void pprint_rename(struct strbuf *name, const char *a, const char *b)
|
||||
{
|
||||
const char *old_name = a;
|
||||
@@ -3592,7 +3820,8 @@ static void builtin_diff(const char *name_a,
|
||||
const char *xfrm_msg,
|
||||
int must_show_header,
|
||||
struct diff_options *o,
|
||||
int complete_rewrite)
|
||||
int complete_rewrite,
|
||||
const struct range_set *line_ranges)
|
||||
{
|
||||
mmfile_t mf1, mf2;
|
||||
const char *lbl[2];
|
||||
@@ -3833,6 +4062,52 @@ static void builtin_diff(const char *name_a,
|
||||
*/
|
||||
xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
|
||||
&ecbdata, &xpp, &xecfg);
|
||||
} else if (line_ranges) {
|
||||
struct line_range_callback lr_state;
|
||||
unsigned int i;
|
||||
long max_span = 0;
|
||||
|
||||
memset(&lr_state, 0, sizeof(lr_state));
|
||||
lr_state.orig_line_fn = fn_out_consume;
|
||||
lr_state.orig_cb_data = &ecbdata;
|
||||
lr_state.ranges = line_ranges;
|
||||
strbuf_init(&lr_state.rhunk, 0);
|
||||
strbuf_init(&lr_state.pending_rm, 0);
|
||||
|
||||
/*
|
||||
* Inflate ctxlen so that all changes within
|
||||
* any single range are merged into one xdiff
|
||||
* hunk and the inter-change context is emitted.
|
||||
* The callback clips back to range boundaries.
|
||||
*
|
||||
* The optimal ctxlen depends on where changes
|
||||
* fall within the range, which is only known
|
||||
* after xdiff runs; the max range span is the
|
||||
* upper bound that guarantees correctness in a
|
||||
* single pass.
|
||||
*/
|
||||
for (i = 0; i < line_ranges->nr; i++) {
|
||||
long span = line_ranges->ranges[i].end -
|
||||
line_ranges->ranges[i].start;
|
||||
if (span > max_span)
|
||||
max_span = span;
|
||||
}
|
||||
if (max_span > xecfg.ctxlen)
|
||||
xecfg.ctxlen = max_span;
|
||||
|
||||
if (xdi_diff_outf(&mf1, &mf2,
|
||||
line_range_hunk_fn,
|
||||
line_range_line_fn,
|
||||
&lr_state, &xpp, &xecfg))
|
||||
die("unable to generate diff for %s",
|
||||
one->path);
|
||||
|
||||
flush_rhunk(&lr_state);
|
||||
if (lr_state.ret)
|
||||
die("unable to generate diff for %s",
|
||||
one->path);
|
||||
strbuf_release(&lr_state.rhunk);
|
||||
strbuf_release(&lr_state.pending_rm);
|
||||
} else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
|
||||
&ecbdata, &xpp, &xecfg))
|
||||
die("unable to generate diff for %s", one->path);
|
||||
@@ -4674,7 +4949,7 @@ static void run_diff_cmd(const struct external_diff *pgm,
|
||||
|
||||
builtin_diff(name, other ? other : name,
|
||||
one, two, xfrm_msg, must_show_header,
|
||||
o, complete_rewrite);
|
||||
o, complete_rewrite, p->line_ranges);
|
||||
if (p->status == DIFF_STATUS_COPIED ||
|
||||
p->status == DIFF_STATUS_RENAMED)
|
||||
o->found_changes = 1;
|
||||
|
||||
+16
@@ -19,6 +19,17 @@ struct userdiff_driver;
|
||||
* in anything else.
|
||||
*/
|
||||
|
||||
/* A range [start, end). Lines are numbered starting at 0. */
|
||||
struct range {
|
||||
long start, end;
|
||||
};
|
||||
|
||||
/* A set of ranges. The ranges must always be disjoint and sorted. */
|
||||
struct range_set {
|
||||
unsigned int alloc, nr;
|
||||
struct range *ranges;
|
||||
};
|
||||
|
||||
/* We internally use unsigned short as the score value,
|
||||
* and rely on an int capable to hold 32-bits. -B can take
|
||||
* -Bmerge_score/break_score format and the two scores are
|
||||
@@ -106,6 +117,11 @@ int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
|
||||
struct diff_filepair {
|
||||
struct diff_filespec *one;
|
||||
struct diff_filespec *two;
|
||||
/*
|
||||
* Tracked line ranges for -L filtering; borrowed from
|
||||
* line_log_data and must not be freed.
|
||||
*/
|
||||
const struct range_set *line_ranges;
|
||||
unsigned short int score;
|
||||
char status; /* M C R A D U etc. (see Documentation/diff-format.adoc or DIFF_STATUS_* in diff.h) */
|
||||
unsigned broken_pair : 1;
|
||||
|
||||
+10
-3
@@ -51,7 +51,6 @@ static int server_supports_filtering;
|
||||
static int advertise_sid;
|
||||
static struct shallow_lock shallow_lock;
|
||||
static const char *alternate_shallow_file;
|
||||
static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
|
||||
static struct strbuf fsck_msg_types = STRBUF_INIT;
|
||||
static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
|
||||
|
||||
@@ -145,7 +144,7 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
|
||||
if (commit) {
|
||||
if (mark_tags_complete_and_check_obj_db) {
|
||||
if (!odb_has_object(the_repository->objects, oid,
|
||||
HAS_OBJECT_RECHECK_PACKED))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED))
|
||||
die_in_commit_graph_only(oid);
|
||||
}
|
||||
return commit;
|
||||
@@ -1096,6 +1095,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||
struct shallow_info *si,
|
||||
struct string_list *pack_lockfiles)
|
||||
{
|
||||
struct fsck_options fsck_options = { 0 };
|
||||
struct repository *r = the_repository;
|
||||
struct ref *ref = copy_ref_list(orig_ref);
|
||||
struct object_id oid;
|
||||
@@ -1224,6 +1224,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||
alternate_shallow_file = setup_temporary_shallow(si->shallow);
|
||||
} else
|
||||
alternate_shallow_file = NULL;
|
||||
|
||||
fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
|
||||
if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
|
||||
&fsck_options.gitmodules_found))
|
||||
die(_("git fetch-pack: fetch failed."));
|
||||
@@ -1231,6 +1233,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||
die("fsck failed");
|
||||
|
||||
all_done:
|
||||
fsck_options_clear(&fsck_options);
|
||||
if (negotiator)
|
||||
negotiator->release(negotiator);
|
||||
return ref;
|
||||
@@ -1650,6 +1653,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||
struct string_list *pack_lockfiles)
|
||||
{
|
||||
struct repository *r = the_repository;
|
||||
struct fsck_options fsck_options;
|
||||
struct ref *ref = copy_ref_list(orig_ref);
|
||||
enum fetch_state state = FETCH_CHECK_LOCAL;
|
||||
struct oidset common = OIDSET_INIT;
|
||||
@@ -1667,6 +1671,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||
struct strvec index_pack_args = STRVEC_INIT;
|
||||
const char *promisor_remote_config;
|
||||
|
||||
fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
|
||||
|
||||
if (server_feature_v2("promisor-remote", &promisor_remote_config))
|
||||
promisor_remote_reply(promisor_remote_config, NULL);
|
||||
|
||||
@@ -1878,6 +1884,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
|
||||
if (negotiator)
|
||||
negotiator->release(negotiator);
|
||||
|
||||
fsck_options_clear(&fsck_options);
|
||||
oidset_clear(&common);
|
||||
return ref;
|
||||
}
|
||||
@@ -2009,7 +2016,7 @@ static void update_shallow(struct fetch_pack_args *args,
|
||||
struct object_id *oid = si->shallow->oid;
|
||||
for (i = 0; i < si->shallow->nr; i++)
|
||||
if (odb_has_object(the_repository->objects, &oid[i],
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
oid_array_append(&extra, &oid[i]);
|
||||
if (extra.nr) {
|
||||
setup_alternate_shallow(&shallow_lock,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "git-compat-util.h"
|
||||
#include "date.h"
|
||||
#include "dir.h"
|
||||
@@ -207,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
|
||||
if (equal == len)
|
||||
die("skiplist requires a path");
|
||||
oidset_parse_file(&options->skip_oids, buf + equal + 1,
|
||||
the_repository->hash_algo);
|
||||
options->repo->hash_algo);
|
||||
buf += len + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -360,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
|
||||
int res = 0;
|
||||
const char *name;
|
||||
|
||||
if (repo_parse_tree(the_repository, tree))
|
||||
if (repo_parse_tree(options->repo, tree))
|
||||
return -1;
|
||||
|
||||
name = fsck_get_object_name(options, &tree->object.oid);
|
||||
@@ -375,14 +373,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
|
||||
continue;
|
||||
|
||||
if (S_ISDIR(entry.mode)) {
|
||||
obj = (struct object *)lookup_tree(the_repository, &entry.oid);
|
||||
obj = (struct object *)lookup_tree(options->repo, &entry.oid);
|
||||
if (name && obj)
|
||||
fsck_put_object_name(options, &entry.oid, "%s%s/",
|
||||
name, entry.path);
|
||||
result = options->walk(obj, OBJ_TREE, data, options);
|
||||
}
|
||||
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
|
||||
obj = (struct object *)lookup_blob(the_repository, &entry.oid);
|
||||
obj = (struct object *)lookup_blob(options->repo, &entry.oid);
|
||||
if (name && obj)
|
||||
fsck_put_object_name(options, &entry.oid, "%s%s",
|
||||
name, entry.path);
|
||||
@@ -409,7 +407,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
|
||||
int result;
|
||||
const char *name;
|
||||
|
||||
if (repo_parse_commit(the_repository, commit))
|
||||
if (repo_parse_commit(options->repo, commit))
|
||||
return -1;
|
||||
|
||||
name = fsck_get_object_name(options, &commit->object.oid);
|
||||
@@ -417,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
|
||||
fsck_put_object_name(options, get_commit_tree_oid(commit),
|
||||
"%s:", name);
|
||||
|
||||
result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),
|
||||
result = options->walk((struct object *) repo_get_commit_tree(options->repo, commit),
|
||||
OBJ_TREE, data, options);
|
||||
if (result < 0)
|
||||
return result;
|
||||
@@ -474,7 +472,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio
|
||||
{
|
||||
const char *name = fsck_get_object_name(options, &tag->object.oid);
|
||||
|
||||
if (parse_tag(the_repository, tag))
|
||||
if (parse_tag(options->repo, tag))
|
||||
return -1;
|
||||
if (name)
|
||||
fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
|
||||
@@ -487,7 +485,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
|
||||
return -1;
|
||||
|
||||
if (obj->type == OBJ_NONE)
|
||||
parse_object(the_repository, &obj->oid);
|
||||
parse_object(options->repo, &obj->oid);
|
||||
|
||||
switch (obj->type) {
|
||||
case OBJ_BLOB:
|
||||
@@ -970,14 +968,14 @@ static int fsck_commit(const struct object_id *oid,
|
||||
|
||||
if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
|
||||
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
|
||||
if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
|
||||
if (parse_oid_hex_algop(buffer, &tree_oid, &p, options->repo->hash_algo) || *p != '\n') {
|
||||
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
buffer = p + 1;
|
||||
while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
|
||||
if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
|
||||
if (parse_oid_hex_algop(buffer, &parent_oid, &p, options->repo->hash_algo) || *p != '\n') {
|
||||
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
|
||||
if (err)
|
||||
return err;
|
||||
@@ -1044,7 +1042,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
|
||||
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
|
||||
goto done;
|
||||
}
|
||||
if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') {
|
||||
if (parse_oid_hex_algop(buffer, tagged_oid, &p, options->repo->hash_algo) || *p != '\n') {
|
||||
ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
|
||||
if (ret)
|
||||
goto done;
|
||||
@@ -1336,9 +1334,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
|
||||
if (oidset_contains(blobs_done, oid))
|
||||
continue;
|
||||
|
||||
buf = odb_read_object(the_repository->objects, oid, &type, &size);
|
||||
buf = odb_read_object(options->repo->objects, oid, &type, &size);
|
||||
if (!buf) {
|
||||
if (is_promisor_object(the_repository, oid))
|
||||
if (is_promisor_object(options->repo, oid))
|
||||
continue;
|
||||
ret |= report(options,
|
||||
oid, OBJ_BLOB, msg_missing,
|
||||
@@ -1380,6 +1378,54 @@ bool fsck_has_queued_checks(struct fsck_options *options)
|
||||
!oidset_equal(&options->gitattributes_found, &options->gitattributes_done);
|
||||
}
|
||||
|
||||
void fsck_options_init(struct fsck_options *options,
|
||||
struct repository *repo,
|
||||
enum fsck_options_type type)
|
||||
{
|
||||
static const struct fsck_options defaults[] = {
|
||||
[FSCK_OPTIONS_DEFAULT] = {
|
||||
.skip_oids = OIDSET_INIT,
|
||||
.gitmodules_found = OIDSET_INIT,
|
||||
.gitmodules_done = OIDSET_INIT,
|
||||
.gitattributes_found = OIDSET_INIT,
|
||||
.gitattributes_done = OIDSET_INIT,
|
||||
.error_func = fsck_objects_error_function
|
||||
},
|
||||
[FSCK_OPTIONS_STRICT] = {
|
||||
.strict = 1,
|
||||
.gitmodules_found = OIDSET_INIT,
|
||||
.gitmodules_done = OIDSET_INIT,
|
||||
.gitattributes_found = OIDSET_INIT,
|
||||
.gitattributes_done = OIDSET_INIT,
|
||||
.error_func = fsck_objects_error_function,
|
||||
},
|
||||
[FSCK_OPTIONS_MISSING_GITMODULES] = {
|
||||
.strict = 1,
|
||||
.gitmodules_found = OIDSET_INIT,
|
||||
.gitmodules_done = OIDSET_INIT,
|
||||
.gitattributes_found = OIDSET_INIT,
|
||||
.gitattributes_done = OIDSET_INIT,
|
||||
.error_func = fsck_objects_error_cb_print_missing_gitmodules,
|
||||
},
|
||||
[FSCK_OPTIONS_REFS] = {
|
||||
.error_func = fsck_refs_error_function,
|
||||
},
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case FSCK_OPTIONS_DEFAULT:
|
||||
case FSCK_OPTIONS_STRICT:
|
||||
case FSCK_OPTIONS_MISSING_GITMODULES:
|
||||
case FSCK_OPTIONS_REFS:
|
||||
memcpy(options, &defaults[type], sizeof(*options));
|
||||
break;
|
||||
default:
|
||||
BUG("unknown fsck options type %d", type);
|
||||
}
|
||||
|
||||
options->repo = repo;
|
||||
}
|
||||
|
||||
void fsck_options_clear(struct fsck_options *options)
|
||||
{
|
||||
free(options->msg_type);
|
||||
|
||||
@@ -166,7 +166,10 @@ struct fsck_ref_report {
|
||||
const char *path;
|
||||
};
|
||||
|
||||
struct repository;
|
||||
|
||||
struct fsck_options {
|
||||
struct repository *repo;
|
||||
fsck_walk_func walk;
|
||||
fsck_error error_func;
|
||||
unsigned strict;
|
||||
@@ -180,34 +183,6 @@ struct fsck_options {
|
||||
kh_oid_map_t *object_names;
|
||||
};
|
||||
|
||||
#define FSCK_OPTIONS_DEFAULT { \
|
||||
.skip_oids = OIDSET_INIT, \
|
||||
.gitmodules_found = OIDSET_INIT, \
|
||||
.gitmodules_done = OIDSET_INIT, \
|
||||
.gitattributes_found = OIDSET_INIT, \
|
||||
.gitattributes_done = OIDSET_INIT, \
|
||||
.error_func = fsck_objects_error_function \
|
||||
}
|
||||
#define FSCK_OPTIONS_STRICT { \
|
||||
.strict = 1, \
|
||||
.gitmodules_found = OIDSET_INIT, \
|
||||
.gitmodules_done = OIDSET_INIT, \
|
||||
.gitattributes_found = OIDSET_INIT, \
|
||||
.gitattributes_done = OIDSET_INIT, \
|
||||
.error_func = fsck_objects_error_function, \
|
||||
}
|
||||
#define FSCK_OPTIONS_MISSING_GITMODULES { \
|
||||
.strict = 1, \
|
||||
.gitmodules_found = OIDSET_INIT, \
|
||||
.gitmodules_done = OIDSET_INIT, \
|
||||
.gitattributes_found = OIDSET_INIT, \
|
||||
.gitattributes_done = OIDSET_INIT, \
|
||||
.error_func = fsck_objects_error_cb_print_missing_gitmodules, \
|
||||
}
|
||||
#define FSCK_REFS_OPTIONS_DEFAULT { \
|
||||
.error_func = fsck_refs_error_function, \
|
||||
}
|
||||
|
||||
/* descend in all linked child objects
|
||||
* the return value is:
|
||||
* -1 error in processing the object
|
||||
@@ -255,6 +230,17 @@ int fsck_finish(struct fsck_options *options);
|
||||
*/
|
||||
bool fsck_has_queued_checks(struct fsck_options *options);
|
||||
|
||||
enum fsck_options_type {
|
||||
FSCK_OPTIONS_DEFAULT,
|
||||
FSCK_OPTIONS_STRICT,
|
||||
FSCK_OPTIONS_MISSING_GITMODULES,
|
||||
FSCK_OPTIONS_REFS,
|
||||
};
|
||||
|
||||
void fsck_options_init(struct fsck_options *options,
|
||||
struct repository *repo,
|
||||
enum fsck_options_type type);
|
||||
|
||||
/*
|
||||
* Clear the fsck_options struct, freeing any allocated memory.
|
||||
*/
|
||||
|
||||
@@ -1164,6 +1164,8 @@ int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid)
|
||||
*mode = SIGN_WARN_STRIP;
|
||||
} else if (!strcmp(arg, "strip")) {
|
||||
*mode = SIGN_STRIP;
|
||||
} else if (!strcmp(arg, "abort-if-invalid")) {
|
||||
*mode = SIGN_ABORT_IF_INVALID;
|
||||
} else if (!strcmp(arg, "strip-if-invalid")) {
|
||||
*mode = SIGN_STRIP_IF_INVALID;
|
||||
} else if (!strcmp(arg, "sign-if-invalid")) {
|
||||
|
||||
@@ -115,6 +115,7 @@ void print_signature_buffer(const struct signature_check *sigc,
|
||||
/* Modes for --signed-tags=<mode> and --signed-commits=<mode> options. */
|
||||
enum sign_mode {
|
||||
SIGN_ABORT,
|
||||
SIGN_ABORT_IF_INVALID,
|
||||
SIGN_WARN_VERBATIM,
|
||||
SIGN_VERBATIM,
|
||||
SIGN_WARN_STRIP,
|
||||
|
||||
@@ -317,3 +317,21 @@ const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop)
|
||||
/* Otherwise use the default one. */
|
||||
return algop;
|
||||
}
|
||||
|
||||
unsigned oid_common_prefix_hexlen(const struct object_id *a,
|
||||
const struct object_id *b)
|
||||
{
|
||||
unsigned rawsz = hash_algos[a->algo].rawsz;
|
||||
|
||||
for (unsigned i = 0; i < rawsz; i++) {
|
||||
if (a->hash[i] == b->hash[i])
|
||||
continue;
|
||||
|
||||
if ((a->hash[i] ^ b->hash[i]) & 0xf0)
|
||||
return i * 2;
|
||||
else
|
||||
return i * 2 + 1;
|
||||
}
|
||||
|
||||
return rawsz * 2;
|
||||
}
|
||||
|
||||
@@ -396,6 +396,9 @@ static inline int oideq(const struct object_id *oid1, const struct object_id *oi
|
||||
return !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);
|
||||
}
|
||||
|
||||
unsigned oid_common_prefix_hexlen(const struct object_id *a,
|
||||
const struct object_id *b);
|
||||
|
||||
static inline void oidcpy(struct object_id *dst, const struct object_id *src)
|
||||
{
|
||||
memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
|
||||
|
||||
+4
-4
@@ -1449,7 +1449,7 @@ static void one_remote_ref(const char *refname)
|
||||
*/
|
||||
if (repo->can_update_info_refs &&
|
||||
!odb_has_object(the_repository->objects, &ref->old_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
obj = lookup_unknown_object(the_repository, &ref->old_oid);
|
||||
fprintf(stderr, " fetch %s for %s\n",
|
||||
oid_to_hex(&ref->old_oid), refname);
|
||||
@@ -1655,7 +1655,7 @@ static int delete_remote_branch(const char *pattern, int force)
|
||||
if (is_null_oid(&head_oid))
|
||||
return error("Unable to resolve remote HEAD");
|
||||
if (!odb_has_object(the_repository->objects, &head_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
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 */
|
||||
@@ -1663,7 +1663,7 @@ static int delete_remote_branch(const char *pattern, int force)
|
||||
return error("Unable to resolve remote branch %s",
|
||||
remote_ref->name);
|
||||
if (!odb_has_object(the_repository->objects, &remote_ref->old_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
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 */
|
||||
@@ -1886,7 +1886,7 @@ int cmd_main(int argc, const char **argv)
|
||||
!is_null_oid(&ref->old_oid) &&
|
||||
!ref->force) {
|
||||
if (!odb_has_object(the_repository->objects, &ref->old_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) ||
|
||||
!ref_newer(&ref->peer_ref->new_oid,
|
||||
&ref->old_oid)) {
|
||||
/*
|
||||
|
||||
+2
-2
@@ -139,7 +139,7 @@ static int fill_active_slot(void *data UNUSED)
|
||||
obj_req = list_entry(pos, struct object_request, node);
|
||||
if (obj_req->state == WAITING) {
|
||||
if (odb_has_object(the_repository->objects, &obj_req->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
obj_req->state = COMPLETE;
|
||||
else {
|
||||
start_object_request(obj_req);
|
||||
@@ -495,7 +495,7 @@ static int fetch_object(struct walker *walker, const struct object_id *oid)
|
||||
return error("Couldn't find request for %s in the queue", hex);
|
||||
|
||||
if (odb_has_object(the_repository->objects, &obj_req->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
if (obj_req->req)
|
||||
abort_http_object_request(&obj_req->req);
|
||||
abort_object_request(obj_req);
|
||||
|
||||
+36
-162
@@ -858,173 +858,33 @@ static void queue_diffs(struct line_log_data *range,
|
||||
diff_queue_clear(&diff_queued_diff);
|
||||
diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
|
||||
if (opt->detect_rename && diff_might_be_rename()) {
|
||||
/* must look at the full tree diff to detect renames */
|
||||
clear_pathspec(&opt->pathspec);
|
||||
diff_queue_clear(&diff_queued_diff);
|
||||
struct diff_options rename_opts;
|
||||
|
||||
diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
|
||||
/*
|
||||
* Build a private diff_options for rename detection so
|
||||
* that any user-specified options on the original opts
|
||||
* (e.g. pickaxe) cannot discard diff pairs needed for
|
||||
* rename tracking. Similar to blame's find_rename().
|
||||
*/
|
||||
repo_diff_setup(opt->repo, &rename_opts);
|
||||
rename_opts.flags.recursive = 1;
|
||||
rename_opts.detect_rename = opt->detect_rename;
|
||||
rename_opts.rename_score = opt->rename_score;
|
||||
rename_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_setup_done(&rename_opts);
|
||||
|
||||
/* must look at the full tree diff to detect renames */
|
||||
diff_queue_clear(&diff_queued_diff);
|
||||
diff_tree_oid(parent_tree_oid, tree_oid, "", &rename_opts);
|
||||
|
||||
filter_diffs_for_paths(range, 1);
|
||||
diffcore_std(opt);
|
||||
diffcore_std(&rename_opts);
|
||||
filter_diffs_for_paths(range, 0);
|
||||
diff_free(&rename_opts);
|
||||
}
|
||||
move_diff_queue(queue, &diff_queued_diff);
|
||||
}
|
||||
|
||||
static char *get_nth_line(long line, unsigned long *ends, void *data)
|
||||
{
|
||||
if (line == 0)
|
||||
return (char *)data;
|
||||
else
|
||||
return (char *)data + ends[line] + 1;
|
||||
}
|
||||
|
||||
static void print_line(const char *prefix, char first,
|
||||
long line, unsigned long *ends, void *data,
|
||||
const char *color, const char *reset, FILE *file)
|
||||
{
|
||||
char *begin = get_nth_line(line, ends, data);
|
||||
char *end = get_nth_line(line+1, ends, data);
|
||||
int had_nl = 0;
|
||||
|
||||
if (end > begin && end[-1] == '\n') {
|
||||
end--;
|
||||
had_nl = 1;
|
||||
}
|
||||
|
||||
fputs(prefix, file);
|
||||
fputs(color, file);
|
||||
putc(first, file);
|
||||
fwrite(begin, 1, end-begin, file);
|
||||
fputs(reset, file);
|
||||
putc('\n', file);
|
||||
if (!had_nl)
|
||||
fputs("\\ No newline at end of file\n", file);
|
||||
}
|
||||
|
||||
static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
|
||||
{
|
||||
unsigned int i, j = 0;
|
||||
long p_lines, t_lines;
|
||||
unsigned long *p_ends = NULL, *t_ends = NULL;
|
||||
struct diff_filepair *pair = range->pair;
|
||||
struct diff_ranges *diff = &range->diff;
|
||||
|
||||
struct diff_options *opt = &rev->diffopt;
|
||||
const char *prefix = diff_line_prefix(opt);
|
||||
const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
|
||||
const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
|
||||
const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
|
||||
const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
|
||||
const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
|
||||
const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
|
||||
|
||||
if (!pair || !diff)
|
||||
goto out;
|
||||
|
||||
if (pair->one->oid_valid)
|
||||
fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
|
||||
fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends);
|
||||
|
||||
fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
|
||||
fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
|
||||
pair->one->oid_valid ? "a/" : "",
|
||||
pair->one->oid_valid ? pair->one->path : "/dev/null",
|
||||
c_reset);
|
||||
fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
|
||||
for (i = 0; i < range->ranges.nr; i++) {
|
||||
long p_start, p_end;
|
||||
long t_start = range->ranges.ranges[i].start;
|
||||
long t_end = range->ranges.ranges[i].end;
|
||||
long t_cur = t_start;
|
||||
unsigned int j_last;
|
||||
|
||||
/*
|
||||
* If a diff range touches multiple line ranges, then all
|
||||
* those line ranges should be shown, so take a step back if
|
||||
* the current line range is still in the previous diff range
|
||||
* (even if only partially).
|
||||
*/
|
||||
if (j > 0 && diff->target.ranges[j-1].end > t_start)
|
||||
j--;
|
||||
|
||||
while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
|
||||
j++;
|
||||
if (j == diff->target.nr || diff->target.ranges[j].start >= t_end)
|
||||
continue;
|
||||
|
||||
/* Scan ahead to determine the last diff that falls in this range */
|
||||
j_last = j;
|
||||
while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end)
|
||||
j_last++;
|
||||
if (j_last > j)
|
||||
j_last--;
|
||||
|
||||
/*
|
||||
* Compute parent hunk headers: we know that the diff
|
||||
* has the correct line numbers (but not all hunks).
|
||||
* So it suffices to shift the start/end according to
|
||||
* the line numbers of the first/last hunk(s) that
|
||||
* fall in this range.
|
||||
*/
|
||||
if (t_start < diff->target.ranges[j].start)
|
||||
p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start);
|
||||
else
|
||||
p_start = diff->parent.ranges[j].start;
|
||||
if (t_end > diff->target.ranges[j_last].end)
|
||||
p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end);
|
||||
else
|
||||
p_end = diff->parent.ranges[j_last].end;
|
||||
|
||||
if (!p_start && !p_end) {
|
||||
p_start = -1;
|
||||
p_end = -1;
|
||||
}
|
||||
|
||||
/* Now output a diff hunk for this range */
|
||||
fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
|
||||
prefix, c_frag,
|
||||
p_start+1, p_end-p_start, t_start+1, t_end-t_start,
|
||||
c_reset);
|
||||
while (j < diff->target.nr && diff->target.ranges[j].start < t_end) {
|
||||
int k;
|
||||
for (; t_cur < diff->target.ranges[j].start; t_cur++)
|
||||
print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
|
||||
c_context, c_reset, opt->file);
|
||||
for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
|
||||
print_line(prefix, '-', k, p_ends, pair->one->data,
|
||||
c_old, c_reset, opt->file);
|
||||
for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
|
||||
print_line(prefix, '+', t_cur, t_ends, pair->two->data,
|
||||
c_new, c_reset, opt->file);
|
||||
j++;
|
||||
}
|
||||
for (; t_cur < t_end; t_cur++)
|
||||
print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
|
||||
c_context, c_reset, opt->file);
|
||||
}
|
||||
|
||||
out:
|
||||
free(p_ends);
|
||||
free(t_ends);
|
||||
}
|
||||
|
||||
/*
|
||||
* NEEDSWORK: manually building a diff here is not the Right
|
||||
* Thing(tm). log -L should be built into the diff pipeline.
|
||||
*/
|
||||
static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
|
||||
{
|
||||
const char *prefix = diff_line_prefix(&rev->diffopt);
|
||||
|
||||
fprintf(rev->diffopt.file, "%s\n", prefix);
|
||||
|
||||
while (range) {
|
||||
dump_diff_hacky_one(rev, range);
|
||||
range = range->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike most other functions, this destructively operates on
|
||||
* 'range'.
|
||||
@@ -1088,7 +948,7 @@ static int process_diff_filepair(struct rev_info *rev,
|
||||
|
||||
static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
|
||||
{
|
||||
struct diff_filepair *new_filepair = xmalloc(sizeof(struct diff_filepair));
|
||||
struct diff_filepair *new_filepair = xcalloc(1, sizeof(struct diff_filepair));
|
||||
new_filepair->one = pair->one;
|
||||
new_filepair->two = pair->two;
|
||||
new_filepair->one->count++;
|
||||
@@ -1146,11 +1006,25 @@ static int process_all_files(struct line_log_data **range_out,
|
||||
|
||||
int line_log_print(struct rev_info *rev, struct commit *commit)
|
||||
{
|
||||
|
||||
show_log(rev);
|
||||
if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) {
|
||||
struct line_log_data *range = lookup_line_range(rev, commit);
|
||||
dump_diff_hacky(rev, range);
|
||||
struct line_log_data *r;
|
||||
const char *prefix = diff_line_prefix(&rev->diffopt);
|
||||
|
||||
fprintf(rev->diffopt.file, "%s\n", prefix);
|
||||
|
||||
for (r = range; r; r = r->next) {
|
||||
if (r->pair) {
|
||||
struct diff_filepair *p =
|
||||
diff_filepair_dup(r->pair);
|
||||
p->line_ranges = &r->ranges;
|
||||
diff_q(&diff_queued_diff, p);
|
||||
}
|
||||
}
|
||||
|
||||
diffcore_std(&rev->diffopt);
|
||||
diff_flush(&rev->diffopt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
+2
-12
@@ -1,22 +1,12 @@
|
||||
#ifndef LINE_LOG_H
|
||||
#define LINE_LOG_H
|
||||
|
||||
#include "diffcore.h" /* struct range, struct range_set */
|
||||
|
||||
struct rev_info;
|
||||
struct commit;
|
||||
struct string_list;
|
||||
|
||||
/* A range [start,end]. Lines are numbered starting at 0, and the
|
||||
* ranges include start but exclude end. */
|
||||
struct range {
|
||||
long start, end;
|
||||
};
|
||||
|
||||
/* A set of ranges. The ranges must always be disjoint and sorted. */
|
||||
struct range_set {
|
||||
unsigned int alloc, nr;
|
||||
struct range *ranges;
|
||||
};
|
||||
|
||||
/* A diff, encoded as the set of pre- and post-image ranges where the
|
||||
* files differ. A pair of ranges corresponds to a hunk. */
|
||||
struct diff_ranges {
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ static void process_blob(struct traversal_context *ctx,
|
||||
*/
|
||||
if (ctx->revs->exclude_promisor_objects &&
|
||||
!odb_has_object(the_repository->objects, &obj->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) &&
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) &&
|
||||
is_promisor_object(ctx->revs->repo, &obj->oid))
|
||||
return;
|
||||
|
||||
|
||||
@@ -796,7 +796,7 @@ static int prune_notes_helper(const struct object_id *object_oid,
|
||||
struct note_delete_list *n;
|
||||
|
||||
if (odb_has_object(the_repository->objects, object_oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
return 0; /* nothing to do for this note */
|
||||
|
||||
/* failed to find object => prune this note */
|
||||
|
||||
+121
-22
@@ -33,6 +33,9 @@
|
||||
/* The maximum size for an object header. */
|
||||
#define MAX_HEADER_LEN 32
|
||||
|
||||
static struct oidtree *odb_source_loose_cache(struct odb_source *source,
|
||||
const struct object_id *oid);
|
||||
|
||||
static int get_conv_flags(unsigned flags)
|
||||
{
|
||||
if (flags & INDEX_RENORMALIZE)
|
||||
@@ -906,7 +909,7 @@ static int start_loose_object_common(struct odb_source *source,
|
||||
|
||||
fd = create_tmpfile(source->odb->repo, tmp_file, filename);
|
||||
if (fd < 0) {
|
||||
if (flags & WRITE_OBJECT_SILENT)
|
||||
if (flags & ODB_WRITE_OBJECT_SILENT)
|
||||
return -1;
|
||||
else if (errno == EACCES)
|
||||
return error(_("insufficient permission for adding "
|
||||
@@ -1039,7 +1042,7 @@ static int write_loose_object(struct odb_source *source,
|
||||
utb.actime = mtime;
|
||||
utb.modtime = mtime;
|
||||
if (utime(tmp_file.buf, &utb) < 0 &&
|
||||
!(flags & WRITE_OBJECT_SILENT))
|
||||
!(flags & ODB_WRITE_OBJECT_SILENT))
|
||||
warning_errno(_("failed utime() on %s"), tmp_file.buf);
|
||||
}
|
||||
|
||||
@@ -1166,7 +1169,8 @@ cleanup:
|
||||
int odb_source_loose_write_object(struct odb_source *source,
|
||||
const void *buf, unsigned long len,
|
||||
enum object_type type, struct object_id *oid,
|
||||
struct object_id *compat_oid_in, unsigned flags)
|
||||
struct object_id *compat_oid_in,
|
||||
enum odb_write_object_flags flags)
|
||||
{
|
||||
const struct git_hash_algo *algo = source->odb->repo->hash_algo;
|
||||
const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
|
||||
@@ -1279,8 +1283,9 @@ static int index_mem(struct index_state *istate,
|
||||
}
|
||||
}
|
||||
if (flags & INDEX_FORMAT_CHECK) {
|
||||
struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
|
||||
struct fsck_options opts;
|
||||
|
||||
fsck_options_init(&opts, the_repository, FSCK_OPTIONS_DEFAULT);
|
||||
opts.strict = 1;
|
||||
opts.error_func = hash_format_check_report;
|
||||
if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))
|
||||
@@ -1374,7 +1379,7 @@ static int already_written(struct odb_transaction_files *transaction,
|
||||
{
|
||||
/* The object may already exist in the repository */
|
||||
if (odb_has_object(transaction->base.source->odb, oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
return 1;
|
||||
|
||||
/* Might want to keep the list sorted */
|
||||
@@ -1637,6 +1642,34 @@ static int index_blob_packfile_transaction(struct odb_transaction_files *transac
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hash_blob_stream(const struct git_hash_algo *hash_algo,
|
||||
struct object_id *result_oid, int fd, size_t size)
|
||||
{
|
||||
unsigned char buf[16384];
|
||||
struct git_hash_ctx ctx;
|
||||
unsigned header_len;
|
||||
|
||||
header_len = format_object_header((char *)buf, sizeof(buf),
|
||||
OBJ_BLOB, size);
|
||||
hash_algo->init_fn(&ctx);
|
||||
git_hash_update(&ctx, buf, header_len);
|
||||
|
||||
while (size) {
|
||||
size_t rsize = size < sizeof(buf) ? size : sizeof(buf);
|
||||
ssize_t read_result = read_in_full(fd, buf, rsize);
|
||||
|
||||
if ((read_result < 0) || ((size_t)read_result != rsize))
|
||||
return -1;
|
||||
|
||||
git_hash_update(&ctx, buf, rsize);
|
||||
size -= read_result;
|
||||
}
|
||||
|
||||
git_hash_final_oid(result_oid, &ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index_fd(struct index_state *istate, struct object_id *oid,
|
||||
int fd, struct stat *st,
|
||||
enum object_type type, const char *path, unsigned flags)
|
||||
@@ -1658,18 +1691,23 @@ int index_fd(struct index_state *istate, struct object_id *oid,
|
||||
ret = index_core(istate, oid, fd, xsize_t(st->st_size),
|
||||
type, path, flags);
|
||||
} else {
|
||||
struct object_database *odb = the_repository->objects;
|
||||
struct odb_transaction_files *files_transaction;
|
||||
struct odb_transaction *transaction;
|
||||
if (flags & INDEX_WRITE_OBJECT) {
|
||||
struct object_database *odb = the_repository->objects;
|
||||
struct odb_transaction_files *files_transaction;
|
||||
struct odb_transaction *transaction;
|
||||
|
||||
transaction = odb_transaction_begin(odb);
|
||||
files_transaction = container_of(odb->transaction,
|
||||
struct odb_transaction_files,
|
||||
base);
|
||||
ret = index_blob_packfile_transaction(files_transaction, oid, fd,
|
||||
xsize_t(st->st_size),
|
||||
path, flags);
|
||||
odb_transaction_commit(transaction);
|
||||
transaction = odb_transaction_begin(odb);
|
||||
files_transaction = container_of(odb->transaction,
|
||||
struct odb_transaction_files,
|
||||
base);
|
||||
ret = index_blob_packfile_transaction(files_transaction, oid, fd,
|
||||
xsize_t(st->st_size),
|
||||
path, flags);
|
||||
odb_transaction_commit(transaction);
|
||||
} else {
|
||||
ret = hash_blob_stream(the_repository->hash_algo, oid,
|
||||
fd, xsize_t(st->st_size));
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
@@ -1845,11 +1883,28 @@ static int for_each_object_wrapper_cb(const struct object_id *oid,
|
||||
}
|
||||
}
|
||||
|
||||
static int for_each_prefixed_object_wrapper_cb(const struct object_id *oid,
|
||||
void *cb_data)
|
||||
{
|
||||
struct for_each_object_wrapper_data *data = cb_data;
|
||||
if (data->request) {
|
||||
struct object_info oi = *data->request;
|
||||
|
||||
if (odb_source_loose_read_object_info(data->source,
|
||||
oid, &oi, 0) < 0)
|
||||
return -1;
|
||||
|
||||
return data->cb(oid, &oi, data->cb_data);
|
||||
} else {
|
||||
return data->cb(oid, NULL, data->cb_data);
|
||||
}
|
||||
}
|
||||
|
||||
int odb_source_loose_for_each_object(struct odb_source *source,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags)
|
||||
const struct odb_for_each_object_options *opts)
|
||||
{
|
||||
struct for_each_object_wrapper_data data = {
|
||||
.source = source,
|
||||
@@ -1859,11 +1914,16 @@ int odb_source_loose_for_each_object(struct odb_source *source,
|
||||
};
|
||||
|
||||
/* There are no loose promisor objects, so we can return immediately. */
|
||||
if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY))
|
||||
if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY))
|
||||
return 0;
|
||||
if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !source->local)
|
||||
if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !source->local)
|
||||
return 0;
|
||||
|
||||
if (opts->prefix)
|
||||
return oidtree_each(odb_source_loose_cache(source, opts->prefix),
|
||||
opts->prefix, opts->prefix_hex_len,
|
||||
for_each_prefixed_object_wrapper_cb, &data);
|
||||
|
||||
return for_each_loose_file_in_source(source, for_each_object_wrapper_cb,
|
||||
NULL, NULL, &data);
|
||||
}
|
||||
@@ -1914,9 +1974,10 @@ int odb_source_loose_count_objects(struct odb_source *source,
|
||||
*out = count * 256;
|
||||
ret = 0;
|
||||
} else {
|
||||
struct odb_for_each_object_options opts = { 0 };
|
||||
*out = 0;
|
||||
ret = odb_source_loose_for_each_object(source, NULL, count_loose_object,
|
||||
out, 0);
|
||||
out, &opts);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -1926,6 +1987,44 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct find_abbrev_len_data {
|
||||
const struct object_id *oid;
|
||||
unsigned len;
|
||||
};
|
||||
|
||||
static int find_abbrev_len_cb(const struct object_id *oid,
|
||||
struct object_info *oi UNUSED,
|
||||
void *cb_data)
|
||||
{
|
||||
struct find_abbrev_len_data *data = cb_data;
|
||||
unsigned len = oid_common_prefix_hexlen(oid, data->oid);
|
||||
if (len != hash_algos[oid->algo].hexsz && len >= data->len)
|
||||
data->len = len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int odb_source_loose_find_abbrev_len(struct odb_source *source,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out)
|
||||
{
|
||||
struct odb_for_each_object_options opts = {
|
||||
.prefix = oid,
|
||||
.prefix_hex_len = min_len,
|
||||
};
|
||||
struct find_abbrev_len_data data = {
|
||||
.oid = oid,
|
||||
.len = min_len,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = odb_source_loose_for_each_object(source, NULL, find_abbrev_len_cb,
|
||||
&data, &opts);
|
||||
*out = data.len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int append_loose_object(const struct object_id *oid,
|
||||
const char *path UNUSED,
|
||||
void *data)
|
||||
@@ -1934,8 +2033,8 @@ static int append_loose_object(const struct object_id *oid,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct oidtree *odb_source_loose_cache(struct odb_source *source,
|
||||
const struct object_id *oid)
|
||||
static struct oidtree *odb_source_loose_cache(struct odb_source *source,
|
||||
const struct object_id *oid)
|
||||
{
|
||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
||||
int subdir_nr = oid->hash[0];
|
||||
|
||||
+15
-9
@@ -68,19 +68,13 @@ int odb_source_loose_freshen_object(struct odb_source *source,
|
||||
int odb_source_loose_write_object(struct odb_source *source,
|
||||
const void *buf, unsigned long len,
|
||||
enum object_type type, struct object_id *oid,
|
||||
struct object_id *compat_oid_in, unsigned flags);
|
||||
struct object_id *compat_oid_in,
|
||||
enum odb_write_object_flags flags);
|
||||
|
||||
int odb_source_loose_write_stream(struct odb_source *source,
|
||||
struct odb_write_stream *stream, size_t len,
|
||||
struct object_id *oid);
|
||||
|
||||
/*
|
||||
* Populate and return the loose object cache array corresponding to the
|
||||
* given object ID.
|
||||
*/
|
||||
struct oidtree *odb_source_loose_cache(struct odb_source *source,
|
||||
const struct object_id *oid);
|
||||
|
||||
/*
|
||||
* Put in `buf` the name of the file in the local object database that
|
||||
* would be used to store a loose object with the specified oid.
|
||||
@@ -137,7 +131,7 @@ int odb_source_loose_for_each_object(struct odb_source *source,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags);
|
||||
const struct odb_for_each_object_options *opts);
|
||||
|
||||
/*
|
||||
* Count the number of loose objects in this source.
|
||||
@@ -153,6 +147,18 @@ int odb_source_loose_count_objects(struct odb_source *source,
|
||||
enum odb_count_objects_flags flags,
|
||||
unsigned long *out);
|
||||
|
||||
/*
|
||||
* Find the shortest unique prefix for the given object ID, where `min_len` is
|
||||
* the minimum length that the prefix should have.
|
||||
*
|
||||
* Returns 0 on success, in which case the computed length will be written to
|
||||
* `out`. Otherwise, a negative error code is returned.
|
||||
*/
|
||||
int odb_source_loose_find_abbrev_len(struct odb_source *source,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out);
|
||||
|
||||
/**
|
||||
* format_object_header() is a thin wrapper around s xsnprintf() that
|
||||
* writes the initial "<type> <obj-len>" part of the loose object
|
||||
|
||||
+74
-363
@@ -15,11 +15,9 @@
|
||||
#include "refs.h"
|
||||
#include "remote.h"
|
||||
#include "dir.h"
|
||||
#include "odb.h"
|
||||
#include "oid-array.h"
|
||||
#include "oidtree.h"
|
||||
#include "packfile.h"
|
||||
#include "pretty.h"
|
||||
#include "object-file.h"
|
||||
#include "read-cache-ll.h"
|
||||
#include "repo-settings.h"
|
||||
#include "repository.h"
|
||||
@@ -49,30 +47,29 @@ struct disambiguate_state {
|
||||
unsigned candidate_ok:1;
|
||||
unsigned disambiguate_fn_used:1;
|
||||
unsigned ambiguous:1;
|
||||
unsigned always_call_fn:1;
|
||||
};
|
||||
|
||||
static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
|
||||
static int update_disambiguate_state(const struct object_id *current,
|
||||
struct object_info *oi UNUSED,
|
||||
void *cb_data)
|
||||
{
|
||||
struct disambiguate_state *ds = cb_data;
|
||||
|
||||
/* The hash algorithm of current has already been filtered */
|
||||
if (ds->always_call_fn) {
|
||||
ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
|
||||
return;
|
||||
}
|
||||
if (!ds->candidate_exists) {
|
||||
/* this is the first candidate */
|
||||
oidcpy(&ds->candidate, current);
|
||||
ds->candidate_exists = 1;
|
||||
return;
|
||||
return 0;
|
||||
} else if (oideq(&ds->candidate, current)) {
|
||||
/* the same as what we already have seen */
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ds->fn) {
|
||||
/* cannot disambiguate between ds->candidate and current */
|
||||
ds->ambiguous = 1;
|
||||
return;
|
||||
return ds->ambiguous;
|
||||
}
|
||||
|
||||
if (!ds->candidate_checked) {
|
||||
@@ -85,7 +82,7 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
|
||||
/* discard the candidate; we know it does not satisfy fn */
|
||||
oidcpy(&ds->candidate, current);
|
||||
ds->candidate_checked = 0;
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if we reach this point, we know ds->candidate satisfies fn */
|
||||
@@ -96,128 +93,12 @@ static void update_candidates(struct disambiguate_state *ds, const struct object
|
||||
*/
|
||||
ds->candidate_ok = 0;
|
||||
ds->ambiguous = 1;
|
||||
return ds->ambiguous;
|
||||
}
|
||||
|
||||
/* otherwise, current can be discarded and candidate is still good */
|
||||
}
|
||||
|
||||
static int match_hash(unsigned, const unsigned char *, const unsigned char *);
|
||||
|
||||
static enum cb_next match_prefix(const struct object_id *oid, void *arg)
|
||||
{
|
||||
struct disambiguate_state *ds = arg;
|
||||
/* no need to call match_hash, oidtree_each did prefix match */
|
||||
update_candidates(ds, oid);
|
||||
return ds->ambiguous ? CB_BREAK : CB_CONTINUE;
|
||||
}
|
||||
|
||||
static void find_short_object_filename(struct disambiguate_state *ds)
|
||||
{
|
||||
struct odb_source *source;
|
||||
|
||||
for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next)
|
||||
oidtree_each(odb_source_loose_cache(source, &ds->bin_pfx),
|
||||
&ds->bin_pfx, ds->len, match_prefix, ds);
|
||||
}
|
||||
|
||||
static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
|
||||
{
|
||||
do {
|
||||
if (*a != *b)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
len -= 2;
|
||||
} while (len > 1);
|
||||
if (len)
|
||||
if ((*a ^ *b) & 0xf0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void unique_in_midx(struct multi_pack_index *m,
|
||||
struct disambiguate_state *ds)
|
||||
{
|
||||
for (; m; m = m->base_midx) {
|
||||
uint32_t num, i, first = 0;
|
||||
const struct object_id *current = NULL;
|
||||
int len = ds->len > ds->repo->hash_algo->hexsz ?
|
||||
ds->repo->hash_algo->hexsz : ds->len;
|
||||
|
||||
if (!m->num_objects)
|
||||
continue;
|
||||
|
||||
num = m->num_objects + m->num_objects_in_base;
|
||||
|
||||
bsearch_one_midx(&ds->bin_pfx, m, &first);
|
||||
|
||||
/*
|
||||
* At this point, "first" is the location of the lowest
|
||||
* object with an object name that could match
|
||||
* "bin_pfx". See if we have 0, 1 or more objects that
|
||||
* actually match(es).
|
||||
*/
|
||||
for (i = first; i < num && !ds->ambiguous; i++) {
|
||||
struct object_id oid;
|
||||
current = nth_midxed_object_oid(&oid, m, i);
|
||||
if (!match_hash(len, ds->bin_pfx.hash, current->hash))
|
||||
break;
|
||||
update_candidates(ds, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void unique_in_pack(struct packed_git *p,
|
||||
struct disambiguate_state *ds)
|
||||
{
|
||||
uint32_t num, i, first = 0;
|
||||
int len = ds->len > ds->repo->hash_algo->hexsz ?
|
||||
ds->repo->hash_algo->hexsz : ds->len;
|
||||
|
||||
if (p->multi_pack_index)
|
||||
return;
|
||||
|
||||
if (open_pack_index(p) || !p->num_objects)
|
||||
return;
|
||||
|
||||
num = p->num_objects;
|
||||
bsearch_pack(&ds->bin_pfx, p, &first);
|
||||
|
||||
/*
|
||||
* At this point, "first" is the location of the lowest object
|
||||
* with an object name that could match "bin_pfx". See if we have
|
||||
* 0, 1 or more objects that actually match(es).
|
||||
*/
|
||||
for (i = first; i < num && !ds->ambiguous; i++) {
|
||||
struct object_id oid;
|
||||
nth_packed_object_id(&oid, p, i);
|
||||
if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
|
||||
break;
|
||||
update_candidates(ds, &oid);
|
||||
}
|
||||
}
|
||||
|
||||
static void find_short_packed_object(struct disambiguate_state *ds)
|
||||
{
|
||||
struct odb_source *source;
|
||||
struct packed_git *p;
|
||||
|
||||
/* Skip, unless oids from the storage hash algorithm are wanted */
|
||||
if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo))
|
||||
return;
|
||||
|
||||
odb_prepare_alternates(ds->repo->objects);
|
||||
for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next) {
|
||||
struct multi_pack_index *m = get_multi_pack_index(source);
|
||||
if (m)
|
||||
unique_in_midx(m, ds);
|
||||
}
|
||||
|
||||
repo_for_each_pack(ds->repo, p) {
|
||||
if (ds->ambiguous)
|
||||
break;
|
||||
unique_in_pack(p, ds);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finish_object_disambiguation(struct disambiguate_state *ds,
|
||||
@@ -348,41 +229,57 @@ int set_disambiguate_hint_config(const char *var, const char *value)
|
||||
return error("unknown hint type for '%s': %s", var, value);
|
||||
}
|
||||
|
||||
static int parse_oid_prefix(const char *name, int len,
|
||||
const struct git_hash_algo *algo,
|
||||
char *hex_out,
|
||||
struct object_id *oid_out)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char c = name[i];
|
||||
unsigned char val;
|
||||
if (c >= '0' && c <= '9') {
|
||||
val = c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
val = c - 'a' + 10;
|
||||
} else if (c >= 'A' && c <='F') {
|
||||
val = c - 'A' + 10;
|
||||
c -= 'A' - 'a';
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hex_out)
|
||||
hex_out[i] = c;
|
||||
if (oid_out) {
|
||||
if (!(i & 1))
|
||||
val <<= 4;
|
||||
oid_out->hash[i >> 1] |= val;
|
||||
}
|
||||
}
|
||||
|
||||
if (hex_out)
|
||||
hex_out[len] = '\0';
|
||||
if (oid_out)
|
||||
oid_out->algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_object_disambiguation(struct repository *r,
|
||||
const char *name, int len,
|
||||
const struct git_hash_algo *algo,
|
||||
struct disambiguate_state *ds)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
|
||||
return -1;
|
||||
|
||||
memset(ds, 0, sizeof(*ds));
|
||||
|
||||
for (i = 0; i < len ;i++) {
|
||||
unsigned char c = name[i];
|
||||
unsigned char val;
|
||||
if (c >= '0' && c <= '9')
|
||||
val = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
val = c - 'a' + 10;
|
||||
else if (c >= 'A' && c <='F') {
|
||||
val = c - 'A' + 10;
|
||||
c -= 'A' - 'a';
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
ds->hex_pfx[i] = c;
|
||||
if (!(i & 1))
|
||||
val <<= 4;
|
||||
ds->bin_pfx.hash[i >> 1] |= val;
|
||||
}
|
||||
if (parse_oid_prefix(name, len, algo, ds->hex_pfx, &ds->bin_pfx) < 0)
|
||||
return -1;
|
||||
|
||||
ds->len = len;
|
||||
ds->hex_pfx[len] = '\0';
|
||||
ds->repo = r;
|
||||
ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
|
||||
odb_prepare_alternates(r->objects);
|
||||
return 0;
|
||||
}
|
||||
@@ -510,8 +407,8 @@ static int collect_ambiguous(const struct object_id *oid, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int repo_collect_ambiguous(struct repository *r UNUSED,
|
||||
const struct object_id *oid,
|
||||
static int repo_collect_ambiguous(const struct object_id *oid,
|
||||
struct object_info *oi UNUSED,
|
||||
void *data)
|
||||
{
|
||||
return collect_ambiguous(oid, data);
|
||||
@@ -561,6 +458,7 @@ static enum get_oid_result get_short_oid(struct repository *r,
|
||||
struct object_id *oid,
|
||||
unsigned flags)
|
||||
{
|
||||
struct odb_for_each_object_options opts = { 0 };
|
||||
int status;
|
||||
struct disambiguate_state ds;
|
||||
int quietly = !!(flags & GET_OID_QUIETLY);
|
||||
@@ -588,8 +486,11 @@ static enum get_oid_result get_short_oid(struct repository *r,
|
||||
else
|
||||
ds.fn = default_disambiguate_hint;
|
||||
|
||||
find_short_object_filename(&ds);
|
||||
find_short_packed_object(&ds);
|
||||
opts.prefix = &ds.bin_pfx;
|
||||
opts.prefix_hex_len = ds.len;
|
||||
|
||||
odb_for_each_object_ext(r->objects, NULL, update_disambiguate_state,
|
||||
&ds, &opts);
|
||||
status = finish_object_disambiguation(&ds, oid);
|
||||
|
||||
/*
|
||||
@@ -599,8 +500,8 @@ static enum get_oid_result get_short_oid(struct repository *r,
|
||||
*/
|
||||
if (status == MISSING_OBJECT) {
|
||||
odb_reprepare(r->objects);
|
||||
find_short_object_filename(&ds);
|
||||
find_short_packed_object(&ds);
|
||||
odb_for_each_object_ext(r->objects, NULL, update_disambiguate_state,
|
||||
&ds, &opts);
|
||||
status = finish_object_disambiguation(&ds, oid);
|
||||
}
|
||||
|
||||
@@ -648,169 +549,25 @@ int repo_for_each_abbrev(struct repository *r, const char *prefix,
|
||||
const struct git_hash_algo *algo,
|
||||
each_abbrev_fn fn, void *cb_data)
|
||||
{
|
||||
struct object_id prefix_oid = { 0 };
|
||||
struct odb_for_each_object_options opts = {
|
||||
.prefix = &prefix_oid,
|
||||
.prefix_hex_len = strlen(prefix),
|
||||
};
|
||||
struct oid_array collect = OID_ARRAY_INIT;
|
||||
struct disambiguate_state ds;
|
||||
int ret;
|
||||
|
||||
if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
|
||||
if (parse_oid_prefix(prefix, opts.prefix_hex_len, algo, NULL, &prefix_oid) < 0)
|
||||
return -1;
|
||||
|
||||
ds.always_call_fn = 1;
|
||||
ds.fn = repo_collect_ambiguous;
|
||||
ds.cb_data = &collect;
|
||||
find_short_object_filename(&ds);
|
||||
find_short_packed_object(&ds);
|
||||
if (odb_for_each_object_ext(r->objects, NULL, repo_collect_ambiguous, &collect, &opts) < 0)
|
||||
return -1;
|
||||
|
||||
ret = oid_array_for_each_unique(&collect, fn, cb_data);
|
||||
oid_array_clear(&collect);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the slot of the most-significant bit set in "val". There are various
|
||||
* ways to do this quickly with fls() or __builtin_clzl(), but speed is
|
||||
* probably not a big deal here.
|
||||
*/
|
||||
static unsigned msb(unsigned long val)
|
||||
{
|
||||
unsigned r = 0;
|
||||
while (val >>= 1)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
struct min_abbrev_data {
|
||||
unsigned int init_len;
|
||||
unsigned int cur_len;
|
||||
char *hex;
|
||||
struct repository *repo;
|
||||
const struct object_id *oid;
|
||||
};
|
||||
|
||||
static inline char get_hex_char_from_oid(const struct object_id *oid,
|
||||
unsigned int pos)
|
||||
{
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
|
||||
if ((pos & 1) == 0)
|
||||
return hex[oid->hash[pos >> 1] >> 4];
|
||||
else
|
||||
return hex[oid->hash[pos >> 1] & 0xf];
|
||||
}
|
||||
|
||||
static int extend_abbrev_len(const struct object_id *oid,
|
||||
struct min_abbrev_data *mad)
|
||||
{
|
||||
unsigned int i = mad->init_len;
|
||||
while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i))
|
||||
i++;
|
||||
|
||||
if (mad->hex[i] && i >= mad->cur_len)
|
||||
mad->cur_len = i + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int repo_extend_abbrev_len(struct repository *r UNUSED,
|
||||
const struct object_id *oid,
|
||||
void *cb_data)
|
||||
{
|
||||
return extend_abbrev_len(oid, cb_data);
|
||||
}
|
||||
|
||||
static void find_abbrev_len_for_midx(struct multi_pack_index *m,
|
||||
struct min_abbrev_data *mad)
|
||||
{
|
||||
for (; m; m = m->base_midx) {
|
||||
int match = 0;
|
||||
uint32_t num, first = 0;
|
||||
struct object_id oid;
|
||||
const struct object_id *mad_oid;
|
||||
|
||||
if (!m->num_objects)
|
||||
continue;
|
||||
|
||||
num = m->num_objects + m->num_objects_in_base;
|
||||
mad_oid = mad->oid;
|
||||
match = bsearch_one_midx(mad_oid, m, &first);
|
||||
|
||||
/*
|
||||
* first is now the position in the packfile where we
|
||||
* would insert mad->hash if it does not exist (or the
|
||||
* position of mad->hash if it does exist). Hence, we
|
||||
* consider a maximum of two objects nearby for the
|
||||
* abbreviation length.
|
||||
*/
|
||||
mad->init_len = 0;
|
||||
if (!match) {
|
||||
if (nth_midxed_object_oid(&oid, m, first))
|
||||
extend_abbrev_len(&oid, mad);
|
||||
} else if (first < num - 1) {
|
||||
if (nth_midxed_object_oid(&oid, m, first + 1))
|
||||
extend_abbrev_len(&oid, mad);
|
||||
}
|
||||
if (first > 0) {
|
||||
if (nth_midxed_object_oid(&oid, m, first - 1))
|
||||
extend_abbrev_len(&oid, mad);
|
||||
}
|
||||
mad->init_len = mad->cur_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void find_abbrev_len_for_pack(struct packed_git *p,
|
||||
struct min_abbrev_data *mad)
|
||||
{
|
||||
int match = 0;
|
||||
uint32_t num, first = 0;
|
||||
struct object_id oid;
|
||||
const struct object_id *mad_oid;
|
||||
|
||||
if (p->multi_pack_index)
|
||||
return;
|
||||
|
||||
if (open_pack_index(p) || !p->num_objects)
|
||||
return;
|
||||
|
||||
num = p->num_objects;
|
||||
mad_oid = mad->oid;
|
||||
match = bsearch_pack(mad_oid, p, &first);
|
||||
|
||||
/*
|
||||
* first is now the position in the packfile where we would insert
|
||||
* mad->hash if it does not exist (or the position of mad->hash if
|
||||
* it does exist). Hence, we consider a maximum of two objects
|
||||
* nearby for the abbreviation length.
|
||||
*/
|
||||
mad->init_len = 0;
|
||||
if (!match) {
|
||||
if (!nth_packed_object_id(&oid, p, first))
|
||||
extend_abbrev_len(&oid, mad);
|
||||
} else if (first < num - 1) {
|
||||
if (!nth_packed_object_id(&oid, p, first + 1))
|
||||
extend_abbrev_len(&oid, mad);
|
||||
}
|
||||
if (first > 0) {
|
||||
if (!nth_packed_object_id(&oid, p, first - 1))
|
||||
extend_abbrev_len(&oid, mad);
|
||||
}
|
||||
mad->init_len = mad->cur_len;
|
||||
}
|
||||
|
||||
static void find_abbrev_len_packed(struct min_abbrev_data *mad)
|
||||
{
|
||||
struct packed_git *p;
|
||||
|
||||
odb_prepare_alternates(mad->repo->objects);
|
||||
for (struct odb_source *source = mad->repo->objects->sources; source; source = source->next) {
|
||||
struct multi_pack_index *m = get_multi_pack_index(source);
|
||||
if (m)
|
||||
find_abbrev_len_for_midx(m, mad);
|
||||
}
|
||||
|
||||
repo_for_each_pack(mad->repo, p)
|
||||
find_abbrev_len_for_pack(p, mad);
|
||||
}
|
||||
|
||||
void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
|
||||
const struct object_id *oid, int abbrev_len)
|
||||
{
|
||||
@@ -827,65 +584,19 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
|
||||
}
|
||||
|
||||
int repo_find_unique_abbrev_r(struct repository *r, char *hex,
|
||||
const struct object_id *oid, int len)
|
||||
const struct object_id *oid, int min_len)
|
||||
{
|
||||
const struct git_hash_algo *algo =
|
||||
oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
|
||||
struct disambiguate_state ds;
|
||||
struct min_abbrev_data mad;
|
||||
struct object_id oid_ret;
|
||||
const unsigned hexsz = algo->hexsz;
|
||||
unsigned len;
|
||||
|
||||
if (len < 0) {
|
||||
unsigned long count;
|
||||
|
||||
if (odb_count_objects(r->objects, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0)
|
||||
count = 0;
|
||||
|
||||
/*
|
||||
* Add one because the MSB only tells us the highest bit set,
|
||||
* not including the value of all the _other_ bits (so "15"
|
||||
* is only one off of 2^4, but the MSB is the 3rd bit.
|
||||
*/
|
||||
len = msb(count) + 1;
|
||||
/*
|
||||
* We now know we have on the order of 2^len objects, which
|
||||
* expects a collision at 2^(len/2). But we also care about hex
|
||||
* chars, not bits, and there are 4 bits per hex. So all
|
||||
* together we need to divide by 2 and round up.
|
||||
*/
|
||||
len = DIV_ROUND_UP(len, 2);
|
||||
/*
|
||||
* For very small repos, we stick with our regular fallback.
|
||||
*/
|
||||
if (len < FALLBACK_DEFAULT_ABBREV)
|
||||
len = FALLBACK_DEFAULT_ABBREV;
|
||||
}
|
||||
if (odb_find_abbrev_len(r->objects, oid, min_len, &len) < 0)
|
||||
len = algo->hexsz;
|
||||
|
||||
oid_to_hex_r(hex, oid);
|
||||
if (len >= hexsz || !len)
|
||||
return hexsz;
|
||||
hex[len] = 0;
|
||||
|
||||
mad.repo = r;
|
||||
mad.init_len = len;
|
||||
mad.cur_len = len;
|
||||
mad.hex = hex;
|
||||
mad.oid = oid;
|
||||
|
||||
find_abbrev_len_packed(&mad);
|
||||
|
||||
if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
|
||||
return -1;
|
||||
|
||||
ds.fn = repo_extend_abbrev_len;
|
||||
ds.always_call_fn = 1;
|
||||
ds.cb_data = (void *)&mad;
|
||||
|
||||
find_short_object_filename(&ds);
|
||||
(void)finish_object_disambiguation(&ds, &oid_ret);
|
||||
|
||||
hex[mad.cur_len] = 0;
|
||||
return mad.cur_len;
|
||||
return len;
|
||||
}
|
||||
|
||||
const char *repo_find_unique_abbrev(struct repository *r,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "midx.h"
|
||||
#include "object-file-convert.h"
|
||||
#include "object-file.h"
|
||||
#include "object-name.h"
|
||||
#include "odb.h"
|
||||
#include "packfile.h"
|
||||
#include "path.h"
|
||||
@@ -871,15 +872,15 @@ void *odb_read_object_peeled(struct object_database *odb,
|
||||
}
|
||||
|
||||
int odb_has_object(struct object_database *odb, const struct object_id *oid,
|
||||
enum has_object_flags flags)
|
||||
enum odb_has_object_flags flags)
|
||||
{
|
||||
unsigned object_info_flags = 0;
|
||||
|
||||
if (!startup_info->have_repository)
|
||||
return 0;
|
||||
if (!(flags & HAS_OBJECT_RECHECK_PACKED))
|
||||
if (!(flags & ODB_HAS_OBJECT_RECHECK_PACKED))
|
||||
object_info_flags |= OBJECT_INFO_QUICK;
|
||||
if (!(flags & HAS_OBJECT_FETCH_PROMISOR))
|
||||
if (!(flags & ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
object_info_flags |= OBJECT_INFO_SKIP_FETCH_OBJECT;
|
||||
|
||||
return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0;
|
||||
@@ -896,20 +897,20 @@ int odb_freshen_object(struct object_database *odb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int odb_for_each_object(struct object_database *odb,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags)
|
||||
int odb_for_each_object_ext(struct object_database *odb,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
const struct odb_for_each_object_options *opts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
odb_prepare_alternates(odb);
|
||||
for (struct odb_source *source = odb->sources; source; source = source->next) {
|
||||
if (flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local)
|
||||
if (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local)
|
||||
continue;
|
||||
|
||||
ret = odb_source_for_each_object(source, request, cb, cb_data, flags);
|
||||
ret = odb_source_for_each_object(source, request, cb, cb_data, opts);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -917,6 +918,18 @@ int odb_for_each_object(struct object_database *odb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int odb_for_each_object(struct object_database *odb,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
enum odb_for_each_object_flags flags)
|
||||
{
|
||||
struct odb_for_each_object_options opts = {
|
||||
.flags = flags,
|
||||
};
|
||||
return odb_for_each_object_ext(odb, request, cb, cb_data, &opts);
|
||||
}
|
||||
|
||||
int odb_count_objects(struct object_database *odb,
|
||||
enum odb_count_objects_flags flags,
|
||||
unsigned long *out)
|
||||
@@ -952,6 +965,78 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the slot of the most-significant bit set in "val". There are various
|
||||
* ways to do this quickly with fls() or __builtin_clzl(), but speed is
|
||||
* probably not a big deal here.
|
||||
*/
|
||||
static unsigned msb(unsigned long val)
|
||||
{
|
||||
unsigned r = 0;
|
||||
while (val >>= 1)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
int odb_find_abbrev_len(struct object_database *odb,
|
||||
const struct object_id *oid,
|
||||
int min_length,
|
||||
unsigned *out)
|
||||
{
|
||||
const struct git_hash_algo *algo =
|
||||
oid->algo ? &hash_algos[oid->algo] : odb->repo->hash_algo;
|
||||
const unsigned hexsz = algo->hexsz;
|
||||
unsigned len;
|
||||
int ret;
|
||||
|
||||
if (min_length < 0) {
|
||||
unsigned long count;
|
||||
|
||||
if (odb_count_objects(odb, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0)
|
||||
count = 0;
|
||||
|
||||
/*
|
||||
* Add one because the MSB only tells us the highest bit set,
|
||||
* not including the value of all the _other_ bits (so "15"
|
||||
* is only one off of 2^4, but the MSB is the 3rd bit.
|
||||
*/
|
||||
len = msb(count) + 1;
|
||||
/*
|
||||
* We now know we have on the order of 2^len objects, which
|
||||
* expects a collision at 2^(len/2). But we also care about hex
|
||||
* chars, not bits, and there are 4 bits per hex. So all
|
||||
* together we need to divide by 2 and round up.
|
||||
*/
|
||||
len = DIV_ROUND_UP(len, 2);
|
||||
/*
|
||||
* For very small repos, we stick with our regular fallback.
|
||||
*/
|
||||
if (len < FALLBACK_DEFAULT_ABBREV)
|
||||
len = FALLBACK_DEFAULT_ABBREV;
|
||||
} else {
|
||||
len = min_length;
|
||||
}
|
||||
|
||||
if (len >= hexsz || !len) {
|
||||
*out = hexsz;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
odb_prepare_alternates(odb);
|
||||
for (struct odb_source *source = odb->sources; source; source = source->next) {
|
||||
ret = odb_source_find_abbrev_len(source, oid, len, &len);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
*out = len;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void odb_assert_oid_type(struct object_database *odb,
|
||||
const struct object_id *oid, enum object_type expect)
|
||||
{
|
||||
@@ -968,7 +1053,7 @@ int odb_write_object_ext(struct object_database *odb,
|
||||
enum object_type type,
|
||||
struct object_id *oid,
|
||||
struct object_id *compat_oid,
|
||||
unsigned flags)
|
||||
enum odb_write_object_flags flags)
|
||||
{
|
||||
return odb_source_write_object(odb->sources, buf, len, type,
|
||||
oid, compat_oid, flags);
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
#ifndef ODB_H
|
||||
#define ODB_H
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "object.h"
|
||||
#include "oidset.h"
|
||||
#include "oidmap.h"
|
||||
#include "string-list.h"
|
||||
#include "thread-utils.h"
|
||||
|
||||
struct oidmap;
|
||||
struct oidtree;
|
||||
struct cached_object_entry;
|
||||
struct packed_git;
|
||||
struct repository;
|
||||
struct strbuf;
|
||||
struct strvec;
|
||||
struct repository;
|
||||
struct multi_pack_index;
|
||||
|
||||
/*
|
||||
* Set this to 0 to prevent odb_read_object_info_extended() from fetching missing
|
||||
@@ -31,10 +29,6 @@ extern int fetch_if_missing;
|
||||
*/
|
||||
char *compute_alternate_path(const char *path, struct strbuf *err);
|
||||
|
||||
struct packed_git;
|
||||
struct packfile_store;
|
||||
struct cached_object_entry;
|
||||
|
||||
/*
|
||||
* A transaction may be started for an object database prior to writing new
|
||||
* objects via odb_transaction_begin(). These objects are not committed until
|
||||
@@ -395,11 +389,11 @@ int odb_read_object_info(struct object_database *odb,
|
||||
const struct object_id *oid,
|
||||
unsigned long *sizep);
|
||||
|
||||
enum has_object_flags {
|
||||
enum odb_has_object_flags {
|
||||
/* Retry packed storage after checking packed and loose storage */
|
||||
HAS_OBJECT_RECHECK_PACKED = (1 << 0),
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED = (1 << 0),
|
||||
/* Allow fetching the object in case the repository has a promisor remote. */
|
||||
HAS_OBJECT_FETCH_PROMISOR = (1 << 1),
|
||||
ODB_HAS_OBJECT_FETCH_PROMISOR = (1 << 1),
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -408,7 +402,7 @@ enum has_object_flags {
|
||||
*/
|
||||
int odb_has_object(struct object_database *odb,
|
||||
const struct object_id *oid,
|
||||
enum has_object_flags flags);
|
||||
enum odb_has_object_flags flags);
|
||||
|
||||
int odb_freshen_object(struct object_database *odb,
|
||||
const struct object_id *oid);
|
||||
@@ -481,6 +475,22 @@ typedef int (*odb_for_each_object_cb)(const struct object_id *oid,
|
||||
struct object_info *oi,
|
||||
void *cb_data);
|
||||
|
||||
/*
|
||||
* Options that can be passed to `odb_for_each_object()` and its
|
||||
* backend-specific implementations.
|
||||
*/
|
||||
struct odb_for_each_object_options {
|
||||
/* A bitfield of `odb_for_each_object_flags`. */
|
||||
enum odb_for_each_object_flags flags;
|
||||
|
||||
/*
|
||||
* If set, only iterate through objects whose first `prefix_hex_len`
|
||||
* hex characters matches the given prefix.
|
||||
*/
|
||||
const struct object_id *prefix;
|
||||
size_t prefix_hex_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Iterate through all objects contained in the object database. Note that
|
||||
* objects may be iterated over multiple times in case they are either stored
|
||||
@@ -495,11 +505,18 @@ typedef int (*odb_for_each_object_cb)(const struct object_id *oid,
|
||||
* Returns 0 on success, a negative error code in case a failure occurred, or
|
||||
* an arbitrary non-zero error code returned by the callback itself.
|
||||
*/
|
||||
int odb_for_each_object_ext(struct object_database *odb,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
const struct odb_for_each_object_options *opts);
|
||||
|
||||
/* Same as `odb_for_each_object_ext()` with `opts.flags` set to the given flags. */
|
||||
int odb_for_each_object(struct object_database *odb,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags);
|
||||
enum odb_for_each_object_flags flags);
|
||||
|
||||
enum odb_count_objects_flags {
|
||||
/*
|
||||
@@ -522,19 +539,35 @@ int odb_count_objects(struct object_database *odb,
|
||||
enum odb_count_objects_flags flags,
|
||||
unsigned long *out);
|
||||
|
||||
enum {
|
||||
/*
|
||||
* Given an object ID, find the minimum required length required to make the
|
||||
* object ID unique across the whole object database.
|
||||
*
|
||||
* The `min_len` determines the minimum abbreviated length that'll be returned
|
||||
* by this function. If `min_len < 0`, then the function will set a sensible
|
||||
* default minimum abbreviation length.
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise. The computed length
|
||||
* will be assigned to `*out`.
|
||||
*/
|
||||
int odb_find_abbrev_len(struct object_database *odb,
|
||||
const struct object_id *oid,
|
||||
int min_len,
|
||||
unsigned *out);
|
||||
|
||||
enum odb_write_object_flags {
|
||||
/*
|
||||
* By default, `odb_write_object()` does not actually write anything
|
||||
* into the object store, but only computes the object ID. This flag
|
||||
* changes that so that the object will be written as a loose object
|
||||
* and persisted.
|
||||
*/
|
||||
WRITE_OBJECT_PERSIST = (1 << 0),
|
||||
ODB_WRITE_OBJECT_PERSIST = (1 << 0),
|
||||
|
||||
/*
|
||||
* Do not print an error in case something goes wrong.
|
||||
*/
|
||||
WRITE_OBJECT_SILENT = (1 << 1),
|
||||
ODB_WRITE_OBJECT_SILENT = (1 << 1),
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -550,7 +583,7 @@ int odb_write_object_ext(struct object_database *odb,
|
||||
enum object_type type,
|
||||
struct object_id *oid,
|
||||
struct object_id *compat_oid,
|
||||
unsigned flags);
|
||||
enum odb_write_object_flags flags);
|
||||
|
||||
static inline int odb_write_object(struct object_database *odb,
|
||||
const void *buf, unsigned long len,
|
||||
|
||||
+30
-5
@@ -75,18 +75,18 @@ static int odb_source_files_for_each_object(struct odb_source *source,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags)
|
||||
const struct odb_for_each_object_options *opts)
|
||||
{
|
||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
||||
int ret;
|
||||
|
||||
if (!(flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) {
|
||||
ret = odb_source_loose_for_each_object(source, request, cb, cb_data, flags);
|
||||
if (!(opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) {
|
||||
ret = odb_source_loose_for_each_object(source, request, cb, cb_data, opts);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, flags);
|
||||
ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, opts);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -122,6 +122,30 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int odb_source_files_find_abbrev_len(struct odb_source *source,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out)
|
||||
{
|
||||
struct odb_source_files *files = odb_source_files_downcast(source);
|
||||
unsigned len = min_len;
|
||||
int ret;
|
||||
|
||||
ret = packfile_store_find_abbrev_len(files->packed, oid, len, &len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = odb_source_loose_find_abbrev_len(source, oid, len, &len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
*out = len;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int odb_source_files_freshen_object(struct odb_source *source,
|
||||
const struct object_id *oid)
|
||||
{
|
||||
@@ -137,7 +161,7 @@ static int odb_source_files_write_object(struct odb_source *source,
|
||||
enum object_type type,
|
||||
struct object_id *oid,
|
||||
struct object_id *compat_oid,
|
||||
unsigned flags)
|
||||
enum odb_write_object_flags flags)
|
||||
{
|
||||
return odb_source_loose_write_object(source, buf, len, type,
|
||||
oid, compat_oid, flags);
|
||||
@@ -250,6 +274,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb,
|
||||
files->base.read_object_stream = odb_source_files_read_object_stream;
|
||||
files->base.for_each_object = odb_source_files_for_each_object;
|
||||
files->base.count_objects = odb_source_files_count_objects;
|
||||
files->base.find_abbrev_len = odb_source_files_find_abbrev_len;
|
||||
files->base.freshen_object = odb_source_files_freshen_object;
|
||||
files->base.write_object = odb_source_files_write_object;
|
||||
files->base.write_object_stream = odb_source_files_write_object_stream;
|
||||
|
||||
+29
-5
@@ -140,7 +140,7 @@ struct odb_source {
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags);
|
||||
const struct odb_for_each_object_options *opts);
|
||||
|
||||
/*
|
||||
* This callback is expected to count objects in the given object
|
||||
@@ -157,6 +157,18 @@ struct odb_source {
|
||||
enum odb_count_objects_flags flags,
|
||||
unsigned long *out);
|
||||
|
||||
/*
|
||||
* This callback is expected to find the minimum required length to
|
||||
* make the given object ID unique.
|
||||
*
|
||||
* The callback is expected to return a negative error code in case it
|
||||
* failed, 0 otherwise.
|
||||
*/
|
||||
int (*find_abbrev_len)(struct odb_source *source,
|
||||
const struct object_id *oid,
|
||||
unsigned min_length,
|
||||
unsigned *out);
|
||||
|
||||
/*
|
||||
* This callback is expected to freshen the given object so that its
|
||||
* last access time is set to the current time. This is used to ensure
|
||||
@@ -185,7 +197,7 @@ struct odb_source {
|
||||
enum object_type type,
|
||||
struct object_id *oid,
|
||||
struct object_id *compat_oid,
|
||||
unsigned flags);
|
||||
enum odb_write_object_flags flags);
|
||||
|
||||
/*
|
||||
* This callback is expected to persist the given object stream into
|
||||
@@ -343,9 +355,9 @@ static inline int odb_source_for_each_object(struct odb_source *source,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags)
|
||||
const struct odb_for_each_object_options *opts)
|
||||
{
|
||||
return source->for_each_object(source, request, cb, cb_data, flags);
|
||||
return source->for_each_object(source, request, cb, cb_data, opts);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -360,6 +372,18 @@ static inline int odb_source_count_objects(struct odb_source *source,
|
||||
return source->count_objects(source, flags, out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the minimum required length to make the given object ID unique in
|
||||
* the given source. Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
static inline int odb_source_find_abbrev_len(struct odb_source *source,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out)
|
||||
{
|
||||
return source->find_abbrev_len(source, oid, min_len, out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Freshen an object in the object database by updating its timestamp.
|
||||
* Returns 1 in case the object has been freshened, 0 in case the object does
|
||||
@@ -381,7 +405,7 @@ static inline int odb_source_write_object(struct odb_source *source,
|
||||
enum object_type type,
|
||||
struct object_id *oid,
|
||||
struct object_id *compat_oid,
|
||||
unsigned flags)
|
||||
enum odb_write_object_flags flags)
|
||||
{
|
||||
return source->write_object(source, buf, len, type, oid,
|
||||
compat_oid, flags);
|
||||
|
||||
@@ -6,14 +6,6 @@
|
||||
#include "oidtree.h"
|
||||
#include "hash.h"
|
||||
|
||||
struct oidtree_iter_data {
|
||||
oidtree_iter fn;
|
||||
void *arg;
|
||||
size_t *last_nibble_at;
|
||||
uint32_t algo;
|
||||
uint8_t last_byte;
|
||||
};
|
||||
|
||||
void oidtree_init(struct oidtree *ot)
|
||||
{
|
||||
cb_init(&ot->tree);
|
||||
@@ -54,8 +46,7 @@ void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
|
||||
cb_insert(&ot->tree, on, sizeof(*oid));
|
||||
}
|
||||
|
||||
|
||||
int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
|
||||
bool oidtree_contains(struct oidtree *ot, const struct object_id *oid)
|
||||
{
|
||||
struct object_id k;
|
||||
size_t klen = sizeof(k);
|
||||
@@ -69,41 +60,51 @@ int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
|
||||
klen += BUILD_ASSERT_OR_ZERO(offsetof(struct object_id, hash) <
|
||||
offsetof(struct object_id, algo));
|
||||
|
||||
return cb_lookup(&ot->tree, (const uint8_t *)&k, klen) ? 1 : 0;
|
||||
return !!cb_lookup(&ot->tree, (const uint8_t *)&k, klen);
|
||||
}
|
||||
|
||||
static enum cb_next iter(struct cb_node *n, void *arg)
|
||||
struct oidtree_each_data {
|
||||
oidtree_each_cb cb;
|
||||
void *cb_data;
|
||||
size_t *last_nibble_at;
|
||||
uint32_t algo;
|
||||
uint8_t last_byte;
|
||||
};
|
||||
|
||||
static int iter(struct cb_node *n, void *cb_data)
|
||||
{
|
||||
struct oidtree_iter_data *x = arg;
|
||||
struct oidtree_each_data *data = cb_data;
|
||||
struct object_id k;
|
||||
|
||||
/* Copy to provide 4-byte alignment needed by struct object_id. */
|
||||
memcpy(&k, n->k, sizeof(k));
|
||||
|
||||
if (x->algo != GIT_HASH_UNKNOWN && x->algo != k.algo)
|
||||
return CB_CONTINUE;
|
||||
if (data->algo != GIT_HASH_UNKNOWN && data->algo != k.algo)
|
||||
return 0;
|
||||
|
||||
if (x->last_nibble_at) {
|
||||
if ((k.hash[*x->last_nibble_at] ^ x->last_byte) & 0xf0)
|
||||
return CB_CONTINUE;
|
||||
if (data->last_nibble_at) {
|
||||
if ((k.hash[*data->last_nibble_at] ^ data->last_byte) & 0xf0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return x->fn(&k, x->arg);
|
||||
return data->cb(&k, data->cb_data);
|
||||
}
|
||||
|
||||
void oidtree_each(struct oidtree *ot, const struct object_id *oid,
|
||||
size_t oidhexsz, oidtree_iter fn, void *arg)
|
||||
int oidtree_each(struct oidtree *ot, const struct object_id *prefix,
|
||||
size_t prefix_hex_len, oidtree_each_cb cb, void *cb_data)
|
||||
{
|
||||
size_t klen = oidhexsz / 2;
|
||||
struct oidtree_iter_data x = { 0 };
|
||||
assert(oidhexsz <= GIT_MAX_HEXSZ);
|
||||
struct oidtree_each_data data = {
|
||||
.cb = cb,
|
||||
.cb_data = cb_data,
|
||||
.algo = prefix->algo,
|
||||
};
|
||||
size_t klen = prefix_hex_len / 2;
|
||||
assert(prefix_hex_len <= GIT_MAX_HEXSZ);
|
||||
|
||||
x.fn = fn;
|
||||
x.arg = arg;
|
||||
x.algo = oid->algo;
|
||||
if (oidhexsz & 1) {
|
||||
x.last_byte = oid->hash[klen];
|
||||
x.last_nibble_at = &klen;
|
||||
if (prefix_hex_len & 1) {
|
||||
data.last_byte = prefix->hash[klen];
|
||||
data.last_nibble_at = &klen;
|
||||
}
|
||||
cb_each(&ot->tree, (const uint8_t *)oid, klen, iter, &x);
|
||||
|
||||
return cb_each(&ot->tree, prefix->hash, klen, iter, &data);
|
||||
}
|
||||
|
||||
@@ -5,18 +5,52 @@
|
||||
#include "hash.h"
|
||||
#include "mem-pool.h"
|
||||
|
||||
/*
|
||||
* OID trees are an efficient storage for object IDs that use a critbit tree
|
||||
* internally. Common prefixes are duplicated and object IDs are stored in a
|
||||
* way that allow easy iteration over the objects in lexicographic order. As a
|
||||
* consequence, operations that want to enumerate all object IDs that match a
|
||||
* given prefix can be answered efficiently.
|
||||
*
|
||||
* Note that it is not (yet) possible to store data other than the object IDs
|
||||
* themselves in this tree.
|
||||
*/
|
||||
struct oidtree {
|
||||
struct cb_tree tree;
|
||||
struct mem_pool mem_pool;
|
||||
};
|
||||
|
||||
void oidtree_init(struct oidtree *);
|
||||
void oidtree_clear(struct oidtree *);
|
||||
void oidtree_insert(struct oidtree *, const struct object_id *);
|
||||
int oidtree_contains(struct oidtree *, const struct object_id *);
|
||||
/* Initialize the oidtree so that it is ready for use. */
|
||||
void oidtree_init(struct oidtree *ot);
|
||||
|
||||
typedef enum cb_next (*oidtree_iter)(const struct object_id *, void *data);
|
||||
void oidtree_each(struct oidtree *, const struct object_id *,
|
||||
size_t oidhexsz, oidtree_iter, void *data);
|
||||
/*
|
||||
* Release all memory associated with the oidtree and reinitialize it for
|
||||
* subsequent use.
|
||||
*/
|
||||
void oidtree_clear(struct oidtree *ot);
|
||||
|
||||
/* Insert the object ID into the tree. */
|
||||
void oidtree_insert(struct oidtree *ot, const struct object_id *oid);
|
||||
|
||||
/* Check whether the tree contains the given object ID. */
|
||||
bool oidtree_contains(struct oidtree *ot, const struct object_id *oid);
|
||||
|
||||
/*
|
||||
* Callback function used for `oidtree_each()`. Returning a non-zero exit code
|
||||
* will cause iteration to stop. The exit code will be propagated to the caller
|
||||
* of `oidtree_each()`.
|
||||
*/
|
||||
typedef int (*oidtree_each_cb)(const struct object_id *oid,
|
||||
void *cb_data);
|
||||
|
||||
/*
|
||||
* Iterate through all object IDs in the tree whose prefix matches the given
|
||||
* object ID prefix and invoke the callback function on each of them.
|
||||
*
|
||||
* Returns any non-zero exit code from the provided callback function.
|
||||
*/
|
||||
int oidtree_each(struct oidtree *ot,
|
||||
const struct object_id *prefix, size_t prefix_hex_len,
|
||||
oidtree_each_cb cb, void *cb_data);
|
||||
|
||||
#endif /* OIDTREE_H */
|
||||
|
||||
+4
-3
@@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r,
|
||||
struct packed_git *p,
|
||||
struct pack_window **w_curs,
|
||||
verify_fn fn,
|
||||
void *fn_data,
|
||||
struct progress *progress, uint32_t base_count)
|
||||
|
||||
{
|
||||
@@ -161,7 +162,7 @@ static int verify_packfile(struct repository *r,
|
||||
oid_to_hex(&oid), p->pack_name);
|
||||
else if (fn) {
|
||||
int eaten = 0;
|
||||
err |= fn(&oid, type, size, data, &eaten);
|
||||
err |= fn(&oid, type, size, data, &eaten, fn_data);
|
||||
if (eaten)
|
||||
data = NULL;
|
||||
}
|
||||
@@ -192,7 +193,7 @@ int verify_pack_index(struct packed_git *p)
|
||||
return err;
|
||||
}
|
||||
|
||||
int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
|
||||
int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data,
|
||||
struct progress *progress, uint32_t base_count)
|
||||
{
|
||||
int err = 0;
|
||||
@@ -202,7 +203,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
|
||||
if (!p->index_data)
|
||||
return -1;
|
||||
|
||||
err |= verify_packfile(r, p, &w_curs, fn, progress, base_count);
|
||||
err |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count);
|
||||
unuse_pack(&w_curs);
|
||||
|
||||
return err;
|
||||
|
||||
@@ -85,7 +85,11 @@ struct pack_idx_entry {
|
||||
|
||||
struct progress;
|
||||
/* Note, the data argument could be NULL if object type is blob */
|
||||
typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
|
||||
typedef int (*verify_fn)(const struct object_id *oid,
|
||||
enum object_type type,
|
||||
unsigned long size,
|
||||
void *buffer, int *eaten,
|
||||
void *fn_data);
|
||||
|
||||
const char *write_idx_file(struct repository *repo,
|
||||
const char *index_name,
|
||||
@@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo,
|
||||
const unsigned char *sha1);
|
||||
int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
|
||||
int verify_pack_index(struct packed_git *);
|
||||
int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
|
||||
int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data,
|
||||
struct progress *, uint32_t);
|
||||
off_t write_pack_header(struct hashfile *f, uint32_t);
|
||||
void fixup_pack_header_footer(const struct git_hash_algo *, int,
|
||||
unsigned char *, const char *, uint32_t,
|
||||
|
||||
+294
-8
@@ -2244,7 +2244,8 @@ struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *st
|
||||
struct packed_git *p = e->pack;
|
||||
|
||||
if ((p->pack_keep && (flags & KEPT_PACK_ON_DISK)) ||
|
||||
(p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE))) {
|
||||
(p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE)) ||
|
||||
(p->pack_keep_in_core_open && (flags & KEPT_PACK_IN_CORE_OPEN))) {
|
||||
ALLOC_GROW(packs, nr + 1, alloc);
|
||||
packs[nr++] = p;
|
||||
}
|
||||
@@ -2299,7 +2300,7 @@ int has_object_kept_pack(struct repository *r, const struct object_id *oid,
|
||||
|
||||
int for_each_object_in_pack(struct packed_git *p,
|
||||
each_packed_object_fn cb, void *data,
|
||||
unsigned flags)
|
||||
enum odb_for_each_object_flags flags)
|
||||
{
|
||||
uint32_t i;
|
||||
int r = 0;
|
||||
@@ -2371,11 +2372,182 @@ static int packfile_store_for_each_object_wrapper(const struct object_id *oid,
|
||||
}
|
||||
}
|
||||
|
||||
static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
|
||||
{
|
||||
do {
|
||||
if (*a != *b)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
len -= 2;
|
||||
} while (len > 1);
|
||||
if (len)
|
||||
if ((*a ^ *b) & 0xf0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int for_each_prefixed_object_in_midx(
|
||||
struct packfile_store *store,
|
||||
struct multi_pack_index *m,
|
||||
const struct odb_for_each_object_options *opts,
|
||||
struct packfile_store_for_each_object_wrapper_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for (; m; m = m->base_midx) {
|
||||
uint32_t num, i, first = 0;
|
||||
int len = opts->prefix_hex_len > m->source->odb->repo->hash_algo->hexsz ?
|
||||
m->source->odb->repo->hash_algo->hexsz : opts->prefix_hex_len;
|
||||
|
||||
if (!m->num_objects)
|
||||
continue;
|
||||
|
||||
num = m->num_objects + m->num_objects_in_base;
|
||||
|
||||
bsearch_one_midx(opts->prefix, m, &first);
|
||||
|
||||
/*
|
||||
* At this point, "first" is the location of the lowest
|
||||
* object with an object name that could match "opts->prefix".
|
||||
* See if we have 0, 1 or more objects that actually match(es).
|
||||
*/
|
||||
for (i = first; i < num; i++) {
|
||||
const struct object_id *current = NULL;
|
||||
struct object_id oid;
|
||||
|
||||
current = nth_midxed_object_oid(&oid, m, i);
|
||||
|
||||
if (!match_hash(len, opts->prefix->hash, current->hash))
|
||||
break;
|
||||
|
||||
if (data->request) {
|
||||
struct object_info oi = *data->request;
|
||||
|
||||
ret = packfile_store_read_object_info(store, current,
|
||||
&oi, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = data->cb(&oid, &oi, data->cb_data);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
ret = data->cb(&oid, NULL, data->cb_data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int for_each_prefixed_object_in_pack(
|
||||
struct packfile_store *store,
|
||||
struct packed_git *p,
|
||||
const struct odb_for_each_object_options *opts,
|
||||
struct packfile_store_for_each_object_wrapper_data *data)
|
||||
{
|
||||
uint32_t num, i, first = 0;
|
||||
int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ?
|
||||
p->repo->hash_algo->hexsz : opts->prefix_hex_len;
|
||||
int ret;
|
||||
|
||||
num = p->num_objects;
|
||||
bsearch_pack(opts->prefix, p, &first);
|
||||
|
||||
/*
|
||||
* At this point, "first" is the location of the lowest object
|
||||
* with an object name that could match "bin_pfx". See if we have
|
||||
* 0, 1 or more objects that actually match(es).
|
||||
*/
|
||||
for (i = first; i < num; i++) {
|
||||
struct object_id oid;
|
||||
|
||||
nth_packed_object_id(&oid, p, i);
|
||||
if (!match_hash(len, opts->prefix->hash, oid.hash))
|
||||
break;
|
||||
|
||||
if (data->request) {
|
||||
struct object_info oi = *data->request;
|
||||
|
||||
ret = packfile_store_read_object_info(store, &oid, &oi, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = data->cb(&oid, &oi, data->cb_data);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
ret = data->cb(&oid, NULL, data->cb_data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int packfile_store_for_each_prefixed_object(
|
||||
struct packfile_store *store,
|
||||
const struct odb_for_each_object_options *opts,
|
||||
struct packfile_store_for_each_object_wrapper_data *data)
|
||||
{
|
||||
struct packfile_list_entry *e;
|
||||
struct multi_pack_index *m;
|
||||
bool pack_errors = false;
|
||||
int ret;
|
||||
|
||||
if (opts->flags)
|
||||
BUG("flags unsupported");
|
||||
|
||||
store->skip_mru_updates = true;
|
||||
|
||||
m = get_multi_pack_index(store->source);
|
||||
if (m) {
|
||||
ret = for_each_prefixed_object_in_midx(store, m, opts, data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (e = packfile_store_get_packs(store); e; e = e->next) {
|
||||
if (e->pack->multi_pack_index)
|
||||
continue;
|
||||
|
||||
if (open_pack_index(e->pack)) {
|
||||
pack_errors = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!e->pack->num_objects)
|
||||
continue;
|
||||
|
||||
ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
store->skip_mru_updates = false;
|
||||
if (!ret && pack_errors)
|
||||
ret = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int packfile_store_for_each_object(struct packfile_store *store,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags)
|
||||
const struct odb_for_each_object_options *opts)
|
||||
{
|
||||
struct packfile_store_for_each_object_wrapper_data data = {
|
||||
.store = store,
|
||||
@@ -2386,20 +2558,23 @@ int packfile_store_for_each_object(struct packfile_store *store,
|
||||
struct packfile_list_entry *e;
|
||||
int pack_errors = 0, ret;
|
||||
|
||||
if (opts->prefix)
|
||||
return packfile_store_for_each_prefixed_object(store, opts, &data);
|
||||
|
||||
store->skip_mru_updates = true;
|
||||
|
||||
for (e = packfile_store_get_packs(store); e; e = e->next) {
|
||||
struct packed_git *p = e->pack;
|
||||
|
||||
if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
|
||||
if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
|
||||
continue;
|
||||
if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) &&
|
||||
if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) &&
|
||||
!p->pack_promisor)
|
||||
continue;
|
||||
if ((flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
|
||||
if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) &&
|
||||
p->pack_keep_in_core)
|
||||
continue;
|
||||
if ((flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
|
||||
if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) &&
|
||||
p->pack_keep)
|
||||
continue;
|
||||
if (open_pack_index(p)) {
|
||||
@@ -2408,7 +2583,7 @@ int packfile_store_for_each_object(struct packfile_store *store,
|
||||
}
|
||||
|
||||
ret = for_each_object_in_pack(p, packfile_store_for_each_object_wrapper,
|
||||
&data, flags);
|
||||
&data, opts->flags);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@@ -2423,6 +2598,117 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int extend_abbrev_len(const struct object_id *a,
|
||||
const struct object_id *b,
|
||||
unsigned *out)
|
||||
{
|
||||
unsigned len = oid_common_prefix_hexlen(a, b);
|
||||
if (len != hash_algos[a->algo].hexsz && len >= *out)
|
||||
*out = len + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void find_abbrev_len_for_midx(struct multi_pack_index *m,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out)
|
||||
{
|
||||
unsigned len = min_len;
|
||||
|
||||
for (; m; m = m->base_midx) {
|
||||
int match = 0;
|
||||
uint32_t num, first = 0;
|
||||
struct object_id found_oid;
|
||||
|
||||
if (!m->num_objects)
|
||||
continue;
|
||||
|
||||
num = m->num_objects + m->num_objects_in_base;
|
||||
match = bsearch_one_midx(oid, m, &first);
|
||||
|
||||
/*
|
||||
* first is now the position in the packfile where we
|
||||
* would insert the object ID if it does not exist (or the
|
||||
* position of the object ID if it does exist). Hence, we
|
||||
* consider a maximum of two objects nearby for the
|
||||
* abbreviation length.
|
||||
*/
|
||||
|
||||
if (!match) {
|
||||
if (nth_midxed_object_oid(&found_oid, m, first))
|
||||
extend_abbrev_len(&found_oid, oid, &len);
|
||||
} else if (first < num - 1) {
|
||||
if (nth_midxed_object_oid(&found_oid, m, first + 1))
|
||||
extend_abbrev_len(&found_oid, oid, &len);
|
||||
}
|
||||
if (first > 0) {
|
||||
if (nth_midxed_object_oid(&found_oid, m, first - 1))
|
||||
extend_abbrev_len(&found_oid, oid, &len);
|
||||
}
|
||||
}
|
||||
|
||||
*out = len;
|
||||
}
|
||||
|
||||
static void find_abbrev_len_for_pack(struct packed_git *p,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out)
|
||||
{
|
||||
int match;
|
||||
uint32_t num, first = 0;
|
||||
struct object_id found_oid;
|
||||
unsigned len = min_len;
|
||||
|
||||
num = p->num_objects;
|
||||
match = bsearch_pack(oid, p, &first);
|
||||
|
||||
/*
|
||||
* first is now the position in the packfile where we would insert
|
||||
* the object ID if it does not exist (or the position of mad->hash if
|
||||
* it does exist). Hence, we consider a maximum of two objects
|
||||
* nearby for the abbreviation length.
|
||||
*/
|
||||
if (!match) {
|
||||
if (!nth_packed_object_id(&found_oid, p, first))
|
||||
extend_abbrev_len(&found_oid, oid, &len);
|
||||
} else if (first < num - 1) {
|
||||
if (!nth_packed_object_id(&found_oid, p, first + 1))
|
||||
extend_abbrev_len(&found_oid, oid, &len);
|
||||
}
|
||||
if (first > 0) {
|
||||
if (!nth_packed_object_id(&found_oid, p, first - 1))
|
||||
extend_abbrev_len(&found_oid, oid, &len);
|
||||
}
|
||||
|
||||
*out = len;
|
||||
}
|
||||
|
||||
int packfile_store_find_abbrev_len(struct packfile_store *store,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out)
|
||||
{
|
||||
struct packfile_list_entry *e;
|
||||
struct multi_pack_index *m;
|
||||
|
||||
m = get_multi_pack_index(store->source);
|
||||
if (m)
|
||||
find_abbrev_len_for_midx(m, oid, min_len, &min_len);
|
||||
|
||||
for (e = packfile_store_get_packs(store); e; e = e->next) {
|
||||
if (e->pack->multi_pack_index)
|
||||
continue;
|
||||
if (open_pack_index(e->pack) || !e->pack->num_objects)
|
||||
continue;
|
||||
|
||||
find_abbrev_len_for_pack(e->pack, oid, min_len, &min_len);
|
||||
}
|
||||
|
||||
*out = min_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct add_promisor_object_data {
|
||||
struct repository *repo;
|
||||
struct oidset *set;
|
||||
|
||||
+9
-2
@@ -28,6 +28,7 @@ struct packed_git {
|
||||
unsigned pack_local:1,
|
||||
pack_keep:1,
|
||||
pack_keep_in_core:1,
|
||||
pack_keep_in_core_open:1,
|
||||
freshened:1,
|
||||
do_not_close:1,
|
||||
pack_promisor:1,
|
||||
@@ -266,6 +267,7 @@ int packfile_store_freshen_object(struct packfile_store *store,
|
||||
enum kept_pack_type {
|
||||
KEPT_PACK_ON_DISK = (1 << 0),
|
||||
KEPT_PACK_IN_CORE = (1 << 1),
|
||||
KEPT_PACK_IN_CORE_OPEN = (1 << 2),
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -352,7 +354,7 @@ typedef int each_packed_object_fn(const struct object_id *oid,
|
||||
void *data);
|
||||
int for_each_object_in_pack(struct packed_git *p,
|
||||
each_packed_object_fn, void *data,
|
||||
unsigned flags);
|
||||
enum odb_for_each_object_flags flags);
|
||||
|
||||
/*
|
||||
* Iterate through all packed objects in the given packfile store and invoke
|
||||
@@ -367,7 +369,12 @@ int packfile_store_for_each_object(struct packfile_store *store,
|
||||
const struct object_info *request,
|
||||
odb_for_each_object_cb cb,
|
||||
void *cb_data,
|
||||
unsigned flags);
|
||||
const struct odb_for_each_object_options *opts);
|
||||
|
||||
int packfile_store_find_abbrev_len(struct packfile_store *store,
|
||||
const struct object_id *oid,
|
||||
unsigned min_len,
|
||||
unsigned *out);
|
||||
|
||||
/* A hook to report invalid files in pack directory */
|
||||
#define PACKDIR_FILE_PACK 1
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
|
||||
|
||||
if (!o) {
|
||||
error(_("failed to find object %s"),
|
||||
oid_to_hex(&o->oid));
|
||||
oid_to_hex(&entry.oid));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
+9
-24
@@ -2309,13 +2309,9 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
|
||||
}
|
||||
munmap((void *)mmap, mmap_size);
|
||||
|
||||
/*
|
||||
* TODO trace2: replace "the_repository" with the actual repo instance
|
||||
* that is associated with the given "istate".
|
||||
*/
|
||||
trace2_data_intmax("index", the_repository, "read/version",
|
||||
trace2_data_intmax("index", istate->repo, "read/version",
|
||||
istate->version);
|
||||
trace2_data_intmax("index", the_repository, "read/cache_nr",
|
||||
trace2_data_intmax("index", istate->repo, "read/cache_nr",
|
||||
istate->cache_nr);
|
||||
|
||||
/*
|
||||
@@ -2360,16 +2356,12 @@ int read_index_from(struct index_state *istate, const char *path,
|
||||
if (istate->initialized)
|
||||
return istate->cache_nr;
|
||||
|
||||
/*
|
||||
* TODO trace2: replace "the_repository" with the actual repo instance
|
||||
* that is associated with the given "istate".
|
||||
*/
|
||||
trace2_region_enter_printf("index", "do_read_index", the_repository,
|
||||
trace2_region_enter_printf("index", "do_read_index", istate->repo,
|
||||
"%s", path);
|
||||
trace_performance_enter();
|
||||
ret = do_read_index(istate, path, 0);
|
||||
trace_performance_leave("read cache %s", path);
|
||||
trace2_region_leave_printf("index", "do_read_index", the_repository,
|
||||
trace2_region_leave_printf("index", "do_read_index", istate->repo,
|
||||
"%s", path);
|
||||
|
||||
split_index = istate->split_index;
|
||||
@@ -3096,13 +3088,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
|
||||
istate->timestamp.nsec = ST_MTIME_NSEC(st);
|
||||
trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
|
||||
|
||||
/*
|
||||
* TODO trace2: replace "the_repository" with the actual repo instance
|
||||
* that is associated with the given "istate".
|
||||
*/
|
||||
trace2_data_intmax("index", the_repository, "write/version",
|
||||
trace2_data_intmax("index", istate->repo, "write/version",
|
||||
istate->version);
|
||||
trace2_data_intmax("index", the_repository, "write/cache_nr",
|
||||
trace2_data_intmax("index", istate->repo, "write/cache_nr",
|
||||
istate->cache_nr);
|
||||
|
||||
ret = 0;
|
||||
@@ -3144,14 +3132,10 @@ static int do_write_locked_index(struct index_state *istate,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO trace2: replace "the_repository" with the actual repo instance
|
||||
* that is associated with the given "istate".
|
||||
*/
|
||||
trace2_region_enter_printf("index", "do_write_index", the_repository,
|
||||
trace2_region_enter_printf("index", "do_write_index", istate->repo,
|
||||
"%s", get_lock_file_path(lock));
|
||||
ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
|
||||
trace2_region_leave_printf("index", "do_write_index", the_repository,
|
||||
trace2_region_leave_printf("index", "do_write_index", istate->repo,
|
||||
"%s", get_lock_file_path(lock));
|
||||
|
||||
if (was_full)
|
||||
@@ -4049,6 +4033,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = &data;
|
||||
rev.diffopt.flags.override_submodule_config = 1;
|
||||
rev.diffopt.detect_rename = 0; /* staging worktree changes does not need renames */
|
||||
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
|
||||
|
||||
/*
|
||||
|
||||
@@ -168,7 +168,7 @@ static int tree_is_complete(const struct object_id *oid)
|
||||
complete = 1;
|
||||
while (tree_entry(&desc, &entry)) {
|
||||
if (!odb_has_object(the_repository->objects, &entry.oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) ||
|
||||
(S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
|
||||
tree->object.flags |= INCOMPLETE;
|
||||
complete = 0;
|
||||
|
||||
@@ -425,7 +425,7 @@ int ref_resolves_to_object(const char *refname,
|
||||
if (flags & REF_ISBROKEN)
|
||||
return 0;
|
||||
if (!odb_has_object(repo->objects, oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
error(_("%s does not point to a valid object!"), refname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -366,11 +366,6 @@ static int reftable_be_config(const char *var, const char *value,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reftable_be_fsync(int fd)
|
||||
{
|
||||
return fsync_component(FSYNC_COMPONENT_REFERENCE, fd);
|
||||
}
|
||||
|
||||
static struct ref_store *reftable_be_init(struct repository *repo,
|
||||
const char *payload,
|
||||
const char *gitdir,
|
||||
@@ -408,7 +403,6 @@ static struct ref_store *reftable_be_init(struct repository *repo,
|
||||
refs->write_options.disable_auto_compact =
|
||||
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
|
||||
refs->write_options.lock_timeout_ms = 100;
|
||||
refs->write_options.fsync = reftable_be_fsync;
|
||||
|
||||
repo_config(the_repository, reftable_be_config, &refs->write_options);
|
||||
|
||||
|
||||
+7
-12
@@ -93,13 +93,12 @@ void block_source_from_buf(struct reftable_block_source *bs,
|
||||
}
|
||||
|
||||
struct file_block_source {
|
||||
uint64_t size;
|
||||
unsigned char *data;
|
||||
struct reftable_mmap mmap;
|
||||
};
|
||||
|
||||
static uint64_t file_size(void *b)
|
||||
{
|
||||
return ((struct file_block_source *)b)->size;
|
||||
return ((struct file_block_source *)b)->mmap.size;
|
||||
}
|
||||
|
||||
static void file_release_data(void *b REFTABLE_UNUSED, struct reftable_block_data *dest REFTABLE_UNUSED)
|
||||
@@ -109,7 +108,7 @@ static void file_release_data(void *b REFTABLE_UNUSED, struct reftable_block_dat
|
||||
static void file_close(void *v)
|
||||
{
|
||||
struct file_block_source *b = v;
|
||||
munmap(b->data, b->size);
|
||||
reftable_munmap(&b->mmap);
|
||||
reftable_free(b);
|
||||
}
|
||||
|
||||
@@ -117,8 +116,8 @@ static ssize_t file_read_data(void *v, struct reftable_block_data *dest, uint64_
|
||||
uint32_t size)
|
||||
{
|
||||
struct file_block_source *b = v;
|
||||
assert(off + size <= b->size);
|
||||
dest->data = b->data + off;
|
||||
assert(off + size <= b->mmap.size);
|
||||
dest->data = (unsigned char *) b->mmap.data + off;
|
||||
dest->len = size;
|
||||
return size;
|
||||
}
|
||||
@@ -156,13 +155,9 @@ int reftable_block_source_from_file(struct reftable_block_source *bs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
p->size = st.st_size;
|
||||
p->data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (p->data == MAP_FAILED) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
p->data = NULL;
|
||||
err = reftable_mmap(&p->mmap, fd, st.st_size);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert(!bs->ops);
|
||||
bs->ops = &file_vtable;
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ static int table_check_name(struct reftable_table *table,
|
||||
|
||||
static int table_checks(struct reftable_table *table,
|
||||
reftable_fsck_report_fn report_fn,
|
||||
reftable_fsck_verbose_fn verbose_fn UNUSED,
|
||||
reftable_fsck_verbose_fn verbose_fn REFTABLE_UNUSED,
|
||||
void *cb_data)
|
||||
{
|
||||
table_check_fn table_check_fns[] = {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef REFTABLE_BASICS_H
|
||||
#define REFTABLE_BASICS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "reftable-system.h"
|
||||
|
||||
/* A buffer that contains arbitrary byte slices. */
|
||||
struct reftable_buf {
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#ifndef REFTABLE_BLOCK_H
|
||||
#define REFTABLE_BLOCK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-basics.h"
|
||||
#include "reftable-blocksource.h"
|
||||
#include "reftable-iterator.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef REFTABLE_BLOCKSOURCE_H
|
||||
#define REFTABLE_BLOCKSOURCE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "reftable-system.h"
|
||||
|
||||
/*
|
||||
* Generic wrapper for a seekable readable file.
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#ifndef REFTABLE_ERROR_H
|
||||
#define REFTABLE_ERROR_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
|
||||
/*
|
||||
* Errors in reftable calls are signaled with negative integer return values. 0
|
||||
* means success.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef REFTABLE_FSCK_H
|
||||
#define REFTABLE_FSCK_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-stack.h"
|
||||
|
||||
enum reftable_fsck_error {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef REFTABLE_ITERATOR_H
|
||||
#define REFTABLE_ITERATOR_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-record.h"
|
||||
|
||||
struct reftable_iterator_vtable;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef REFTABLE_MERGED_H
|
||||
#define REFTABLE_MERGED_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-iterator.h"
|
||||
|
||||
/*
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#ifndef REFTABLE_RECORD_H
|
||||
#define REFTABLE_RECORD_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-basics.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Basic data types
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef REFTABLE_STACK_H
|
||||
#define REFTABLE_STACK_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-writer.h"
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef REFTABLE_SYSTEM_H
|
||||
#define REFTABLE_SYSTEM_H
|
||||
|
||||
/*
|
||||
* This header defines the platform-specific bits required to compile the
|
||||
* reftable library. It should provide an environment that bridges over the
|
||||
* gaps between POSIX and your system, as well as the zlib interfaces. This
|
||||
* header is expected to be changed by the individual project.
|
||||
*/
|
||||
|
||||
#define MINGW_DONT_HANDLE_IN_USE_ERROR
|
||||
#include "compat/posix.h"
|
||||
#include "compat/zlib-compat.h"
|
||||
|
||||
int reftable_fsync(int fd);
|
||||
#define fsync(fd) reftable_fsync(fd)
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef REFTABLE_TABLE_H
|
||||
#define REFTABLE_TABLE_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-iterator.h"
|
||||
#include "reftable-block.h"
|
||||
#include "reftable-blocksource.h"
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
#ifndef REFTABLE_WRITER_H
|
||||
#define REFTABLE_WRITER_H
|
||||
|
||||
#include "reftable-system.h"
|
||||
#include "reftable-record.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h> /* ssize_t */
|
||||
|
||||
/* Writing single reftables */
|
||||
|
||||
/* reftable_write_options sets options for writing a single reftable. */
|
||||
@@ -63,12 +61,6 @@ struct reftable_write_options {
|
||||
*/
|
||||
long lock_timeout_ms;
|
||||
|
||||
/*
|
||||
* Optional callback used to fsync files to disk. Falls back to using
|
||||
* fsync(3P) when unset.
|
||||
*/
|
||||
int (*fsync)(int fd);
|
||||
|
||||
/*
|
||||
* Callback function to execute whenever the stack is being reloaded.
|
||||
* This can be used e.g. to discard cached information that relies on
|
||||
|
||||
+7
-33
@@ -29,13 +29,6 @@ static int stack_filename(struct reftable_buf *dest, struct reftable_stack *st,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stack_fsync(const struct reftable_write_options *opts, int fd)
|
||||
{
|
||||
if (opts->fsync)
|
||||
return opts->fsync(fd);
|
||||
return fsync(fd);
|
||||
}
|
||||
|
||||
static ssize_t reftable_write_data(int fd, const void *data, size_t size)
|
||||
{
|
||||
size_t total_written = 0;
|
||||
@@ -69,7 +62,7 @@ static ssize_t fd_writer_write(void *arg, const void *data, size_t sz)
|
||||
static int fd_writer_flush(void *arg)
|
||||
{
|
||||
struct fd_writer *writer = arg;
|
||||
return stack_fsync(writer->opts, writer->fd);
|
||||
return fsync(writer->fd);
|
||||
}
|
||||
|
||||
static int fd_read_lines(int fd, char ***namesp)
|
||||
@@ -372,45 +365,26 @@ done:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* return negative if a before b. */
|
||||
static int tv_cmp(struct timeval *a, struct timeval *b)
|
||||
{
|
||||
time_t diff = a->tv_sec - b->tv_sec;
|
||||
int udiff = a->tv_usec - b->tv_usec;
|
||||
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
return udiff;
|
||||
}
|
||||
|
||||
static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
|
||||
int reuse_open)
|
||||
{
|
||||
char **names = NULL, **names_after = NULL;
|
||||
struct timeval deadline;
|
||||
uint64_t deadline;
|
||||
int64_t delay = 0;
|
||||
int tries = 0, err;
|
||||
int fd = -1;
|
||||
|
||||
err = gettimeofday(&deadline, NULL);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
deadline.tv_sec += 3;
|
||||
deadline = reftable_time_ms() + 3000;
|
||||
|
||||
while (1) {
|
||||
struct timeval now;
|
||||
|
||||
err = gettimeofday(&now, NULL);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
uint64_t now = reftable_time_ms();
|
||||
|
||||
/*
|
||||
* Only look at deadlines after the first few times. This
|
||||
* simplifies debugging in GDB.
|
||||
*/
|
||||
tries++;
|
||||
if (tries > 3 && tv_cmp(&now, &deadline) >= 0)
|
||||
if (tries > 3 && now >= deadline)
|
||||
goto out;
|
||||
|
||||
fd = open(st->list_file, O_RDONLY);
|
||||
@@ -812,7 +786,7 @@ int reftable_addition_commit(struct reftable_addition *add)
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = stack_fsync(&add->stack->opts, add->tables_list_lock.fd);
|
||||
err = fsync(add->tables_list_lock.fd);
|
||||
if (err < 0) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
goto done;
|
||||
@@ -1480,7 +1454,7 @@ static int stack_compact_range(struct reftable_stack *st,
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = stack_fsync(&st->opts, tables_list_lock.fd);
|
||||
err = fsync(tables_list_lock.fd);
|
||||
if (err < 0) {
|
||||
err = REFTABLE_IO_ERROR;
|
||||
unlink(new_table_path.buf);
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#include "basics.h"
|
||||
#include "reftable-error.h"
|
||||
#include "../lockfile.h"
|
||||
#include "../trace.h"
|
||||
#include "../tempfile.h"
|
||||
#include "../write-or-die.h"
|
||||
|
||||
uint32_t reftable_rand(void)
|
||||
{
|
||||
@@ -131,3 +133,33 @@ int flock_commit(struct reftable_flock *l)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reftable_fsync(int fd)
|
||||
{
|
||||
return fsync_component(FSYNC_COMPONENT_REFERENCE, fd);
|
||||
}
|
||||
|
||||
uint64_t reftable_time_ms(void)
|
||||
{
|
||||
return getnanotime() / 1000000;
|
||||
}
|
||||
|
||||
int reftable_mmap(struct reftable_mmap *out, int fd, size_t len)
|
||||
{
|
||||
void *data = xmmap_gently(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (data == MAP_FAILED)
|
||||
return REFTABLE_IO_ERROR;
|
||||
|
||||
out->data = data;
|
||||
out->size = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reftable_munmap(struct reftable_mmap *mmap)
|
||||
{
|
||||
if (munmap(mmap->data, mmap->size) < 0)
|
||||
return REFTABLE_IO_ERROR;
|
||||
memset(mmap, 0, sizeof(*mmap));
|
||||
return 0;
|
||||
}
|
||||
|
||||
+28
-4
@@ -9,11 +9,14 @@
|
||||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
/* This header glues the reftable library to the rest of Git */
|
||||
/*
|
||||
* This header defines the platform-agnostic interface that is to be
|
||||
* implemented by the project to make it work on their respective supported
|
||||
* systems, and to integrate it into the project itself. This header is not
|
||||
* expected to be changed by the individual project.
|
||||
*/
|
||||
|
||||
#define MINGW_DONT_HANDLE_IN_USE_ERROR
|
||||
#include "compat/posix.h"
|
||||
#include "compat/zlib-compat.h"
|
||||
#include "reftable-system.h"
|
||||
|
||||
/*
|
||||
* Return a random 32 bit integer. This function is expected to return
|
||||
@@ -108,4 +111,25 @@ int flock_release(struct reftable_flock *l);
|
||||
*/
|
||||
int flock_commit(struct reftable_flock *l);
|
||||
|
||||
/* Report the time in milliseconds. */
|
||||
uint64_t reftable_time_ms(void);
|
||||
|
||||
struct reftable_mmap {
|
||||
void *data;
|
||||
size_t size;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/*
|
||||
* Map the file into memory. Returns 0 on success, a reftable error code on
|
||||
* error.
|
||||
*/
|
||||
int reftable_mmap(struct reftable_mmap *out, int fd, size_t len);
|
||||
|
||||
/*
|
||||
* Unmap the file from memory. Returns 0 on success, a reftable error code on
|
||||
* error.
|
||||
*/
|
||||
int reftable_munmap(struct reftable_mmap *mmap);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1723,7 +1723,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
||||
if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
|
||||
if (starts_with(ref->name, "refs/tags/"))
|
||||
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
|
||||
else if (!odb_has_object(the_repository->objects, &ref->old_oid, HAS_OBJECT_RECHECK_PACKED))
|
||||
else if (!odb_has_object(the_repository->objects, &ref->old_oid, ODB_HAS_OBJECT_RECHECK_PACKED))
|
||||
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
|
||||
else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) ||
|
||||
!lookup_commit_reference_gently(the_repository, &ref->new_oid, 1))
|
||||
|
||||
@@ -254,7 +254,10 @@ static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
|
||||
struct commit *commit,
|
||||
struct commit *fallback)
|
||||
{
|
||||
khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
|
||||
khint_t pos;
|
||||
if (!commit)
|
||||
return fallback;
|
||||
pos = kh_get_oid_map(replayed_commits, commit->object.oid);
|
||||
if (pos == kh_end(replayed_commits))
|
||||
return fallback;
|
||||
return kh_value(replayed_commits, pos);
|
||||
@@ -271,18 +274,26 @@ static struct commit *pick_regular_commit(struct repository *repo,
|
||||
struct commit *base, *replayed_base;
|
||||
struct tree *pickme_tree, *base_tree, *replayed_base_tree;
|
||||
|
||||
base = pickme->parents->item;
|
||||
replayed_base = mapped_commit(replayed_commits, base, onto);
|
||||
if (pickme->parents) {
|
||||
base = pickme->parents->item;
|
||||
base_tree = repo_get_commit_tree(repo, base);
|
||||
} else {
|
||||
base = NULL;
|
||||
base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
|
||||
}
|
||||
|
||||
replayed_base = mapped_commit(replayed_commits, base, onto);
|
||||
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
|
||||
pickme_tree = repo_get_commit_tree(repo, pickme);
|
||||
base_tree = repo_get_commit_tree(repo, base);
|
||||
|
||||
if (mode == REPLAY_MODE_PICK) {
|
||||
/* Cherry-pick: normal order */
|
||||
merge_opt->branch1 = short_commit_name(repo, replayed_base);
|
||||
merge_opt->branch2 = short_commit_name(repo, pickme);
|
||||
merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
|
||||
if (pickme->parents)
|
||||
merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
|
||||
else
|
||||
merge_opt->ancestor = xstrdup("empty tree");
|
||||
|
||||
merge_incore_nonrecursive(merge_opt,
|
||||
base_tree,
|
||||
@@ -347,13 +358,15 @@ int replay_revisions(struct rev_info *revs,
|
||||
struct commit *last_commit = NULL;
|
||||
struct commit *commit;
|
||||
struct commit *onto = NULL;
|
||||
struct merge_options merge_opt;
|
||||
struct merge_options merge_opt = { 0 };
|
||||
struct merge_result result = {
|
||||
.clean = 1,
|
||||
};
|
||||
bool detached_head;
|
||||
char *advance;
|
||||
char *revert;
|
||||
const char *ref;
|
||||
struct object_id old_oid;
|
||||
enum replay_mode mode = REPLAY_MODE_PICK;
|
||||
int ret;
|
||||
|
||||
@@ -364,7 +377,26 @@ int replay_revisions(struct rev_info *revs,
|
||||
set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto,
|
||||
&detached_head, &advance, &revert, &onto, &update_refs);
|
||||
|
||||
/* FIXME: Should allow replaying commits with the first as a root commit */
|
||||
if (opts->ref) {
|
||||
struct object_id oid;
|
||||
|
||||
if (update_refs && strset_get_size(update_refs) > 1) {
|
||||
ret = error(_("'--ref' cannot be used with multiple revision ranges"));
|
||||
goto out;
|
||||
}
|
||||
if (check_refname_format(opts->ref, 0) || !starts_with(opts->ref, "refs/")) {
|
||||
ret = error(_("'%s' is not a valid refname"), opts->ref);
|
||||
goto out;
|
||||
}
|
||||
ref = opts->ref;
|
||||
if (!refs_read_ref(get_main_ref_store(revs->repo), opts->ref, &oid))
|
||||
oidcpy(&old_oid, &oid);
|
||||
else
|
||||
oidclr(&old_oid, revs->repo->hash_algo);
|
||||
} else {
|
||||
ref = advance ? advance : revert;
|
||||
oidcpy(&old_oid, &onto->object.oid);
|
||||
}
|
||||
|
||||
if (prepare_revision_walk(revs) < 0) {
|
||||
ret = error(_("error preparing revisions"));
|
||||
@@ -380,9 +412,7 @@ int replay_revisions(struct rev_info *revs,
|
||||
khint_t pos;
|
||||
int hr;
|
||||
|
||||
if (!commit->parents)
|
||||
die(_("replaying down from root commit is not supported yet!"));
|
||||
if (commit->parents->next)
|
||||
if (commit->parents && commit->parents->next)
|
||||
die(_("replaying merge commits is not supported yet!"));
|
||||
|
||||
last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
|
||||
@@ -399,7 +429,7 @@ int replay_revisions(struct rev_info *revs,
|
||||
kh_value(replayed_commits, pos) = last_commit;
|
||||
|
||||
/* Update any necessary branches */
|
||||
if (advance || revert)
|
||||
if (ref)
|
||||
continue;
|
||||
|
||||
for (decoration = get_name_decoration(&commit->object);
|
||||
@@ -433,13 +463,9 @@ int replay_revisions(struct rev_info *revs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* In --advance or --revert mode, update the target ref */
|
||||
if (advance || revert) {
|
||||
const char *ref = advance ? advance : revert;
|
||||
replay_result_queue_update(out, ref,
|
||||
&onto->object.oid,
|
||||
if (ref)
|
||||
replay_result_queue_update(out, ref, &old_oid,
|
||||
&last_commit->object.oid);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
|
||||
@@ -24,6 +24,13 @@ struct replay_revisions_options {
|
||||
*/
|
||||
const char *onto;
|
||||
|
||||
/*
|
||||
* Reference to update with the result of the replay. This will not
|
||||
* update any refs from `onto`, `advance`, or `revert`. Ignores
|
||||
* `contained`.
|
||||
*/
|
||||
const char *ref;
|
||||
|
||||
/*
|
||||
* Starting point at which to create revert commits; must be a branch
|
||||
* name. The branch will be updated to point to the revert commits.
|
||||
|
||||
+36
-33
@@ -2038,41 +2038,32 @@ static void prepare_show_merge(struct rev_info *revs)
|
||||
free(prune);
|
||||
}
|
||||
|
||||
static int dotdot_missing(const char *arg, char *dotdot,
|
||||
static int dotdot_missing(const char *full_name,
|
||||
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);
|
||||
: "Invalid revision range %s", full_name);
|
||||
}
|
||||
|
||||
static int handle_dotdot_1(const char *arg, char *dotdot,
|
||||
static int handle_dotdot_1(const char *a_name, const char *b_name,
|
||||
const char *full_name, int symmetric,
|
||||
struct rev_info *revs, int flags,
|
||||
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;
|
||||
unsigned int a_flags, b_flags;
|
||||
int symmetric = 0;
|
||||
unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
|
||||
unsigned int oc_flags = GET_OID_COMMITTISH | GET_OID_RECORD_PATH;
|
||||
|
||||
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";
|
||||
|
||||
@@ -2081,15 +2072,13 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
|
||||
return -1;
|
||||
|
||||
if (!cant_be_filename) {
|
||||
*dotdot = '.';
|
||||
verify_non_filename(revs->prefix, arg);
|
||||
*dotdot = '\0';
|
||||
verify_non_filename(revs->prefix, full_name);
|
||||
}
|
||||
|
||||
a_obj = parse_object(revs->repo, &a_oid);
|
||||
b_obj = parse_object(revs->repo, &b_oid);
|
||||
if (!a_obj || !b_obj)
|
||||
return dotdot_missing(arg, dotdot, revs, symmetric);
|
||||
return dotdot_missing(full_name, revs, symmetric);
|
||||
|
||||
if (!symmetric) {
|
||||
/* just A..B */
|
||||
@@ -2103,7 +2092,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
|
||||
a = lookup_commit_reference(revs->repo, &a_obj->oid);
|
||||
b = lookup_commit_reference(revs->repo, &b_obj->oid);
|
||||
if (!a || !b)
|
||||
return dotdot_missing(arg, dotdot, revs, symmetric);
|
||||
return dotdot_missing(full_name, revs, symmetric);
|
||||
|
||||
if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
|
||||
commit_list_free(exclude);
|
||||
@@ -2132,16 +2121,23 @@ static int handle_dotdot(const char *arg,
|
||||
int cant_be_filename)
|
||||
{
|
||||
struct object_context a_oc = {0}, b_oc = {0};
|
||||
char *dotdot = strstr(arg, "..");
|
||||
const char *dotdot = strstr(arg, "..");
|
||||
char *tmp;
|
||||
int symmetric = 0;
|
||||
int ret;
|
||||
|
||||
if (!dotdot)
|
||||
return -1;
|
||||
|
||||
*dotdot = '\0';
|
||||
ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
|
||||
&a_oc, &b_oc);
|
||||
*dotdot = '.';
|
||||
tmp = xmemdupz(arg, dotdot - arg);
|
||||
dotdot += 2;
|
||||
if (*dotdot == '.') {
|
||||
symmetric = 1;
|
||||
dotdot++;
|
||||
}
|
||||
ret = handle_dotdot_1(tmp, dotdot, arg, symmetric, revs, flags,
|
||||
cant_be_filename, &a_oc, &b_oc);
|
||||
free(tmp);
|
||||
|
||||
object_context_release(&a_oc);
|
||||
object_context_release(&b_oc);
|
||||
@@ -2151,7 +2147,10 @@ static int handle_dotdot(const char *arg,
|
||||
static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
|
||||
{
|
||||
struct object_context oc = {0};
|
||||
char *mark;
|
||||
const char *mark;
|
||||
char *arg_minus_at = NULL;
|
||||
char *arg_minus_excl = NULL;
|
||||
char *arg_minus_dash = NULL;
|
||||
struct object *object;
|
||||
struct object_id oid;
|
||||
int local_flags;
|
||||
@@ -2178,18 +2177,17 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
|
||||
|
||||
mark = strstr(arg, "^@");
|
||||
if (mark && !mark[2]) {
|
||||
*mark = 0;
|
||||
if (add_parents_only(revs, arg, flags, 0)) {
|
||||
arg_minus_at = xmemdupz(arg, mark - arg);
|
||||
if (add_parents_only(revs, arg_minus_at, flags, 0)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
*mark = '^';
|
||||
}
|
||||
mark = strstr(arg, "^!");
|
||||
if (mark && !mark[2]) {
|
||||
*mark = 0;
|
||||
if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
|
||||
*mark = '^';
|
||||
arg_minus_excl = xmemdupz(arg, mark - arg);
|
||||
if (add_parents_only(revs, arg_minus_excl, flags ^ (UNINTERESTING | BOTTOM), 0))
|
||||
arg = arg_minus_excl;
|
||||
}
|
||||
mark = strstr(arg, "^-");
|
||||
if (mark) {
|
||||
@@ -2203,9 +2201,9 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
|
||||
}
|
||||
}
|
||||
|
||||
*mark = 0;
|
||||
if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
|
||||
*mark = '^';
|
||||
arg_minus_dash = xmemdupz(arg, mark - arg);
|
||||
if (add_parents_only(revs, arg_minus_dash, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
|
||||
arg = arg_minus_dash;
|
||||
}
|
||||
|
||||
local_flags = 0;
|
||||
@@ -2240,6 +2238,9 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
|
||||
|
||||
out:
|
||||
object_context_release(&oc);
|
||||
free(arg_minus_at);
|
||||
free(arg_minus_excl);
|
||||
free(arg_minus_dash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3128,6 +3129,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
|
||||
if (want_ancestry(revs))
|
||||
revs->limited = 1;
|
||||
revs->topo_order = 1;
|
||||
if (!revs->diffopt.output_format)
|
||||
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
|
||||
}
|
||||
|
||||
if (revs->topo_order && !generation_numbers_enabled(the_repository))
|
||||
|
||||
+8
-3
@@ -1895,14 +1895,19 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
|
||||
"max:%"PRIuMAX,
|
||||
(uintmax_t)opts->processes);
|
||||
|
||||
pp_init(&pp, opts, &pp_sig);
|
||||
|
||||
/*
|
||||
* Child tasks might receive input via stdin, terminating early (or not), so
|
||||
* ignore the default SIGPIPE which gets handled by each feed_pipe_fn which
|
||||
* actually writes the data to children stdin fds.
|
||||
*
|
||||
* This _must_ come after pp_init(), because it installs its own
|
||||
* SIGPIPE handler (to cleanup children), and we want to supersede
|
||||
* that.
|
||||
*/
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
|
||||
pp_init(&pp, opts, &pp_sig);
|
||||
while (1) {
|
||||
for (i = 0;
|
||||
i < spawn_cap && !pp.shutdown &&
|
||||
@@ -1928,10 +1933,10 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts)
|
||||
}
|
||||
}
|
||||
|
||||
pp_cleanup(&pp, opts);
|
||||
|
||||
sigchain_pop(SIGPIPE);
|
||||
|
||||
pp_cleanup(&pp, opts);
|
||||
|
||||
if (do_trace2)
|
||||
trace2_region_leave(tr2_category, tr2_label, NULL);
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
|
||||
return 0;
|
||||
if (data->flags & QUICK) {
|
||||
if (!odb_has_object(the_repository->objects, &graft->oid,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
return 0;
|
||||
} else if (data->flags & SEEN_ONLY) {
|
||||
struct commit *c = lookup_commit(the_repository, &graft->oid);
|
||||
@@ -528,7 +528,7 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
|
||||
ALLOC_ARRAY(info->theirs, sa->nr);
|
||||
for (size_t i = 0; i < sa->nr; i++) {
|
||||
if (odb_has_object(the_repository->objects, sa->oid + i,
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
|
||||
struct commit_graft *graft;
|
||||
graft = lookup_commit_graft(the_repository,
|
||||
&sa->oid[i]);
|
||||
@@ -567,7 +567,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
|
||||
if (i != dst)
|
||||
info->theirs[dst] = info->theirs[i];
|
||||
if (odb_has_object(the_repository->objects, oid + info->theirs[i],
|
||||
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
|
||||
ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
|
||||
dst++;
|
||||
}
|
||||
info->nr_theirs = dst;
|
||||
|
||||
@@ -675,7 +675,9 @@ test_expect_success 'match percent-encoded values' '
|
||||
test_expect_success 'match percent-encoded UTF-8 values in path' '
|
||||
test_config credential.https://example.com.useHttpPath true &&
|
||||
test_config credential.https://example.com/perú.git.helper "$HELPER" &&
|
||||
check fill <<-\EOF
|
||||
# NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs
|
||||
# that contain multibyte chars.
|
||||
check fill <<-EOF
|
||||
url=https://example.com/per%C3%BA.git
|
||||
--
|
||||
protocol=https
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user