Code clean-up.
* rs/midx-use-strvec-pushf:
midx: use strvec_pushf() for pack-objects base name
* The output format for dates "iso-strict" has been tweaked to show
a time in the Zulu timezone with "Z" suffix, instead of "+00:00".
+ * "git diff" and friends learned two extra configuration variables,
+ diff.srcPrefix and diff.dstPrefix.
+
+ * The status.showUntrackedFiles configuration variable had a name
+ that tempts users to set a Boolean value expressed in our usual
+ "false", "off", and "0", but it only took "no". This has been
+ corrected so "true" and its synonyms are taken as "normal", while
+ "false" and its synonyms are taken as "no".
+
+ * Remove an ancient and not well maintained Hg-to-git migration
+ script from contrib/.
+
+ * Hints that suggest what to do after resolving conflicts can now be
+ squelched by disabling advice.mergeConflict.
+
Performance, Internal Implementation, Development Support etc.
* The code to iterate over reflogs in the reftable has been optimized
to reduce memory allocation and deallocation.
+ * Work to support a repository that work with both SHA-1 and SHA-256
+ hash algorithms has started.
+
+ * A new fuzz target that exercises config parsing code has been
+ added.
+
+ * Fix the way recently added tests interpolate variables defined
+ outside them, and document the best practice to help future
+ developers.
+
Fixes since v2.44
-----------------
* The status.showUntrackedFiles configuration variable was
incorrectly documented to accept "false", which has been corrected.
+ * Leaks from "git restore" have been plugged.
+ (merge 2f64da0790 rj/restore-plug-leaks later to maint).
+
+ * "git bugreport --no-suffix" was not supported and instead
+ segfaulted, which has been corrected.
+ (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).
+
+ * The documentation for "%(trailers[:options])" placeholder in the
+ "--pretty" option of commands in the "git log" family has been
+ updated.
+ (merge bff85a338c bl/doc-key-val-sep-fix later to maint).
+
+ * "git checkout --conflict=bad" reported a bad conflictStyle as if it
+ were given to a configuration variable; it has been corrected to
+ report that the command line option is bad.
+ (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).
+
+ * Code clean-up in the "git log" machinery that implements custom log
+ message formatting.
+ (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).
+
+ * "git config" corrupted literal HT characters written in the
+ configuration file as part of a value, which has been corrected.
+ (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).
+
+ * A unit test for reftable code tried to enumerate all files in a
+ directory after reftable operations and expected to see nothing but
+ the files it wanted to leave there, but was fooled by .nfs* cruft
+ files left, which has been corrected.
+ (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge f0e578c69c rs/use-xstrncmpz later to maint).
(merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
(merge 781fb7b4c2 as/option-names-in-messages later to maint).
(merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
(merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
+ (merge ad538c61da jc/index-pack-fsck-levels later to maint).
+ (merge 67471bc704 ja/doc-formatting-fix later to maint).
+ (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
Git-notes and inserted automatically following the three-dash
line via `git format-patch --notes`.
+[[the-topic-summary]]
+*This is EXPERIMENTAL*.
+
+When sending a topic, you can propose a one-paragraph summary that
+should appear in the "What's cooking" report when it is picked up to
+explain the topic. If you choose to do so, please write a 2-5 line
+paragraph that will fit well in our release notes (see many bulleted
+entries in the Documentation/RelNotes/* files for examples), and make
+it the first paragraph of the cover letter. For a single-patch
+series, use the space between the three-dash line and the diffstat, as
+described earlier.
+
[[attachment]]
Do not attach the patch as a MIME attachment, compressed or not.
Do not let your e-mail client send quoted-printable. Do not let
Syntax
~~~~~~
-The syntax is fairly flexible and permissive; whitespaces are mostly
-ignored. The '#' and ';' characters begin comments to the end of line,
-blank lines are ignored.
+The syntax is fairly flexible and permissive. Whitespace characters,
+which in this context are the space character (SP) and the horizontal
+tabulation (HT), are mostly ignored. The '#' and ';' characters begin
+comments to the end of line. Blank lines are ignored.
The file consists of sections and variables. A section begins with
the name of the section in square brackets and continues until the next
The variable names are case-insensitive, allow only alphanumeric characters
and `-`, and must start with an alphabetic character.
-A line that defines a value can be continued to the next line by
-ending it with a `\`; the backslash and the end-of-line are
-stripped. Leading whitespaces after 'name =', the remainder of the
-line after the first comment character '#' or ';', and trailing
-whitespaces of the line are discarded unless they are enclosed in
-double quotes. Internal whitespaces within the value are retained
-verbatim.
-
-Inside double quotes, double quote `"` and backslash `\` characters
-must be escaped: use `\"` for `"` and `\\` for `\`.
+Whitespace characters surrounding `name`, `=` and `value` are discarded.
+Internal whitespace characters within 'value' are retained verbatim.
+Comments starting with either `#` or `;` and extending to the end of line
+are discarded. A line that defines a value can be continued to the next
+line by ending it with a backslash (`\`); the backslash and the end-of-line
+characters are discarded.
+
+If `value` needs to contain leading or trailing whitespace characters,
+it must be enclosed in double quotation marks (`"`). Inside double quotation
+marks, double quote (`"`) and backslash (`\`) characters must be escaped:
+use `\"` for `"` and `\\` for `\`.
The following escape sequences (beside `\"` and `\\`) are recognized:
`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
Shown when the user's information is guessed from the
system username and domain name, to tell the user how to
set their identity configuration.
+ mergeConflict::
+ Shown when various commands stop because of conflicts.
nestedTag::
Shown when a user attempts to recursively tag a tag object.
pushAlreadyExists::
`git diff --no-index a b`;;
compares two non-git things (1) and (2).
-diff.noprefix::
+diff.noPrefix::
If set, 'git diff' does not show any source or destination prefix.
+diff.srcPrefix::
+ If set, 'git diff' uses this source prefix. Defaults to "a/".
+
+diff.dstPrefix::
+ If set, 'git diff' uses this destination prefix. Defaults to "b/".
+
diff.relative::
If set to 'true', 'git diff' does not show changes outside of the directory
and show pathnames relative to the current directory.
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.
+extensions.compatObjectFormat::
+
+ Specify a compatitbility hash algorithm to use. The acceptable values
+ are `sha1` and `sha256`. The value specified must be different from the
+ value of extensions.objectFormat. This allows client level
+ interoperability between git repositories whose objectFormat matches
+ this compatObjectFormat. In particular when fully implemented the
+ pushes and pulls from a repository in whose objectFormat matches
+ compatObjectFormat. As well as being able to use oids encoded in
+ compatObjectFormat in addition to oids encoded with objectFormat to
+ locally specify objects.
+
extensions.refStorage::
Specify the ref storage format to use. The acceptable values are:
+
If set to true, enable `--full-name` option by default.
grep.fallbackToNoIndex::
- If set to true, fall back to git grep --no-index if git grep
+ If set to true, fall back to `git grep --no-index` if `git grep`
is executed outside of a git repository. Defaults to false.
--
+
If this variable is not specified, it defaults to 'normal'.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
This variable can be overridden with the -u|--untracked-files option
of linkgit:git-status[1] and linkgit:git-commit[1].
--default-prefix::
Use the default source and destination prefixes ("a/" and "b/").
- This is usually the default already, but may be used to override
- config such as `diff.noprefix`.
+ This overrides configuration variables such as `diff.noprefix`,
+ `diff.srcPrefix`, `diff.dstPrefix`, and `diff.mnemonicPrefix`
+ (see `git-config`(1)).
--line-prefix=<prefix>::
Prepend an additional prefix to every line of output.
destination of an explicit refspec; see `--prune`).
ifndef::git-pull[]
---recurse-submodules[=yes|on-demand|no]::
+--recurse-submodules[=(yes|on-demand|no)]::
This option controls if and under what conditions new commits of
submodules should be fetched too. When recursing through submodules,
`git fetch` always attempts to fetch "changed" submodules, that is, a
SYNOPSIS
--------
[verse]
-'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
+'git bugreport' [(-o | --output-directory) <path>]
+ [(-s | --suffix) <format> | --no-suffix]
[--diagnose[=<mode>]]
DESCRIPTION
-s <format>::
--suffix <format>::
+--no-suffix::
Specify an alternate suffix for the bugreport name, to create a file
named 'git-bugreport-<formatted-suffix>'. This should take the form of a
strftime(3) format string; the current local time will be used.
+ `--no-suffix` disables the suffix and the file is just named
+ `git-bugreport` without any disambiguation measure.
--no-diagnose::
--diagnose[=<mode>]::
- 'normal' - Shows untracked files and directories
- 'all' - Also shows individual files in untracked directories.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
The default can be changed using the status.showUntrackedFiles
configuration variable documented in linkgit:git-config[1].
--
-e::
--edit::
Opens an editor to modify the specified config file; either
- `--system`, `--global`, or repository (default).
+ `--system`, `--global`, `--local` (default), `--worktree`, or
+ `--file <config-file>`.
--[no-]includes::
Respect `include.*` directives in config files when looking up
--default <value>::
When using `--get`, and the requested variable is not found, behave as if
- <value> were the value assigned to the that variable.
+ <value> were the value assigned to that variable.
CONFIGURATION
-------------
[-f <file>] [-e] <pattern>
[--and|--or|--not|(|)|-e <pattern>...]
[--recurse-submodules] [--parent-basename <basename>]
- [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
+ [ [--[no-]exclude-standard] [--cached | --untracked | --no-index] | <tree>...]
[--] [<pathspec>...]
DESCRIPTION
Instead of searching tracked files in the working tree, search
blobs registered in the index file.
---no-index::
- Search files in the current directory that is not managed by Git.
-
--untracked::
In addition to searching in the tracked files in the working
tree, search also in untracked files.
+--no-index::
+ Search files in the current directory that is not managed by Git,
+ or by ignoring that the current directory is managed by Git. This
+ is rather similar to running the regular `grep(1)` utility with its
+ `-r` option specified, but with some additional benefits, such as
+ using pathspec patterns to limit paths; see the 'pathspec' entry
+ in linkgit:gitglossary[7] for more information.
++
+This option cannot be used together with `--cached` or `--untracked`.
+See also `grep.fallbackToNoIndex` in 'CONFIGURATION' below.
+
--no-exclude-standard::
Also search in ignored files by not honoring the `.gitignore`
mechanism. Only useful with `--untracked`.
--recurse-submodules::
Recursively search in each submodule that is active and
checked out in the repository. When used in combination with the
- <tree> option the prefix of all submodule output will be the name of
- the parent project's <tree> object. This option has no effect
- if `--no-index` is given.
+ _<tree>_ option the prefix of all submodule output will be the name of
+ the parent project's _<tree>_ object. This option cannot be used together
+ with `--untracked`, and it has no effect if `--no-index` is specified.
-a::
--text::
Use \0 as the delimiter for pathnames in the output, and print
them verbatim. Without this option, pathnames with "unusual"
characters are quoted as explained for the configuration
- variable core.quotePath (see linkgit:git-config[1]).
+ variable `core.quotePath` (see linkgit:git-config[1]).
-o::
--only-matching::
a non-zero status.
--threads <num>::
- Number of grep worker threads to use.
- See `grep.threads` in 'CONFIGURATION' for more information.
+ Number of `grep` worker threads to use. See 'NOTES ON THREADS'
+ and `grep.threads` in 'CONFIGURATION' for more information.
-f <file>::
Read patterns from <file>, one per line.
NOTES ON THREADS
----------------
-The `--threads` option (and the grep.threads configuration) will be ignored when
+The `--threads` option (and the `grep.threads` configuration) will be ignored when
`--open-files-in-pager` is used, forcing a single-threaded execution.
When grepping the object store (with `--cached` or giving tree objects), running
-with multiple threads might perform slower than single threaded if `--textconv`
-is given and there are too many text conversions. So if you experience low
-performance in this case, it might be desirable to use `--threads=1`.
+with multiple threads might perform slower than single-threaded if `--textconv`
+is given and there are too many text conversions. Thus, if low performance is
+experienced in this case, it might be desirable to use `--threads=1`.
CONFIGURATION
-------------
--------
[verse]
'git interpret-trailers' [--in-place] [--trim-empty]
- [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
+ [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]
[--parse] [<file>...]
DESCRIPTION
This means that the trimmed <key> and <value> will be separated by
`': '` (one colon followed by one space).
-For convenience, a <keyAlias> can be configured to make using `--trailer`
+For convenience, a <key-alias> can be configured to make using `--trailer`
shorter to type on the command line. This can be configured using the
-'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix
+'trailer.<key-alias>.key' configuration variable. The <keyAlias> must be a prefix
of the full <key> string, although case sensitivity does not matter. For
example, if you have
--verbose::
Pass --verbose to git-fetch and git-merge.
---[no-]recurse-submodules[=yes|on-demand|no]::
+--[no-]recurse-submodules[=(yes|on-demand|no)]::
This option controls if new commits of populated submodules should
be fetched, and if the working trees of active submodules should be
updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
include::merge-options.txt[]
-r::
---rebase[=false|true|merges|interactive]::
+--rebase[=(false|true|merges|interactive)]::
When true, rebase the current branch on top of the upstream
branch after fetching. If there is a remote-tracking branch
corresponding to the upstream branch and the upstream branch
[--onto <newbase> | --keep-base] [<upstream> [<branch>]]
'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
-'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
+'git rebase' (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
DESCRIPTION
-----------
unfortunately named tag "master"), and shows them as full
refnames (e.g. "refs/heads/master").
+--output-object-format=(sha1|sha256|storage)::
+
+ Allow oids to be input from any object format that the current
+ repository supports.
+
+ Specifying "sha1" translates if necessary and returns a sha1 oid.
+
+ Specifying "sha256" translates if necessary and returns a sha256 oid.
+
+ Specifying "storage" translates if necessary and returns an oid in
+ encoded in the storage hash algorithm.
+
Options for Objects
~~~~~~~~~~~~~~~~~~~
SYNOPSIS
--------
[verse]
-'git send-email' [<options>] <file|directory>...
+'git send-email' [<options>] (<file>|<directory>)...
'git send-email' [<options>] <format-patch-options>
'git send-email' --dump-aliases
if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),
then authentication is not attempted.
---smtp-debug=0|1::
+--smtp-debug=(0|1)::
Enable (1) or disable (0) debug output. If enabled, SMTP
commands and replies will be printed. Useful to debug TLS
connection and authentication problems.
Automating
~~~~~~~~~~
---no-[to|cc|bcc]::
+--no-to::
+--no-cc::
+--no-bcc::
Clears any list of "To:", "Cc:", "Bcc:" addresses previously
set via config.
`git update-index --untracked-cache` and `git update-index
--split-index`), Otherwise you can use `no` to have `git status`
return more quickly without showing untracked files.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
The default can be changed using the status.showUntrackedFiles
configuration variable documented in linkgit:git-config[1].
`GIT_PROTOCOL`::
For internal use only. Used in handshaking the wire protocol.
Contains a colon ':' separated list of keys with optional values
- 'key[=value]'. Presence of unknown keys and values must be
+ '<key>[=<value>]'. Presence of unknown keys and values must be
ignored.
+
Note that servers may need to be configured to allow this variable to
'option depth' <depth>::
Deepens the history of a shallow repository.
-'option deepen-since <timestamp>::
+'option deepen-since' <timestamp>::
Deepens the history of a shallow repository based on time.
-'option deepen-not <ref>::
+'option deepen-not' <ref>::
Deepens the history of a shallow repository excluding ref.
Multiple options add up.
-'option deepen-relative {'true'|'false'}::
+'option deepen-relative' {'true'|'false'}::
Deepens the history of a shallow repository relative to
current boundary. Only valid when used with "option depth".
transaction. If successful, all refs will be updated, or none will. If the
remote side does not support this capability, the push will fail.
-'option object-format' {'true'|algorithm}::
- If 'true', indicate that the caller wants hash algorithm information
+'option object-format true'::
+ Indicate that the caller wants hash algorithm information
to be passed back from the remote. This mode is used when fetching
refs.
-+
-If set to an algorithm, indicate that the caller wants to interact with
-the remote side using that algorithm.
SEE ALSO
--------
if test -f "$allowed_users_file"
then
- rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
+ rc=$(grep -Ev '^(#|$)' $allowed_users_file |
while read heads user_patterns
do
# does this rule apply to us?
if test -f "$allowed_groups_file"
then
- rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
+ rc=$(grep -Ev '^(#|$)' $allowed_groups_file |
while read heads group_patterns
do
# does this rule apply to us?
`Reviewed-by`.
** 'only[=<bool>]': select whether non-trailer lines from the trailer
block should be included.
-** 'separator=<sep>': specify a separator inserted between trailer
- lines. When this option is not given each trailer line is
- terminated with a line feed character. The string <sep> may contain
+** 'separator=<sep>': specify the separator inserted between trailer
+ lines. Defaults to a line feed character. The string <sep> may contain
the literal formatting codes described above. To use comma as
separator one must use `%x2C` as it would otherwise be parsed as
next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
`%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
** 'keyonly[=<bool>]': only show the key part of the trailer.
** 'valueonly[=<bool>]': only show the value part of the trailer.
-** 'key_value_separator=<sep>': specify a separator inserted between
- trailer lines. When this option is not given each trailer key-value
- pair is separated by ": ". Otherwise it shares the same semantics
- as 'separator=<sep>' above.
+** 'key_value_separator=<sep>': specify the separator inserted between
+ the key and value of each trailer. Defaults to ": ". Otherwise it
+ shares the same semantics as 'separator=<sep>' above.
NOTE: Some placeholders may depend on other options given to the
revision traversal engine. For example, the `%g*` reflog options will
With `--pretty` format other than `oneline` and `reference` (for obvious reasons),
this causes the output to have two extra lines of information
taken from the reflog. The reflog designator in the output may be shown
-as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
-reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+as `ref@{<Nth>}` (where _<Nth>_ is the reverse-chronological index in the
+reflog) or as `ref@{<timestamp>}` (with the _<timestamp>_ for that entry),
depending on a few rules:
+
--
-1. If the starting point is specified as `ref@{Nth}`, show the index
+1. If the starting point is specified as `ref@{<Nth>}`, show the index
format.
+
2. If the starting point was specified as `ref@{now}`, show the
# runs in the future.
FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
FUZZ_OBJS += oss-fuzz/fuzz-date.o
FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
TEST_BUILTINS_OBJS += test-crontab.o
TEST_BUILTINS_OBJS += test-csprng.o
TEST_BUILTINS_OBJS += test-date.o
+TEST_BUILTINS_OBJS += test-delete-gpgsig.o
TEST_BUILTINS_OBJS += test-delta.o
TEST_BUILTINS_OBJS += test-dir-iterator.o
TEST_BUILTINS_OBJS += test-drop-caches.o
LIB_OBJS += list-objects.o
LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
+LIB_OBJS += loose.o
LIB_OBJS += ls-refs.o
LIB_OBJS += mailinfo.o
LIB_OBJS += mailmap.o
LIB_OBJS += notes-merge.o
LIB_OBJS += notes-utils.o
LIB_OBJS += notes.o
+LIB_OBJS += object-file-convert.o
LIB_OBJS += object-file.o
LIB_OBJS += object-name.o
LIB_OBJS += object.o
[ADVICE_GRAFT_FILE_DEPRECATED] = { "graftFileDeprecated" },
[ADVICE_IGNORED_HOOK] = { "ignoredHook" },
[ADVICE_IMPLICIT_IDENTITY] = { "implicitIdentity" },
+ [ADVICE_MERGE_CONFLICT] = { "mergeConflict" },
[ADVICE_NESTED_TAG] = { "nestedTag" },
[ADVICE_OBJECT_NAME_WARNING] = { "objectNameWarning" },
[ADVICE_PUSH_ALREADY_EXISTS] = { "pushAlreadyExists" },
ADVICE_GRAFT_FILE_DEPRECATED,
ADVICE_IGNORED_HOOK,
ADVICE_IMPLICIT_IDENTITY,
+ ADVICE_MERGE_CONFLICT,
ADVICE_NESTED_TAG,
ADVICE_OBJECT_NAME_WARNING,
ADVICE_PUSH_ALREADY_EXISTS,
opts.src_index = args->repo->index;
opts.dst_index = args->repo->index;
opts.fn = oneway_merge;
- init_tree_desc(&t, args->tree->buffer, args->tree->size);
+ init_tree_desc(&t, &args->tree->object.oid,
+ args->tree->buffer, args->tree->size);
if (unpack_trees(1, &t, &opts))
return -1;
git_attr_set_direction(GIT_ATTR_INDEX);
static void NORETURN die_user_resolve(const struct am_state *state)
{
if (state->resolvemsg) {
- printf_ln("%s", state->resolvemsg);
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", state->resolvemsg);
} else {
const char *cmdline = state->interactive ? "git am -i" : "git am";
+ struct strbuf sb = STRBUF_INIT;
- printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
- printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+ strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\".\n"), cmdline);
+ strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead.\n"), cmdline);
if (advice_enabled(ADVICE_AM_WORK_DIR) &&
is_empty_or_missing_file(am_path(state, "patch")) &&
!repo_index_has_changes(the_repository, NULL, NULL))
- printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+ strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\".\n"), cmdline);
- printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+ strbuf_addf(&sb, _("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", sb.buf);
+ strbuf_release(&sb);
}
exit(128);
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = twoway_merge;
- init_tree_desc(&t[0], head->buffer, head->size);
- init_tree_desc(&t[1], remote->buffer, remote->size);
+ init_tree_desc(&t[0], &head->object.oid, head->buffer, head->size);
+ init_tree_desc(&t[1], &remote->object.oid, remote->buffer, remote->size);
if (unpack_trees(2, t, &opts)) {
rollback_lock_file(&lock_file);
opts.dst_index = &the_index;
opts.merge = 1;
opts.fn = oneway_merge;
- init_tree_desc(&t[0], tree->buffer, tree->size);
+ init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, t, &opts)) {
rollback_lock_file(&lock_file);
}
static const char * const bugreport_usage[] = {
- N_("git bugreport [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
+ N_("git bugreport [(-o | --output-directory) <path>]\n"
+ " [(-s | --suffix) <format> | --no-suffix]\n"
" [--diagnose[=<mode>]]"),
NULL
};
strbuf_complete(&report_path, '/');
output_path_len = report_path.len;
- strbuf_addstr(&report_path, "git-bugreport-");
- strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+ strbuf_addstr(&report_path, "git-bugreport");
+ if (option_suffix) {
+ strbuf_addch(&report_path, '-');
+ strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+ }
strbuf_addstr(&report_path, ".txt");
switch (safe_create_leading_directories(report_path.buf)) {
struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
- unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE;
+ unsigned get_oid_flags =
+ GET_OID_RECORD_PATH |
+ GET_OID_ONLY_TO_DIE |
+ GET_OID_HASH_ANY;
const char *path = force_path;
const int opt_cw = (opt == 'c' || opt == 'w');
if (!path && opt_cw)
die(_("unable to read %s"), oid_to_hex(&oid));
if (!skip_prefix(buffer, "object ", &target) ||
- get_oid_hex(target, &blob_oid))
+ get_oid_hex_algop(target, &blob_oid,
+ &hash_algos[oid.algo]))
die("%s not a valid tag", oid_to_hex(&oid));
free(buffer);
} else
struct expand_data *data)
{
struct object_context ctx;
- int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
+ int flags =
+ GET_OID_HASH_ANY |
+ (opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0);
enum get_oid_result result;
result = get_oid_with_context(the_repository, obj_name,
int new_branch_log;
enum branch_track track;
struct diff_options diff_options;
- char *conflict_style;
+ int conflict_style;
int branch_exists;
const char *prefix;
struct tree *source_tree;
};
+#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 }
+
struct branch_info {
char *name; /* The short name used */
char *path; /* The full name of a real branch */
}
static int checkout_merged(int pos, const struct checkout *state,
- int *nr_checkouts, struct mem_pool *ce_mem_pool)
+ int *nr_checkouts, struct mem_pool *ce_mem_pool,
+ int conflict_style)
{
struct cache_entry *ce = the_index.cache[pos];
const char *path = ce->name;
mmbuffer_t result_buf;
struct object_id threeway[3];
unsigned mode = 0;
- struct ll_merge_options ll_opts;
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
int renormalize = 0;
memset(threeway, 0, sizeof(threeway));
read_mmblob(&ours, &threeway[1]);
read_mmblob(&theirs, &threeway[2]);
- memset(&ll_opts, 0, sizeof(ll_opts));
git_config_get_bool("merge.renormalize", &renormalize);
ll_opts.renormalize = renormalize;
+ ll_opts.conflict_style = conflict_style;
merge_status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs",
state->istate, &ll_opts);
else if (opts->merge)
errs |= checkout_merged(pos, &state,
&nr_unmerged,
- &ce_mem_pool);
+ &ce_mem_pool,
+ opts->conflict_style);
pos = skip_same_name(ce, pos) - 1;
}
}
NULL);
if (parse_tree(tree) < 0)
return 128;
- init_tree_desc(&tree_desc, tree->buffer, tree->size);
+ init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size);
switch (unpack_trees(1, &tree_desc, &opts)) {
case -2:
*writeout_error = 1;
die(_("unable to parse commit %s"),
oid_to_hex(old_commit_oid));
- init_tree_desc(&trees[0], tree->buffer, tree->size);
+ init_tree_desc(&trees[0], &tree->object.oid,
+ tree->buffer, tree->size);
if (parse_tree(new_tree) < 0)
exit(128);
tree = new_tree;
- init_tree_desc(&trees[1], tree->buffer, tree->size);
+ init_tree_desc(&trees[1], &tree->object.oid,
+ tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
clear_unpack_trees_porcelain(&topts);
}
o.branch1 = new_branch_info->name;
o.branch2 = "local";
+ o.conflict_style = opts->conflict_style;
ret = merge_trees(&o,
new_tree,
work,
return switch_branches(opts, new_branch_info);
}
+static int parse_opt_conflict(const struct option *o, const char *arg, int unset)
+{
+ struct checkout_opts *opts = o->value;
+
+ if (unset) {
+ opts->conflict_style = -1;
+ return 0;
+ }
+ opts->conflict_style = parse_conflict_style_name(arg);
+ if (opts->conflict_style < 0)
+ return error(_("unknown conflict style '%s'"), arg);
+ /* --conflict overrides a previous --no-merge */
+ if (!opts->merge)
+ opts->merge = -1;
+
+ return 0;
+}
+
static struct option *add_common_options(struct checkout_opts *opts,
struct option *prevopts)
{
PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
- OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
- N_("conflict style (merge, diff3, or zdiff3)")),
+ OPT_CALLBACK(0, "conflict", opts, N_("style"),
+ N_("conflict style (merge, diff3, or zdiff3)"),
+ parse_opt_conflict),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
- const char * const usagestr[],
- struct branch_info *new_branch_info)
+ const char * const usagestr[])
{
int parseopt_flags = 0;
+ struct branch_info new_branch_info = { 0 };
+ int ret;
opts->overwrite_ignore = 1;
opts->prefix = prefix;
opts->show_progress = isatty(2);
}
- if (opts->conflict_style) {
- struct key_value_info kvi = KVI_INIT;
- struct config_context ctx = {
- .kvi = &kvi,
- };
- opts->merge = 1; /* implied */
- git_xmerge_config("merge.conflictstyle", opts->conflict_style,
- &ctx, NULL);
- }
+ /* --conflicts implies --merge */
+ if (opts->merge == -1)
+ opts->merge = opts->conflict_style >= 0;
+
if (opts->force) {
opts->discard_changes = 1;
opts->ignore_unmerged_opt = "--force";
opts->track == BRANCH_TRACK_UNSPECIFIED &&
!opts->new_branch;
int n = parse_branchname_arg(argc, argv, dwim_ok,
- new_branch_info, opts, &rev);
+ &new_branch_info, opts, &rev);
argv += n;
argc -= n;
} else if (!opts->accept_ref && opts->from_treeish) {
if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
die(_("could not resolve %s"), opts->from_treeish);
- setup_new_branch_info_and_source_tree(new_branch_info,
+ setup_new_branch_info_and_source_tree(&new_branch_info,
opts, &rev,
opts->from_treeish);
* Try to give more helpful suggestion.
* new_branch && argc > 1 will be caught later.
*/
- if (opts->new_branch && argc == 1 && !new_branch_info->commit)
+ if (opts->new_branch && argc == 1 && !new_branch_info.commit)
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
argv[0], opts->new_branch);
}
if (opts->patch_mode || opts->pathspec.nr)
- return checkout_paths(opts, new_branch_info);
+ ret = checkout_paths(opts, &new_branch_info);
else
- return checkout_branch(opts, new_branch_info);
+ ret = checkout_branch(opts, &new_branch_info);
+
+ branch_info_release(&new_branch_info);
+ clear_pathspec(&opts->pathspec);
+ free(opts->pathspec_from_file);
+ free(options);
+
+ return ret;
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
- struct checkout_opts opts;
+ struct checkout_opts opts = CHECKOUT_OPTS_INIT;
struct option *options;
struct option checkout_options[] = {
OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
OPT_END()
};
- int ret;
- struct branch_info new_branch_info = { 0 };
- memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
opts.switch_branch_doing_nothing_is_ok = 1;
opts.only_merge_on_switching_branches = 0;
options = add_common_switch_branch_options(&opts, options);
options = add_checkout_path_options(&opts, options);
- ret = checkout_main(argc, argv, prefix, &opts,
- options, checkout_usage, &new_branch_info);
- branch_info_release(&new_branch_info);
- clear_pathspec(&opts.pathspec);
- free(opts.pathspec_from_file);
- FREE_AND_NULL(options);
- return ret;
+ return checkout_main(argc, argv, prefix, &opts, options,
+ checkout_usage);
}
int cmd_switch(int argc, const char **argv, const char *prefix)
{
- struct checkout_opts opts;
+ struct checkout_opts opts = CHECKOUT_OPTS_INIT;
struct option *options = NULL;
struct option switch_options[] = {
OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
N_("throw away local modifications")),
OPT_END()
};
- int ret;
- struct branch_info new_branch_info = { 0 };
- memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
opts.accept_ref = 1;
opts.accept_pathspec = 0;
cb_option = 'c';
- ret = checkout_main(argc, argv, prefix, &opts,
- options, switch_branch_usage, &new_branch_info);
- branch_info_release(&new_branch_info);
- FREE_AND_NULL(options);
- return ret;
+ return checkout_main(argc, argv, prefix, &opts, options,
+ switch_branch_usage);
}
int cmd_restore(int argc, const char **argv, const char *prefix)
{
- struct checkout_opts opts;
+ struct checkout_opts opts = CHECKOUT_OPTS_INIT;
struct option *options;
struct option restore_options[] = {
OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
OPT_END()
};
- int ret;
- struct branch_info new_branch_info = { 0 };
- memset(&opts, 0, sizeof(opts));
opts.accept_ref = 0;
opts.accept_pathspec = 1;
opts.empty_pathspec_ok = 0;
options = add_common_options(&opts, options);
options = add_checkout_path_options(&opts, options);
- ret = checkout_main(argc, argv, prefix, &opts,
- options, restore_usage, &new_branch_info);
- branch_info_release(&new_branch_info);
- FREE_AND_NULL(options);
- return ret;
+ return checkout_main(argc, argv, prefix, &opts, options,
+ restore_usage);
}
die(_("unable to parse commit %s"), oid_to_hex(&oid));
if (parse_tree(tree) < 0)
exit(128);
- init_tree_desc(&t, tree->buffer, tree->size);
+ init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts) < 0)
die(_("unable to checkout working tree"));
die(_("failed to unpack HEAD tree object"));
if (parse_tree(tree) < 0)
exit(128);
- init_tree_desc(&t, tree->buffer, tree->size);
+ init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts))
exit(128); /* We've already reported the error, finish dying */
}
die(_("Invalid ignored mode '%s'"), ignored_arg);
}
-static void handle_untracked_files_arg(struct wt_status *s)
+static enum untracked_status_type parse_untracked_setting_name(const char *u)
{
- if (!untracked_files_arg)
- ; /* default already initialized */
- else if (!strcmp(untracked_files_arg, "no"))
- s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
- else if (!strcmp(untracked_files_arg, "normal"))
- s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
- else if (!strcmp(untracked_files_arg, "all"))
- s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
/*
* Please update $__git_untracked_file_modes in
* git-completion.bash when you add new options
*/
+ switch (git_parse_maybe_bool(u)) {
+ case 0:
+ u = "no";
+ break;
+ case 1:
+ u = "normal";
+ break;
+ default:
+ break;
+ }
+
+ if (!strcmp(u, "no"))
+ return SHOW_NO_UNTRACKED_FILES;
+ else if (!strcmp(u, "normal"))
+ return SHOW_NORMAL_UNTRACKED_FILES;
+ else if (!strcmp(u, "all"))
+ return SHOW_ALL_UNTRACKED_FILES;
else
- die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
+ return SHOW_UNTRACKED_FILES_ERROR;
+}
+
+static void handle_untracked_files_arg(struct wt_status *s)
+{
+ enum untracked_status_type u;
+
+ if (!untracked_files_arg)
+ return; /* default already initialized */
+
+ u = parse_untracked_setting_name(untracked_files_arg);
+ if (u == SHOW_UNTRACKED_FILES_ERROR)
+ die(_("Invalid untracked files mode '%s'"),
+ untracked_files_arg);
+ s->show_untracked_files = u;
}
static const char *read_commit_message(const char *name)
return 0;
}
if (!strcmp(k, "status.showuntrackedfiles")) {
- if (!v)
- return config_error_nonbool(k);
- else if (!strcmp(v, "no"))
- s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
- else if (!strcmp(v, "normal"))
- s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
- else if (!strcmp(v, "all"))
- s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
- else
+ enum untracked_status_type u;
+
+ u = parse_untracked_setting_name(v);
+ if (u == SHOW_UNTRACKED_FILES_ERROR)
return error(_("Invalid untracked files mode '%s'"), v);
+ s->show_untracked_files = u;
return 0;
}
if (!strcmp(k, "diff.renamelimit")) {
return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep);
}
-static const char *get_mode(const char *str, uint16_t *modep)
-{
- unsigned char c;
- uint16_t mode = 0;
-
- while ((c = *str++) != ' ') {
- if (c < '0' || c > '7')
- return NULL;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- return str;
-}
-
static void load_tree(struct tree_entry *root)
{
struct object_id *oid = &root->versions[1].oid;
t->entries[t->entry_count++] = e;
e->tree = NULL;
- c = get_mode(c, &e->versions[1].mode);
+ c = parse_mode(c, &e->versions[1].mode);
if (!c)
die("Corrupt mode in %s", oid_to_hex(oid));
e->versions[0].mode = e->versions[1].mode;
struct object_id oid;
uint16_t mode, inline_data = 0;
- p = get_mode(p, &mode);
+ p = parse_mode(p, &mode);
if (!p)
die("Corrupt mode: %s", command_buf.buf);
switch (mode) {
strbuf_addstr(&base, filename);
strbuf_addch(&base, '/');
- init_tree_desc(&tree, data, size);
+ init_tree_desc(&tree, oid, data, size);
hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
object_type == OBJ_COMMIT);
strbuf_release(&base);
&type, &size);
if (!data)
die(_("unable to read tree %s"), oid_to_hex(&ce->oid));
- init_tree_desc(&tree, data, size);
+ init_tree_desc(&tree, &ce->oid, data, size);
hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
strbuf_setlen(&name, name_base_len);
oid_to_hex(&entry.oid));
strbuf_addch(base, '/');
- init_tree_desc(&sub, data, size);
+ init_tree_desc(&sub, &entry.oid, data, size);
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
check_attr);
free(data);
strbuf_add(&base, name, len);
strbuf_addch(&base, ':');
}
- init_tree_desc(&tree, data, size);
+ init_tree_desc(&tree, &obj->oid, data, size);
hit = grep_tree(opt, pathspec, &tree, &base, base.len,
obj->type == OBJ_COMMIT);
strbuf_release(&base);
static const char * const git_interpret_trailers_usage[] = {
N_("git interpret-trailers [--in-place] [--trim-empty]\n"
- " [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+ " [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
" [--parse] [<file>...]"),
NULL
};
subject = subject_sb.buf;
do_pp:
- pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+ pp_email_subject(pp, &subject, sb, encoding, need_8bit_cte);
pp_remainder(pp, &body, sb, 0);
strbuf_release(&description_sb);
pp.fmt = CMIT_FMT_EMAIL;
pp.date_mode.type = DATE_RFC2822;
pp.rev = rev;
- pp.print_email_subject = 1;
pp.encode_email_headers = rev->encode_email_headers;
pp_user_info(&pp, NULL, &sb, committer, encoding);
prepare_cover_text(&pp, description_file, branch_name, &sb,
encoding, need_8bit_cte);
fprintf(rev->diffopt.file, "%s\n", sb.buf);
+ free(pp.after_subject);
strbuf_release(&sb);
shortlog_init(&log);
OPT_END()
};
struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+ struct object_context obj_context;
int ret;
git_config(git_default_config, NULL);
ls_tree_usage, ls_tree_options);
if (argc < 1)
usage_with_options(ls_tree_usage, ls_tree_options);
- if (repo_get_oid(the_repository, argv[0], &oid))
+ if (get_oid_with_context(the_repository, argv[0],
+ GET_OID_HASH_ANY, &oid,
+ &obj_context))
die("Not a valid object name %s", argv[0]);
/*
cache_tree_free(&the_index.cache_tree);
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
- init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+ init_tree_desc(t+i, &trees[i]->object.oid,
+ trees[i]->buffer, trees[i]->size);
}
if (unpack_trees(nr_trees, t, &opts))
return -1;
tree = pbase_tree_get(&entry.oid);
if (!tree)
return;
- init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+ init_tree_desc(&sub, &tree->oid,
+ tree->tree_data, tree->tree_size);
add_pbase_object(&sub, down, downlen, fullname);
pbase_tree_put(tree);
}
else {
struct tree_desc tree;
- init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+ init_tree_desc(&tree, &it->pcache.oid,
+ it->pcache.tree_data, it->pcache.tree_size);
add_pbase_object(&tree, name, cmplen, name);
}
}
struct tree *tree = trees[i];
if (parse_tree(tree) < 0)
return 128;
- init_tree_desc(t+i, tree->buffer, tree->size);
+ init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size);
}
if (unpack_trees(nr_trees, t, &opts))
return 128;
status = error_errno(_("could not open '%s' for writing"),
rebased_patches);
free(rebased_patches);
- strvec_clear(&am.args);
+ child_process_clear(&am);
return status;
}
struct reset_head_opts ropts = { 0 };
unlink(rebased_patches);
free(rebased_patches);
- strvec_clear(&am.args);
+ child_process_clear(&am);
ropts.oid = &opts->orig_head->object.oid;
ropts.branch = opts->head_name;
status = error_errno(_("could not open '%s' for reading"),
rebased_patches);
free(rebased_patches);
- strvec_clear(&am.args);
+ child_process_clear(&am);
return status;
}
ctx.fmt = revs->commit_format;
ctx.output_encoding = get_log_output_encoding();
ctx.color = revs->diffopt.use_color;
+ ctx.rev = revs;
pretty_print_commit(&ctx, commit, &buf);
if (buf.len) {
if (revs->commit_format != CMIT_FMT_ONELINE)
#include "submodule.h"
#include "commit-reach.h"
#include "shallow.h"
+#include "object-file-convert.h"
#define DO_REVS 1
#define DO_NOREV 2
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
{
int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+ const struct git_hash_algo *output_algo = NULL;
+ const struct git_hash_algo *compat = NULL;
int did_repo_setup = 0;
int has_dashdash = 0;
int output_prefix = 0;
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
+ compat = the_repository->compat_hash_algo;
}
if (!strcmp(arg, "--")) {
flags |= GET_OID_QUIETLY;
continue;
}
+ if (opt_with_value(arg, "--output-object-format", &arg)) {
+ if (!arg)
+ die(_("no object format specified"));
+ if (!strcmp(arg, the_hash_algo->name) ||
+ !strcmp(arg, "storage")) {
+ flags |= GET_OID_HASH_ANY;
+ output_algo = the_hash_algo;
+ continue;
+ }
+ else if (compat && !strcmp(arg, compat->name)) {
+ flags |= GET_OID_HASH_ANY;
+ output_algo = compat;
+ continue;
+ }
+ else die(_("unsupported object format: %s"), arg);
+ }
if (opt_with_value(arg, "--short", &arg)) {
filter &= ~(DO_FLAGS|DO_NOREV);
verify = 1;
continue;
}
if (skip_prefix(arg, "--disambiguate=", &arg)) {
- repo_for_each_abbrev(the_repository, arg,
+ repo_for_each_abbrev(the_repository, arg, the_hash_algo,
show_abbrev, NULL);
continue;
}
}
if (!get_oid_with_context(the_repository, name,
flags, &oid, &unused)) {
+ if (output_algo)
+ repo_oid_to_algop(the_repository, &oid,
+ output_algo, &oid);
if (verify)
revs_count++;
else
ctx.fmt = CMIT_FMT_USERFORMAT;
ctx.abbrev = log->abbrev;
- ctx.print_email_subject = 1;
ctx.date_mode = log->date_mode;
ctx.output_encoding = get_log_output_encoding();
if (parse_tree(tree))
return -1;
- init_tree_desc(t, tree->buffer, tree->size);
+ init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
opts.head_idx = 1;
opts.src_index = &the_index;
tree[i] = parse_tree_indirect(oid[i]);
if (parse_tree(tree[i]) < 0)
die(_("failed to parse tree"));
- init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
+ init_tree_desc(&tree_desc[i], &tree[i]->object.oid,
+ tree[i]->buffer, tree[i]->size);
}
unpack_tree_opt.head_idx = -1;
#include "ref-filter.h"
#include "date.h"
#include "write-or-die.h"
+#include "object-file-convert.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
return 0;
}
-static int do_sign(struct strbuf *buffer)
+static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
+ struct object_id *compat_oid_buf)
{
- return sign_buffer(buffer, buffer, get_signing_key()) ? -1 : 0;
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+ struct strbuf compat_buf = STRBUF_INIT;
+ const char *keyid = get_signing_key();
+ int ret = -1;
+
+ if (sign_buffer(buffer, &sig, keyid))
+ return -1;
+
+ if (compat) {
+ const struct git_hash_algo *algo = the_repository->hash_algo;
+
+ if (convert_object_file(&compat_buf, algo, compat,
+ buffer->buf, buffer->len, OBJ_TAG, 1))
+ goto out;
+ if (sign_buffer(&compat_buf, &compat_sig, keyid))
+ goto out;
+ add_header_signature(&compat_buf, &sig, algo);
+ strbuf_addbuf(&compat_buf, &compat_sig);
+ hash_object_file(compat, compat_buf.buf, compat_buf.len,
+ OBJ_TAG, compat_oid_buf);
+ *compat_oid = compat_oid_buf;
+ }
+
+ if (compat_sig.len)
+ add_header_signature(buffer, &compat_sig, compat);
+
+ strbuf_addbuf(buffer, &sig);
+ ret = 0;
+out:
+ strbuf_release(&sig);
+ strbuf_release(&compat_sig);
+ strbuf_release(&compat_buf);
+ return ret;
}
static const char tag_template[] =
static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
{
- if (sign && do_sign(buf) < 0)
+ struct object_id *compat_oid = NULL, compat_oid_buf;
+ if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0)
return error(_("unable to sign the tag"));
- if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
+ if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result,
+ compat_oid, 0) < 0)
return error(_("unable to write tag file"));
return 0;
}
hash_object_file(the_hash_algo, buffer.buf, buffer.len,
OBJ_TREE, &it->oid);
} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
- &it->oid, flags & WRITE_TREE_SILENT
+ &it->oid, NULL, flags & WRITE_TREE_SILENT
? HASH_SILENT : 0)) {
strbuf_release(&buffer);
return -1;
oidcpy(&it->oid, &tree->object.oid);
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
cnt = 0;
while (tree_entry(&desc, &entry)) {
if (!S_ISDIR(entry.mode))
LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
fuzz-all
-for fuzzer in commit-graph date pack-headers pack-idx ; do
+for fuzzer in commit-graph config date pack-headers pack-idx ; do
begin_group "fuzz-$fuzzer"
./oss-fuzz/fuzz-$fuzzer -verbosity=0 -runs=1 || exit 1
end_group "fuzz-$fuzzer"
#include "tree.h"
#include "hook.h"
#include "parse.h"
+#include "object-file-convert.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
"gpgsig-sha256",
};
-int sign_with_header(struct strbuf *buf, const char *keyid)
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
{
- struct strbuf sig = STRBUF_INIT;
int inspos, copypos;
const char *eoh;
- const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+ const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
int gpg_sig_header_len = strlen(gpg_sig_header);
/* find the end of the header */
else
inspos = eoh - buf->buf + 1;
- if (!keyid || !*keyid)
- keyid = get_signing_key();
- if (sign_buffer(buf, &sig, keyid)) {
- strbuf_release(&sig);
- return -1;
- }
-
- for (copypos = 0; sig.buf[copypos]; ) {
- const char *bol = sig.buf + copypos;
+ for (copypos = 0; sig->buf[copypos]; ) {
+ const char *bol = sig->buf + copypos;
const char *eol = strchrnul(bol, '\n');
int len = (eol - bol) + !!*eol;
inspos += len;
copypos += len;
}
- strbuf_release(&sig);
return 0;
}
-
+static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
+{
+ if (!keyid || !*keyid)
+ keyid = get_signing_key();
+ if (sign_buffer(buf, sig, keyid))
+ return -1;
+ return 0;
+}
int parse_signed_commit(const struct commit *commit,
struct strbuf *payload, struct strbuf *signature,
}
}
+static int convert_commit_extra_headers(struct commit_extra_header *orig,
+ struct commit_extra_header **result)
+{
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ const struct git_hash_algo *algo = the_repository->hash_algo;
+ struct commit_extra_header *extra = NULL, **tail = &extra;
+ struct strbuf out = STRBUF_INIT;
+ while (orig) {
+ struct commit_extra_header *new;
+ CALLOC_ARRAY(new, 1);
+ if (!strcmp(orig->key, "mergetag")) {
+ if (convert_object_file(&out, algo, compat,
+ orig->value, orig->len,
+ OBJ_TAG, 1)) {
+ free(new);
+ free_commit_extra_headers(extra);
+ return -1;
+ }
+ new->key = xstrdup("mergetag");
+ new->value = strbuf_detach(&out, &new->len);
+ } else {
+ new->key = xstrdup(orig->key);
+ new->len = orig->len;
+ new->value = xmemdupz(orig->value, orig->len);
+ }
+ *tail = new;
+ tail = &new->next;
+ orig = orig->next;
+ }
+ *result = extra;
+ return 0;
+}
+
static void add_extra_header(struct strbuf *buffer,
struct commit_extra_header *extra)
{
"You may want to amend it after fixing the message, or set the config\n"
"variable i18n.commitEncoding to the encoding your project uses.\n");
-int commit_tree_extended(const char *msg, size_t msg_len,
- const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
- const char *author, const char *committer,
- const char *sign_commit,
- struct commit_extra_header *extra)
+static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
+ const struct object_id *tree,
+ const struct object_id *parents, size_t parents_len,
+ const char *author, const char *committer,
+ struct commit_extra_header *extra)
{
- int result;
int encoding_is_utf8;
- struct strbuf buffer;
-
- assert_oid_type(tree, OBJ_TREE);
-
- if (memchr(msg, '\0', msg_len))
- return error("a NUL byte in commit log message not allowed.");
+ size_t i;
/* Not having i18n.commitencoding is the same as having utf-8 */
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
- strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
+ strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
+ strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
/*
* NOTE! This ordering means that the same exact tree merged with a
* different order of parents will be a _different_ changeset even
* if everything else stays the same.
*/
- while (parents) {
- struct commit *parent = pop_commit(&parents);
- strbuf_addf(&buffer, "parent %s\n",
- oid_to_hex(&parent->object.oid));
- }
+ for (i = 0; i < parents_len; i++)
+ strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
/* Person/date information */
if (!author)
author = git_author_info(IDENT_STRICT);
- strbuf_addf(&buffer, "author %s\n", author);
+ strbuf_addf(buffer, "author %s\n", author);
if (!committer)
committer = git_committer_info(IDENT_STRICT);
- strbuf_addf(&buffer, "committer %s\n", committer);
+ strbuf_addf(buffer, "committer %s\n", committer);
if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+ strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
while (extra) {
- add_extra_header(&buffer, extra);
+ add_extra_header(buffer, extra);
extra = extra->next;
}
- strbuf_addch(&buffer, '\n');
+ strbuf_addch(buffer, '\n');
/* And add the comment */
- strbuf_add(&buffer, msg, msg_len);
+ strbuf_add(buffer, msg, msg_len);
+}
- /* And check the encoding */
- if (encoding_is_utf8 && !verify_utf8(&buffer))
- fprintf(stderr, _(commit_utf8_warn));
+int commit_tree_extended(const char *msg, size_t msg_len,
+ const struct object_id *tree,
+ struct commit_list *parents, struct object_id *ret,
+ const char *author, const char *committer,
+ const char *sign_commit,
+ struct commit_extra_header *extra)
+{
+ struct repository *r = the_repository;
+ int result = 0;
+ int encoding_is_utf8;
+ struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
+ struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+ struct object_id *parent_buf = NULL, *compat_oid = NULL;
+ struct object_id compat_oid_buf;
+ size_t i, nparents;
+
+ /* Not having i18n.commitencoding is the same as having utf-8 */
+ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+ assert_oid_type(tree, OBJ_TREE);
- if (sign_commit && sign_with_header(&buffer, sign_commit)) {
+ if (memchr(msg, '\0', msg_len))
+ return error("a NUL byte in commit log message not allowed.");
+
+ nparents = commit_list_count(parents);
+ CALLOC_ARRAY(parent_buf, nparents);
+ i = 0;
+ while (parents) {
+ struct commit *parent = pop_commit(&parents);
+ oidcpy(&parent_buf[i++], &parent->object.oid);
+ }
+
+ write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
+ if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
result = -1;
goto out;
}
+ if (r->compat_hash_algo) {
+ struct commit_extra_header *compat_extra = NULL;
+ struct object_id mapped_tree;
+ struct object_id *mapped_parents;
+
+ CALLOC_ARRAY(mapped_parents, nparents);
- result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
+ if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ for (i = 0; i < nparents; i++)
+ if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ if (convert_commit_extra_headers(extra, &compat_extra)) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
+ mapped_parents, nparents, author, committer, compat_extra);
+ free_commit_extra_headers(compat_extra);
+ free(mapped_parents);
+
+ if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
+ result = -1;
+ goto out;
+ }
+ }
+
+ if (sign_commit) {
+ struct sig_pairs {
+ struct strbuf *sig;
+ const struct git_hash_algo *algo;
+ } bufs [2] = {
+ { &compat_sig, r->compat_hash_algo },
+ { &sig, r->hash_algo },
+ };
+ int i;
+
+ /*
+ * We write algorithms in the order they were implemented in
+ * Git to produce a stable hash when multiple algorithms are
+ * used.
+ */
+ if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
+ SWAP(bufs[0], bufs[1]);
+
+ /*
+ * We traverse each algorithm in order, and apply the signature
+ * to each buffer.
+ */
+ for (i = 0; i < ARRAY_SIZE(bufs); i++) {
+ if (!bufs[i].algo)
+ continue;
+ add_header_signature(&buffer, bufs[i].sig, bufs[i].algo);
+ if (r->compat_hash_algo)
+ add_header_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
+ }
+ }
+
+ /* And check the encoding. */
+ if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
+ fprintf(stderr, _(commit_utf8_warn));
+
+ if (r->compat_hash_algo) {
+ hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
+ OBJ_COMMIT, &compat_oid_buf);
+ compat_oid = &compat_oid_buf;
+ }
+
+ result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
+ ret, compat_oid, 0);
out:
+ free(parent_buf);
strbuf_release(&buffer);
+ strbuf_release(&compat_buffer);
+ strbuf_release(&sig);
+ strbuf_release(&compat_sig);
return result;
}
struct strbuf *payload,
struct strbuf *signature,
const struct git_hash_algo *algop);
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo);
#endif /* COMMIT_H */
static char *parse_value(struct config_source *cs)
{
- int quote = 0, comment = 0, space = 0;
+ int quote = 0, comment = 0;
+ size_t trim_len = 0;
strbuf_reset(&cs->value);
for (;;) {
cs->linenr--;
return NULL;
}
+ if (trim_len)
+ strbuf_setlen(&cs->value, trim_len);
return cs->value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
+ if (!trim_len)
+ trim_len = cs->value.len;
if (cs->value.len)
- space++;
+ strbuf_addch(&cs->value, c);
continue;
}
if (!quote) {
continue;
}
}
- for (; space; space--)
- strbuf_addch(&cs->value, ' ');
+ if (trim_len)
+ trim_len = 0;
if (c == '\\') {
c = get_next_char(cs);
switch (c) {
continue;
}
if (c == '"') {
- quote = 1-quote;
+ quote = 1 - quote;
continue;
}
strbuf_addch(&cs->value, c);
sort >uncovered_lines.txt
comm -12 uncovered_lines.txt new_lines.txt |
- sed -e 's/$/\)/' |
- sed -e 's/^/ /' >uncovered_new_lines.txt
+ sed -e 's/$/\)/' -e 's/^/ /' >uncovered_new_lines.txt
grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
echo $file >>coverage-data.txt &&
echo "Commits introducing uncovered code:"
-commit_list=$(cat coverage-data.txt |
- grep -E '^[0-9a-f]{7,} ' |
- awk '{print $1;}' |
- sort |
- uniq)
+commit_list=$(awk '/^[0-9a-f]{7,}/ { print $1 }' coverage-data.txt | sort -u)
(
for commit in $commit_list
+++ /dev/null
-#!/usr/bin/env python
-
-""" hg-to-git.py - A Mercurial to GIT converter
-
- Copyright (C)2007 Stelian Pop <stelian@popies.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-"""
-
-import os, os.path, sys
-import tempfile, pickle, getopt
-import re
-
-if sys.hexversion < 0x02030000:
- # The behavior of the pickle module changed significantly in 2.3
- sys.stderr.write("hg-to-git.py: requires Python 2.3 or later.\n")
- sys.exit(1)
-
-# Maps hg version -> git version
-hgvers = {}
-# List of children for each hg revision
-hgchildren = {}
-# List of parents for each hg revision
-hgparents = {}
-# Current branch for each hg revision
-hgbranch = {}
-# Number of new changesets converted from hg
-hgnewcsets = 0
-
-#------------------------------------------------------------------------------
-
-def usage():
-
- print("""\
-%s: [OPTIONS] <hgprj>
-
-options:
- -s, --gitstate=FILE: name of the state to be saved/read
- for incrementals
- -n, --nrepack=INT: number of changesets that will trigger
- a repack (default=0, -1 to deactivate)
- -v, --verbose: be verbose
-
-required:
- hgprj: name of the HG project to import (directory)
-""" % sys.argv[0])
-
-#------------------------------------------------------------------------------
-
-def getgitenv(user, date):
- env = ''
- elems = re.compile('(.*?)\s+<(.*)>').match(user)
- if elems:
- env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
- env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
- env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
- env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
- else:
- env += 'export GIT_AUTHOR_NAME="%s" ;' % user
- env += 'export GIT_COMMITTER_NAME="%s" ;' % user
- env += 'export GIT_AUTHOR_EMAIL= ;'
- env += 'export GIT_COMMITTER_EMAIL= ;'
-
- env += 'export GIT_AUTHOR_DATE="%s" ;' % date
- env += 'export GIT_COMMITTER_DATE="%s" ;' % date
- return env
-
-#------------------------------------------------------------------------------
-
-state = ''
-opt_nrepack = 0
-verbose = False
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose'])
- for o, a in opts:
- if o in ('-s', '--gitstate'):
- state = a
- state = os.path.abspath(state)
- if o in ('-n', '--nrepack'):
- opt_nrepack = int(a)
- if o in ('-v', '--verbose'):
- verbose = True
- if len(args) != 1:
- raise Exception('params')
-except:
- usage()
- sys.exit(1)
-
-hgprj = args[0]
-os.chdir(hgprj)
-
-if state:
- if os.path.exists(state):
- if verbose:
- print('State does exist, reading')
- f = open(state, 'r')
- hgvers = pickle.load(f)
- else:
- print('State does not exist, first run')
-
-sock = os.popen('hg tip --template "{rev}"')
-tip = sock.read()
-if sock.close():
- sys.exit(1)
-if verbose:
- print('tip is', tip)
-
-# Calculate the branches
-if verbose:
- print('analysing the branches...')
-hgchildren["0"] = ()
-hgparents["0"] = (None, None)
-hgbranch["0"] = "master"
-for cset in range(1, int(tip) + 1):
- hgchildren[str(cset)] = ()
- prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ')
- prnts = map(lambda x: x[:x.find(':')], prnts)
- if prnts[0] != '':
- parent = prnts[0].strip()
- else:
- parent = str(cset - 1)
- hgchildren[parent] += ( str(cset), )
- if len(prnts) > 1:
- mparent = prnts[1].strip()
- hgchildren[mparent] += ( str(cset), )
- else:
- mparent = None
-
- hgparents[str(cset)] = (parent, mparent)
-
- if mparent:
- # For merge changesets, take either one, preferably the 'master' branch
- if hgbranch[mparent] == 'master':
- hgbranch[str(cset)] = 'master'
- else:
- hgbranch[str(cset)] = hgbranch[parent]
- else:
- # Normal changesets
- # For first children, take the parent branch, for the others create a new branch
- if hgchildren[parent][0] == str(cset):
- hgbranch[str(cset)] = hgbranch[parent]
- else:
- hgbranch[str(cset)] = "branch-" + str(cset)
-
-if "0" not in hgvers:
- print('creating repository')
- os.system('git init')
-
-# loop through every hg changeset
-for cset in range(int(tip) + 1):
-
- # incremental, already seen
- if str(cset) in hgvers:
- continue
- hgnewcsets += 1
-
- # get info
- log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines()
- tag = log_data[0].strip()
- date = log_data[1].strip()
- user = log_data[2].strip()
- parent = hgparents[str(cset)][0]
- mparent = hgparents[str(cset)][1]
-
- #get comment
- (fdcomment, filecomment) = tempfile.mkstemp()
- csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip()
- os.write(fdcomment, csetcomment)
- os.close(fdcomment)
-
- print('-----------------------------------------')
- print('cset:', cset)
- print('branch:', hgbranch[str(cset)])
- print('user:', user)
- print('date:', date)
- print('comment:', csetcomment)
- if parent:
- print('parent:', parent)
- if mparent:
- print('mparent:', mparent)
- if tag:
- print('tag:', tag)
- print('-----------------------------------------')
-
- # checkout the parent if necessary
- if cset != 0:
- if hgbranch[str(cset)] == "branch-" + str(cset):
- print('creating new branch', hgbranch[str(cset)])
- os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
- else:
- print('checking out branch', hgbranch[str(cset)])
- os.system('git checkout %s' % hgbranch[str(cset)])
-
- # merge
- if mparent:
- if hgbranch[parent] == hgbranch[str(cset)]:
- otherbranch = hgbranch[mparent]
- else:
- otherbranch = hgbranch[parent]
- print('merging', otherbranch, 'into', hgbranch[str(cset)])
- os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
-
- # remove everything except .git and .hg directories
- os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
-
- # repopulate with checkouted files
- os.system('hg update -C %d' % cset)
-
- # add new files
- os.system('git ls-files -x .hg --others | git update-index --add --stdin')
- # delete removed files
- os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
-
- # commit
- os.system(getgitenv(user, date) + 'git commit --allow-empty --allow-empty-message -a -F %s' % filecomment)
- os.unlink(filecomment)
-
- # tag
- if tag and tag != 'tip':
- os.system(getgitenv(user, date) + 'git tag %s' % tag)
-
- # delete branch if not used anymore...
- if mparent and len(hgchildren[str(cset)]):
- print("Deleting unused branch:", otherbranch)
- os.system('git branch -d %s' % otherbranch)
-
- # retrieve and record the version
- vvv = os.popen('git show --quiet --pretty=format:%H').read()
- print('record', cset, '->', vvv)
- hgvers[str(cset)] = vvv
-
-if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
- os.system('git repack -a -d')
-
-# write the state for incrementals
-if state:
- if verbose:
- print('Writing state')
- f = open(state, 'w')
- pickle.dump(hgvers, f)
-
-# vim: et ts=8 sw=4 sts=4
+++ /dev/null
-hg-to-git.py is able to convert a Mercurial repository into a git one,
-and preserves the branches in the process (unlike tailor)
-
-hg-to-git.py can probably be greatly improved (it's a rather crude
-combination of shell and python) but it does already work quite well for
-me. Features:
- - supports incremental conversion
- (for keeping a git repo in sync with a hg one)
- - supports hg branches
- - converts hg tags
-
-Note that the git repository will be created 'in place' (at the same
-location as the source hg repo). You will have to manually remove the
-'.hg' directory after the conversion.
-
-Also note that the incremental conversion uses 'simple' hg changesets
-identifiers (ordinals, as opposed to SHA-1 ids), and since these ids
-are not stable across different repositories the hg-to-git.py state file
-is forever tied to one hg repository.
-
-Stelian Pop <stelian@popies.net>
git -C "$1" log -1 --format=%B HEAD^2 >msg &&
test_commit -C "$1-sub" --annotate sub2 &&
git clone --no-local "$1" "$1-clone" &&
- new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+ new_commit=$(sed -e "s/$commit/$tag/" msg | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
git -C "$1-clone" replace HEAD^2 $new_commit
}
if (!tree || parse_tree(tree) < 0)
die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object *obj;
opts.pathspec = &revs->diffopt.pathspec;
opts.pathspec->recursive = 1;
- init_tree_desc(&t, tree->buffer, tree->size);
+ init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
return unpack_trees(1, &t, &opts);
}
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
+static const char *diff_src_prefix = "a/";
+static const char *diff_dst_prefix = "b/";
static int diff_relative;
static int diff_stat_name_width;
static int diff_stat_graph_width;
diff_no_prefix = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.srcprefix")) {
+ return git_config_string(&diff_src_prefix, var, value);
+ }
+ if (!strcmp(var, "diff.dstprefix")) {
+ return git_config_string(&diff_dst_prefix, var, value);
+ }
if (!strcmp(var, "diff.relative")) {
diff_relative = git_config_bool(var, value);
return 0;
void diff_set_default_prefix(struct diff_options *options)
{
- options->a_prefix = "a/";
- options->b_prefix = "b/";
+ options->a_prefix = diff_src_prefix;
+ options->b_prefix = diff_dst_prefix;
}
struct userdiff_driver *get_textconv(struct repository *r,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
+ diff_src_prefix = "a/";
+ diff_dst_prefix = "b/";
diff_set_default_prefix(options);
return 0;
}
return -1;
name = fsck_get_object_name(options, &tree->object.oid);
- if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+ if (init_tree_desc_gently(&desc, &tree->object.oid,
+ tree->buffer, tree->size, 0))
return -1;
while (tree_entry_gently(&desc, &entry)) {
struct object *obj;
const char *o_name;
struct name_stack df_dup_candidates = { NULL };
- if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
+ if (init_tree_desc_gently(&desc, tree_oid, buffer, size,
+ TREE_DESC_RAW_MODES)) {
retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_BAD_TREE,
"cannot be parsed as a tree");
if [ -z "$dry_run" ] ; then
git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
tree=$(git write-tree) &&
- commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
+ commit=$( { echo "$SUBJECT"; echo; cat "$tmp_msg"; } | git commit-tree $tree -p $commit) &&
git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
done 3<"$QUILT_SERIES"
*list = p->next;
x = compile_pattern_or(list);
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
- die("unmatched parenthesis");
+ die("unmatched ( for expression group");
*list = (*list)->next;
return x;
default:
if (p)
opt->pattern_expression = compile_pattern_expr(&p);
if (p)
- die("incomplete pattern expression: %s", p->pattern);
+ die("incomplete pattern expression group: %s", p->pattern);
if (opt->no_body_match && opt->pattern_expression)
opt->pattern_expression = grep_not_expr(opt->pattern_expression);
#define GET_OID_RECORD_PATH 0200
#define GET_OID_ONLY_TO_DIE 04000
#define GET_OID_REQUIRE_PATH 010000
+#define GET_OID_HASH_ANY 020000
#define GET_OID_DISAMBIGUATORS \
(GET_OID_COMMIT | GET_OID_COMMITTISH | \
oid->algo = hash_algo_by_ptr(the_hash_algo);
}
+static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
+{
+ memcpy(oid->hash, hash, algop->rawsz);
+ oid->algo = hash_algo_by_ptr(algop);
+}
+
static inline void oidread(struct object_id *oid, const unsigned char *hash)
{
- memcpy(oid->hash, hash, the_hash_algo->rawsz);
- oid->algo = hash_algo_by_ptr(the_hash_algo);
+ oidread_algop(oid, hash, the_hash_algo);
}
static inline int is_empty_blob_sha1(const unsigned char *sha1)
obj->flags |= SEEN;
p = add_one_object(obj, p);
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry))
switch (object_type(entry.mode)) {
enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
all_entries_interesting : entry_not_interesting;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (match != all_entries_interesting) {
}
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
- const char **extra_headers_p,
+ char **extra_headers_p,
int *need_8bit_cte_p,
int maybe_multipart)
{
- const char *extra_headers = opt->extra_headers;
+ struct strbuf headers = STRBUF_INIT;
const char *name = oid_to_hex(opt->zero_commit ?
null_oid() : &commit->object.oid);
*need_8bit_cte_p = 0; /* unknown */
+ if (opt->extra_headers && *opt->extra_headers)
+ strbuf_addstr(&headers, opt->extra_headers);
+
fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
graph_show_oneline(opt->graph);
if (opt->message_id) {
graph_show_oneline(opt->graph);
}
if (opt->mime_boundary && maybe_multipart) {
- static struct strbuf subject_buffer = STRBUF_INIT;
static struct strbuf buffer = STRBUF_INIT;
struct strbuf filename = STRBUF_INIT;
*need_8bit_cte_p = -1; /* NEVER */
- strbuf_reset(&subject_buffer);
strbuf_reset(&buffer);
- strbuf_addf(&subject_buffer,
- "%s"
+ strbuf_addf(&headers,
"MIME-Version: 1.0\n"
"Content-Type: multipart/mixed;"
" boundary=\"%s%s\"\n"
"Content-Type: text/plain; "
"charset=UTF-8; format=fixed\n"
"Content-Transfer-Encoding: 8bit\n\n",
- extra_headers ? extra_headers : "",
mime_boundary_leader, opt->mime_boundary,
mime_boundary_leader, opt->mime_boundary);
- extra_headers = subject_buffer.buf;
if (opt->numbered_files)
strbuf_addf(&filename, "%d", opt->nr);
opt->diffopt.stat_sep = buffer.buf;
strbuf_release(&filename);
}
- *extra_headers_p = extra_headers;
+ *extra_headers_p = headers.len ? strbuf_detach(&headers, NULL) : NULL;
}
static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
struct log_info *log = opt->loginfo;
struct commit *commit = log->commit, *parent = log->parent;
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
- const char *extra_headers = opt->extra_headers;
struct pretty_print_context ctx = {0};
opt->loginfo = NULL;
*/
if (cmit_fmt_is_mail(opt->commit_format)) {
- log_write_email_headers(opt, commit, &extra_headers,
+ log_write_email_headers(opt, commit, &ctx.after_subject,
&ctx.need_8bit_cte, 1);
ctx.rev = opt;
- ctx.print_email_subject = 1;
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
if (opt->commit_format != CMIT_FMT_ONELINE)
ctx.date_mode = opt->date_mode;
ctx.date_mode_explicit = opt->date_mode_explicit;
ctx.abbrev = opt->diffopt.abbrev;
- ctx.after_subject = extra_headers;
ctx.preserve_subject = opt->preserve_subject;
ctx.encode_email_headers = opt->encode_email_headers;
ctx.reflog_info = opt->reflog_info;
strbuf_release(&msgbuf);
free(ctx.notes_message);
+ free(ctx.after_subject);
if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
struct diff_queue_struct dq;
int use_color, const struct decoration_options *opts);
void show_decorations(struct rev_info *opt, struct commit *commit);
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
- const char **extra_headers_p,
+ char **extra_headers_p,
int *need_8bit_cte_p,
int maybe_multipart);
void load_ref_decorations(struct decoration_filter *filter, int flags);
--- /dev/null
+#include "git-compat-util.h"
+#include "hash.h"
+#include "path.h"
+#include "object-store.h"
+#include "hex.h"
+#include "wrapper.h"
+#include "gettext.h"
+#include "loose.h"
+#include "lockfile.h"
+#include "oidtree.h"
+
+static const char *loose_object_header = "# loose-object-idx\n";
+
+static inline int should_use_loose_object_map(struct repository *repo)
+{
+ return repo->compat_hash_algo && repo->gitdir;
+}
+
+void loose_object_map_init(struct loose_object_map **map)
+{
+ struct loose_object_map *m;
+ m = xmalloc(sizeof(**map));
+ m->to_compat = kh_init_oid_map();
+ m->to_storage = kh_init_oid_map();
+ *map = m;
+}
+
+static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
+{
+ khiter_t pos;
+ int ret;
+ struct object_id *stored;
+
+ pos = kh_put_oid_map(map, *key, &ret);
+
+ /* This item already exists in the map. */
+ if (ret == 0)
+ return 0;
+
+ stored = xmalloc(sizeof(*stored));
+ oidcpy(stored, value);
+ kh_value(map, pos) = stored;
+ return 1;
+}
+
+static int insert_loose_map(struct object_directory *odb,
+ const struct object_id *oid,
+ const struct object_id *compat_oid)
+{
+ struct loose_object_map *map = odb->loose_map;
+ int inserted = 0;
+
+ inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
+ inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
+ if (inserted)
+ oidtree_insert(odb->loose_objects_cache, compat_oid);
+
+ return inserted;
+}
+
+static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
+{
+ struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+ FILE *fp;
+
+ if (!dir->loose_map)
+ loose_object_map_init(&dir->loose_map);
+ if (!dir->loose_objects_cache) {
+ ALLOC_ARRAY(dir->loose_objects_cache, 1);
+ oidtree_init(dir->loose_objects_cache);
+ }
+
+ insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
+ insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
+ insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
+
+ strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ fp = fopen(path.buf, "rb");
+ if (!fp) {
+ strbuf_release(&path);
+ return 0;
+ }
+
+ errno = 0;
+ if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
+ goto err;
+ while (!strbuf_getline_lf(&buf, fp)) {
+ const char *p;
+ struct object_id oid, compat_oid;
+ if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
+ *p++ != ' ' ||
+ parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
+ p != buf.buf + buf.len)
+ goto err;
+ insert_loose_map(dir, &oid, &compat_oid);
+ }
+
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return errno ? -1 : 0;
+err:
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return -1;
+}
+
+int repo_read_loose_object_map(struct repository *repo)
+{
+ struct object_directory *dir;
+
+ if (!should_use_loose_object_map(repo))
+ return 0;
+
+ prepare_alt_odb(repo);
+
+ for (dir = repo->objects->odb; dir; dir = dir->next) {
+ if (load_one_loose_object_map(repo, dir) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int repo_write_loose_object_map(struct repository *repo)
+{
+ kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
+ struct lock_file lock;
+ int fd;
+ khiter_t iter;
+ struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+ if (!should_use_loose_object_map(repo))
+ return 0;
+
+ strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+ iter = kh_begin(map);
+ if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+ goto errout;
+
+ for (; iter != kh_end(map); iter++) {
+ if (kh_exist(map, iter)) {
+ if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
+ oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
+ continue;
+ strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ goto errout;
+ strbuf_reset(&buf);
+ }
+ }
+ strbuf_release(&buf);
+ if (commit_lock_file(&lock) < 0) {
+ error_errno(_("could not write loose object index %s"), path.buf);
+ strbuf_release(&path);
+ return -1;
+ }
+ strbuf_release(&path);
+ return 0;
+errout:
+ rollback_lock_file(&lock);
+ strbuf_release(&buf);
+ error_errno(_("failed to write loose object index %s\n"), path.buf);
+ strbuf_release(&path);
+ return -1;
+}
+
+static int write_one_object(struct repository *repo, const struct object_id *oid,
+ const struct object_id *compat_oid)
+{
+ struct lock_file lock;
+ int fd;
+ struct stat st;
+ struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+ strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+
+ fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ if (fd < 0)
+ goto errout;
+ if (fstat(fd, &st) < 0)
+ goto errout;
+ if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+ goto errout;
+
+ strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ goto errout;
+ if (close(fd))
+ goto errout;
+ adjust_shared_perm(path.buf);
+ rollback_lock_file(&lock);
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return 0;
+errout:
+ error_errno(_("failed to write loose object index %s\n"), path.buf);
+ close(fd);
+ rollback_lock_file(&lock);
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return -1;
+}
+
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+ const struct object_id *compat_oid)
+{
+ int inserted = 0;
+
+ if (!should_use_loose_object_map(repo))
+ return 0;
+
+ inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
+ if (inserted)
+ return write_one_object(repo, oid, compat_oid);
+ return 0;
+}
+
+int repo_loose_object_map_oid(struct repository *repo,
+ const struct object_id *src,
+ const struct git_hash_algo *to,
+ struct object_id *dest)
+{
+ struct object_directory *dir;
+ kh_oid_map_t *map;
+ khiter_t pos;
+
+ for (dir = repo->objects->odb; dir; dir = dir->next) {
+ struct loose_object_map *loose_map = dir->loose_map;
+ if (!loose_map)
+ continue;
+ map = (to == repo->compat_hash_algo) ?
+ loose_map->to_compat :
+ loose_map->to_storage;
+ pos = kh_get_oid_map(map, *src);
+ if (pos < kh_end(map)) {
+ oidcpy(dest, kh_value(map, pos));
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void loose_object_map_clear(struct loose_object_map **map)
+{
+ struct loose_object_map *m = *map;
+ struct object_id *oid;
+
+ if (!m)
+ return;
+
+ kh_foreach_value(m->to_compat, oid, free(oid));
+ kh_foreach_value(m->to_storage, oid, free(oid));
+ kh_destroy_oid_map(m->to_compat);
+ kh_destroy_oid_map(m->to_storage);
+ free(m);
+ *map = NULL;
+}
--- /dev/null
+#ifndef LOOSE_H
+#define LOOSE_H
+
+#include "khash.h"
+
+struct loose_object_map {
+ kh_oid_map_t *to_compat;
+ kh_oid_map_t *to_storage;
+};
+
+void loose_object_map_init(struct loose_object_map **map);
+void loose_object_map_clear(struct loose_object_map **map);
+int repo_loose_object_map_oid(struct repository *repo,
+ const struct object_id *src,
+ const struct git_hash_algo *dest_algo,
+ struct object_id *dest);
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+ const struct object_id *compat_oid);
+int repo_read_loose_object_map(struct repository *repo);
+int repo_write_loose_object_map(struct repository *repo);
+
+#endif
die("unable to read tree (%s)", oid_to_hex(hash));
if (type != OBJ_TREE)
die("%s is not a tree", oid_to_hex(hash));
- init_tree_desc(desc, buffer, size);
+ init_tree_desc(desc, hash, buffer, size);
return buffer;
}
buf = repo_read_object_file(the_repository, oid1, &type, &sz);
if (!buf)
die("cannot read tree %s", oid_to_hex(oid1));
- init_tree_desc(&desc, buf, sz);
+ init_tree_desc(&desc, oid1, buf, sz);
rewrite_here = NULL;
while (desc.size) {
xmp.level = XDL_MERGE_ZEALOUS;
xmp.favor = opts->variant;
xmp.xpp.flags = opts->xdl_opts;
- if (git_xmerge_style >= 0)
+ if (opts->conflict_style >= 0)
+ xmp.style = opts->conflict_style;
+ else if (git_xmerge_style >= 0)
xmp.style = git_xmerge_style;
if (marker_size > 0)
xmp.marker_size = marker_size;
const struct ll_merge_options *opts)
{
struct attr_check *check = load_merge_attributes();
- static const struct ll_merge_options default_opts;
+ static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT;
const char *ll_driver_name = NULL;
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
const struct ll_merge_driver *driver;
*/
unsigned extra_marker_size;
+ /* Override the global conflict style. */
+ int conflict_style;
+
/* Extra xpparam_t flags as defined in xdiff/xdiff.h. */
long xdl_opts;
};
+#define LL_MERGE_OPTIONS_INIT { .conflict_style = -1 }
+
enum ll_merge_result {
LL_MERGE_ERROR = -1,
LL_MERGE_OK = 0,
parse_tree(side1) < 0 ||
parse_tree(side2) < 0)
return -1;
- init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
- init_tree_desc(t + 1, side1->buffer, side1->size);
- init_tree_desc(t + 2, side2->buffer, side2->size);
+ init_tree_desc(t + 0, &merge_base->object.oid,
+ merge_base->buffer, merge_base->size);
+ init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size);
+ init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size);
trace2_region_enter("merge", "traverse_trees", opt->repo);
ret = traverse_trees(NULL, 3, t, &info);
mmbuffer_t *result_buf)
{
mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = {0};
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
char *base, *name1, *name2;
enum ll_merge_result merge_status;
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
ll_opts.xdl_opts = opt->xdl_opts;
+ ll_opts.conflict_style = opt->conflict_style;
if (opt->priv->call_depth) {
ll_opts.virtual_ancestor = 1;
unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
if (parse_tree(prev) < 0)
return -1;
- init_tree_desc(&trees[0], prev->buffer, prev->size);
+ init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
if (parse_tree(next) < 0)
return -1;
- init_tree_desc(&trees[1], next->buffer, next->size);
+ init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
ret = unpack_trees(2, trees, &unpack_opts);
clear_unpack_trees_porcelain(&unpack_opts);
{
if (parse_tree(tree) < 0)
exit(128);
- init_tree_desc(desc, tree->buffer, tree->size);
+ init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
}
static int unpack_trees_start(struct merge_options *opt,
const int extra_marker_size)
{
mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = {0};
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
char *base, *name1, *name2;
enum ll_merge_result merge_status;
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
ll_opts.xdl_opts = opt->xdl_opts;
+ ll_opts.conflict_style = opt->conflict_style;
if (opt->priv->call_depth) {
ll_opts.virtual_ancestor = 1;
opt->renormalize = 0;
+ opt->conflict_style = -1;
+
merge_recursive_config(opt);
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
if (merge_verbosity)
/* xdiff-related options (patience, ignore whitespace, ours/theirs) */
long xdl_opts;
+ int conflict_style;
enum {
MERGE_VARIANT_NORMAL = 0,
MERGE_VARIANT_OURS,
rollback_lock_file(&lock_file);
return -1;
}
- init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+ init_tree_desc(t+i, &trees[i]->object.oid,
+ trees[i]->buffer, trees[i]->size);
}
memset(&opts, 0, sizeof(opts));
--- /dev/null
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "hex.h"
+#include "repository.h"
+#include "hash-ll.h"
+#include "hash.h"
+#include "object.h"
+#include "loose.h"
+#include "commit.h"
+#include "gpg-interface.h"
+#include "object-file-convert.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+ const struct git_hash_algo *to, struct object_id *dest)
+{
+ /*
+ * If the source algorithm is not set, then we're using the
+ * default hash algorithm for that object.
+ */
+ const struct git_hash_algo *from =
+ src->algo ? &hash_algos[src->algo] : repo->hash_algo;
+
+ if (from == to) {
+ if (src != dest)
+ oidcpy(dest, src);
+ return 0;
+ }
+ if (repo_loose_object_map_oid(repo, src, to, dest)) {
+ /*
+ * We may have loaded the object map at repo initialization but
+ * another process (perhaps upstream of a pipe from us) may have
+ * written a new object into the map. If the object is missing,
+ * let's reload the map to see if the object has appeared.
+ */
+ repo_read_loose_object_map(repo);
+ if (repo_loose_object_map_oid(repo, src, to, dest))
+ return -1;
+ }
+ return 0;
+}
+
+static int decode_tree_entry_raw(struct object_id *oid, const char **path,
+ size_t *len, const struct git_hash_algo *algo,
+ const char *buf, unsigned long size)
+{
+ uint16_t mode;
+ const unsigned hashsz = algo->rawsz;
+
+ if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
+ return -1;
+ }
+
+ *path = parse_mode(buf, &mode);
+ if (!*path || !**path)
+ return -1;
+ *len = strlen(*path) + 1;
+
+ oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+ return 0;
+}
+
+static int convert_tree_object(struct strbuf *out,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const char *buffer, size_t size)
+{
+ const char *p = buffer, *end = buffer + size;
+
+ while (p < end) {
+ struct object_id entry_oid, mapped_oid;
+ const char *path = NULL;
+ size_t pathlen;
+
+ if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
+ end - p))
+ return error(_("failed to decode tree entry"));
+ if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
+ return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
+ strbuf_add(out, p, path - p);
+ strbuf_add(out, path, pathlen);
+ strbuf_add(out, mapped_oid.hash, to->rawsz);
+ p = path + pathlen + from->rawsz;
+ }
+ return 0;
+}
+
+static int convert_tag_object(struct strbuf *out,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const char *buffer, size_t size)
+{
+ struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
+ const int entry_len = from->hexsz + 7;
+ size_t payload_size;
+ struct object_id oid, mapped_oid;
+ const char *p;
+
+ /* Consume the object line */
+ if ((entry_len >= size) ||
+ memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
+ return error("bogus tag object");
+ if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
+ return error("bad tag object ID");
+ if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ return error("unable to map tree %s in tag object",
+ oid_to_hex(&oid));
+ size -= ((p + 1) - buffer);
+ buffer = p + 1;
+
+ /* Is there a signature for our algorithm? */
+ payload_size = parse_signed_buffer(buffer, size);
+ if (payload_size != size) {
+ /* Yes, there is. */
+ strbuf_add(&oursig, buffer + payload_size, size - payload_size);
+ }
+
+ /* Now, is there a signature for the other algorithm? */
+ parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
+ /*
+ * Our payload is now in payload and we may have up to two signatrures
+ * in oursig and othersig.
+ */
+
+ /* Add some slop for longer signature header in the new algorithm. */
+ strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
+ strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
+ strbuf_addbuf(out, &payload);
+ if (oursig.len)
+ add_header_signature(out, &oursig, from);
+ strbuf_addbuf(out, &othersig);
+
+ strbuf_release(&payload);
+ strbuf_release(&othersig);
+ strbuf_release(&oursig);
+ return 0;
+}
+
+static int convert_commit_object(struct strbuf *out,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const char *buffer, size_t size)
+{
+ const char *tail = buffer;
+ const char *bufptr = buffer;
+ const int tree_entry_len = from->hexsz + 5;
+ const int parent_entry_len = from->hexsz + 7;
+ struct object_id oid, mapped_oid;
+ const char *p, *eol;
+
+ tail += size;
+
+ while ((bufptr < tail) && (*bufptr != '\n')) {
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ return error(_("bad %s in commit"), "line");
+
+ if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
+ {
+ if (((bufptr + tree_entry_len) != eol) ||
+ parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
+ (p != eol))
+ return error(_("bad %s in commit"), "tree");
+
+ if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ return error(_("unable to map %s %s in commit object"),
+ "tree", oid_to_hex(&oid));
+ strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
+ }
+ else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
+ {
+ if (((bufptr + parent_entry_len) != eol) ||
+ parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
+ (p != eol))
+ return error(_("bad %s in commit"), "parent");
+
+ if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ return error(_("unable to map %s %s in commit object"),
+ "parent", oid_to_hex(&oid));
+
+ strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
+ }
+ else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
+ {
+ struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
+
+ /* Recover the tag object from the mergetag */
+ strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
+
+ bufptr = eol + 1;
+ while ((bufptr < tail) && (*bufptr == ' ')) {
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol) {
+ strbuf_release(&tag);
+ return error(_("bad %s in commit"), "mergetag continuation");
+ }
+ strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
+ bufptr = eol + 1;
+ }
+
+ /* Compute the new tag object */
+ if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
+ strbuf_release(&tag);
+ strbuf_release(&new_tag);
+ return -1;
+ }
+
+ /* Write the new mergetag */
+ strbuf_addstr(out, "mergetag");
+ strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
+ strbuf_release(&tag);
+ strbuf_release(&new_tag);
+ }
+ else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else {
+ /* Unknown line fail it might embed an oid */
+ return -1;
+ }
+ /* Consume any trailing continuation lines */
+ bufptr = eol + 1;
+ while ((bufptr < tail) && (*bufptr == ' ')) {
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ return error(_("bad %s in commit"), "continuation");
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ bufptr = eol + 1;
+ }
+ }
+ if (bufptr < tail)
+ strbuf_add(out, bufptr, tail - bufptr);
+ return 0;
+}
+
+int convert_object_file(struct strbuf *outbuf,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const void *buf, size_t len,
+ enum object_type type,
+ int gentle)
+{
+ int ret;
+
+ /* Don't call this function when no conversion is necessary */
+ if ((from == to) || (type == OBJ_BLOB))
+ BUG("Refusing noop object file conversion");
+
+ switch (type) {
+ case OBJ_COMMIT:
+ ret = convert_commit_object(outbuf, from, to, buf, len);
+ break;
+ case OBJ_TREE:
+ ret = convert_tree_object(outbuf, from, to, buf, len);
+ break;
+ case OBJ_TAG:
+ ret = convert_tag_object(outbuf, from, to, buf, len);
+ break;
+ default:
+ /* Not implemented yet, so fail. */
+ ret = -1;
+ break;
+ }
+ if (!ret)
+ return 0;
+ if (gentle) {
+ strbuf_release(outbuf);
+ return ret;
+ }
+ die(_("Failed to convert object from %s to %s"),
+ from->name, to->name);
+}
--- /dev/null
+#ifndef OBJECT_CONVERT_H
+#define OBJECT_CONVERT_H
+
+struct repository;
+struct object_id;
+struct git_hash_algo;
+struct strbuf;
+#include "object.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+ const struct git_hash_algo *to, struct object_id *dest);
+
+/*
+ * Convert an object file from one hash algorithm to another algorithm.
+ * Return -1 on failure, 0 on success.
+ */
+int convert_object_file(struct strbuf *outbuf,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const void *buf, size_t len,
+ enum object_type type,
+ int gentle);
+
+#endif /* OBJECT_CONVERT_H */
#include "setup.h"
#include "submodule.h"
#include "fsck.h"
+#include "loose.h"
+#include "object-file-convert.h"
/* The maximum size for an object header. */
#define MAX_HEADER_LEN 32
void *buf, unsigned long size,
enum object_type type)
{
+ const struct git_hash_algo *algo =
+ oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
struct object_id real_oid;
- hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+ hash_object_file(algo, buf, size, type, &real_oid);
return !oideq(oid, &real_oid) ? -1 : 0;
}
return 0;
}
+static int oid_object_info_convert(struct repository *r,
+ const struct object_id *input_oid,
+ struct object_info *input_oi, unsigned flags)
+{
+ const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+ int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+ struct strbuf type_name = STRBUF_INIT;
+ struct object_id oid, delta_base_oid;
+ struct object_info new_oi, *oi;
+ unsigned long size;
+ void *content;
+ int ret;
+
+ if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(input_oid), the_hash_algo->name);
+ return -1;
+ }
+
+ /* Is new_oi needed? */
+ oi = input_oi;
+ if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+ input_oi->contentp)) {
+ new_oi = *input_oi;
+ /* Does delta_base_oid need to be converted? */
+ if (input_oi->delta_base_oid)
+ new_oi.delta_base_oid = &delta_base_oid;
+ /* Will the attributes differ when converted? */
+ if (input_oi->sizep || input_oi->contentp) {
+ new_oi.contentp = &content;
+ new_oi.sizep = &size;
+ new_oi.type_name = &type_name;
+ }
+ oi = &new_oi;
+ }
+
+ ret = oid_object_info_extended(r, &oid, oi, flags);
+ if (ret)
+ return -1;
+ if (oi == input_oi)
+ return ret;
+
+ if (new_oi.contentp) {
+ struct strbuf outbuf = STRBUF_INIT;
+ enum object_type type;
+
+ type = type_from_string_gently(type_name.buf, type_name.len,
+ !do_die);
+ if (type == -1)
+ return -1;
+ if (type != OBJ_BLOB) {
+ ret = convert_object_file(&outbuf,
+ the_hash_algo, input_algo,
+ content, size, type, !do_die);
+ if (ret == -1)
+ return -1;
+ free(content);
+ size = outbuf.len;
+ content = strbuf_detach(&outbuf, NULL);
+ }
+ if (input_oi->sizep)
+ *input_oi->sizep = size;
+ if (input_oi->contentp)
+ *input_oi->contentp = content;
+ else
+ free(content);
+ if (input_oi->type_name)
+ *input_oi->type_name = type_name;
+ else
+ strbuf_release(&type_name);
+ }
+ if (new_oi.delta_base_oid == &delta_base_oid) {
+ if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+ input_oi->delta_base_oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(&delta_base_oid),
+ input_algo->name);
+ return -1;
+ }
+ }
+ input_oi->whence = new_oi.whence;
+ input_oi->u = new_oi.u;
+ return ret;
+}
+
int oid_object_info_extended(struct repository *r, const struct object_id *oid,
struct object_info *oi, unsigned flags)
{
int ret;
+
+ if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+ return oid_object_info_convert(r, oid, oi, flags);
+
obj_read_lock();
ret = do_oid_object_info_extended(r, oid, oi, flags);
obj_read_unlock();
const char *filename, unsigned flags,
git_zstream *stream,
unsigned char *buf, size_t buflen,
- git_hash_ctx *c,
+ git_hash_ctx *c, git_hash_ctx *compat_c,
char *hdr, int hdrlen)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int fd;
fd = create_tmpfile(tmp_file, filename);
git_deflate_init(stream, zlib_compression_level);
stream->next_out = buf;
stream->avail_out = buflen;
- the_hash_algo->init_fn(c);
+ algo->init_fn(c);
+ if (compat && compat_c)
+ compat->init_fn(compat_c);
/* Start to feed header to zlib stream */
stream->next_in = (unsigned char *)hdr;
stream->avail_in = hdrlen;
while (git_deflate(stream, 0) == Z_OK)
; /* nothing */
- the_hash_algo->update_fn(c, hdr, hdrlen);
+ algo->update_fn(c, hdr, hdrlen);
+ if (compat && compat_c)
+ compat->update_fn(compat_c, hdr, hdrlen);
return fd;
}
* Common steps for the inner git_deflate() loop for writing loose
* objects. Returns what git_deflate() returns.
*/
-static int write_loose_object_common(git_hash_ctx *c,
+static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
git_zstream *stream, const int flush,
unsigned char *in0, const int fd,
unsigned char *compressed,
const size_t compressed_len)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int ret;
ret = git_deflate(stream, flush ? Z_FINISH : 0);
- the_hash_algo->update_fn(c, in0, stream->next_in - in0);
+ algo->update_fn(c, in0, stream->next_in - in0);
+ if (compat && compat_c)
+ compat->update_fn(compat_c, in0, stream->next_in - in0);
if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
die_errno(_("unable to write loose object file"));
stream->next_out = compressed;
* - End the compression of zlib stream.
* - Get the calculated oid to "oid".
*/
-static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream,
- struct object_id *oid)
+static int end_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
+ git_zstream *stream, struct object_id *oid,
+ struct object_id *compat_oid)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int ret;
ret = git_deflate_end_gently(stream);
if (ret != Z_OK)
return ret;
- the_hash_algo->final_oid_fn(oid, c);
+ algo->final_oid_fn(oid, c);
+ if (compat && compat_c)
+ compat->final_oid_fn(compat_oid, compat_c);
return Z_OK;
}
fd = start_loose_object_common(&tmp_file, filename.buf, flags,
&stream, compressed, sizeof(compressed),
- &c, hdr, hdrlen);
+ &c, NULL, hdr, hdrlen);
if (fd < 0)
return -1;
do {
unsigned char *in0 = stream.next_in;
- ret = write_loose_object_common(&c, &stream, 1, in0, fd,
+ ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd,
compressed, sizeof(compressed));
} while (ret == Z_OK);
if (ret != Z_STREAM_END)
die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
ret);
- ret = end_loose_object_common(&c, &stream, ¶no_oid);
+ ret = end_loose_object_common(&c, NULL, &stream, ¶no_oid, NULL);
if (ret != Z_OK)
die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
ret);
int stream_loose_object(struct input_stream *in_stream, size_t len,
struct object_id *oid)
{
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ struct object_id compat_oid;
int fd, ret, err = 0, flush = 0;
unsigned char compressed[4096];
git_zstream stream;
- git_hash_ctx c;
+ git_hash_ctx c, compat_c;
struct strbuf tmp_file = STRBUF_INIT;
struct strbuf filename = STRBUF_INIT;
int dirlen;
*/
fd = start_loose_object_common(&tmp_file, filename.buf, 0,
&stream, compressed, sizeof(compressed),
- &c, hdr, hdrlen);
+ &c, &compat_c, hdr, hdrlen);
if (fd < 0) {
err = -1;
goto cleanup;
if (in_stream->is_finished)
flush = 1;
}
- ret = write_loose_object_common(&c, &stream, flush, in0, fd,
+ ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd,
compressed, sizeof(compressed));
/*
* Unlike write_loose_object(), we do not have the entire
*/
if (ret != Z_STREAM_END)
die(_("unable to stream deflate new object (%d)"), ret);
- ret = end_loose_object_common(&c, &stream, oid);
+ ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid);
if (ret != Z_OK)
die(_("deflateEnd on stream object failed (%d)"), ret);
close_loose_object(fd, tmp_file.buf);
}
err = finalize_object_file(tmp_file.buf, filename.buf);
+ if (!err && compat)
+ err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
cleanup:
strbuf_release(&tmp_file);
strbuf_release(&filename);
int write_object_file_flags(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
- unsigned flags)
+ struct object_id *compat_oid_in, unsigned flags)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
+ struct object_id compat_oid;
char hdr[MAX_HEADER_LEN];
int hdrlen = sizeof(hdr);
+ /* Generate compat_oid */
+ if (compat) {
+ if (compat_oid_in)
+ oidcpy(&compat_oid, compat_oid_in);
+ else if (type == OBJ_BLOB)
+ hash_object_file(compat, buf, len, type, &compat_oid);
+ else {
+ struct strbuf converted = STRBUF_INIT;
+ convert_object_file(&converted, algo, compat,
+ buf, len, type, 0);
+ hash_object_file(compat, converted.buf, converted.len,
+ type, &compat_oid);
+ strbuf_release(&converted);
+ }
+ }
+
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
*/
- write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr,
- &hdrlen);
+ write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
if (freshen_packed_object(oid) || freshen_loose_object(oid))
return 0;
- return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
+ if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags))
+ return -1;
+ if (compat)
+ return repo_add_loose_object_map(repo, oid, &compat_oid);
+ return 0;
}
int write_object_file_literally(const void *buf, unsigned long len,
unsigned flags)
{
char *header;
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
+ struct object_id compat_oid;
int hdrlen, status = 0;
+ int compat_type = -1;
+
+ if (compat) {
+ compat_type = type_from_string_gently(type, -1, 1);
+ if (compat_type == OBJ_BLOB)
+ hash_object_file(compat, buf, len, compat_type,
+ &compat_oid);
+ else if (compat_type != -1) {
+ struct strbuf converted = STRBUF_INIT;
+ convert_object_file(&converted, algo, compat,
+ buf, len, compat_type, 0);
+ hash_object_file(compat, converted.buf, converted.len,
+ compat_type, &compat_oid);
+ strbuf_release(&converted);
+ }
+ }
/* type string, SP, %lu of the length plus NUL must fit this */
hdrlen = strlen(type) + MAX_HEADER_LEN;
if (freshen_packed_object(oid) || freshen_loose_object(oid))
goto cleanup;
status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
+ if (compat_type != -1)
+ return repo_add_loose_object_map(repo, oid, &compat_oid);
cleanup:
free(header);
int force_object_loose(const struct object_id *oid, time_t mtime)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
void *buf;
unsigned long len;
struct object_info oi = OBJECT_INFO_INIT;
+ struct object_id compat_oid;
enum object_type type;
char hdr[MAX_HEADER_LEN];
int hdrlen;
oi.contentp = &buf;
if (oid_object_info_extended(the_repository, oid, &oi, 0))
return error(_("cannot read object for %s"), oid_to_hex(oid));
+ if (compat) {
+ if (repo_oid_to_algop(repo, oid, compat, &compat_oid))
+ return error(_("cannot map object %s to %s"),
+ oid_to_hex(oid), compat->name);
+ }
hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
+ if (!ret && compat)
+ ret = repo_add_loose_object_map(the_repository, oid, &compat_oid);
free(buf);
return ret;
#include "midx.h"
#include "commit-reach.h"
#include "date.h"
+#include "object-file-convert.h"
static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
{
+ /* 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;
{
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;
num = m->num_objects;
if (!num)
for (i = first; i < num && !ds->ambiguous; i++) {
struct object_id oid;
current = nth_midxed_object_oid(&oid, m, i);
- if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash))
+ if (!match_hash(len, ds->bin_pfx.hash, current->hash))
break;
update_candidates(ds, current);
}
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;
for (i = first; i < num && !ds->ambiguous; i++) {
struct object_id oid;
nth_packed_object_id(&oid, p, i);
- if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash))
+ if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
break;
update_candidates(ds, &oid);
}
struct multi_pack_index *m;
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;
+
for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
m = m->next)
unique_in_midx(m, ds);
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 > the_hash_algo->hexsz)
+ if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
return -1;
memset(ds, 0, sizeof(*ds));
ds->len = len;
ds->hex_pfx[len] = '\0';
ds->repo = r;
+ ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
prepare_alt_odb(r);
return 0;
}
return collect_ambiguous(oid, data);
}
-static int sort_ambiguous(const void *a, const void *b, void *ctx)
+static int sort_ambiguous(const void *va, const void *vb, void *ctx)
{
struct repository *sort_ambiguous_repo = ctx;
+ const struct object_id *a = va, *b = vb;
int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
int a_type_sort;
* Sorts by hash within the same object type, just as
* oid_array_for_each_unique() would do.
*/
- if (a_type == b_type)
- return oidcmp(a, b);
+ if (a_type == b_type) {
+ if (a->algo == b->algo)
+ return oidcmp(a, b);
+ else
+ return a->algo > b->algo ? 1 : -1;
+ }
/*
* Between object types show tags, then commits, and finally
int status;
struct disambiguate_state ds;
int quietly = !!(flags & GET_OID_QUIETLY);
+ const struct git_hash_algo *algo = r->hash_algo;
+
+ if (flags & GET_OID_HASH_ANY)
+ algo = NULL;
- if (init_object_disambiguation(r, name, len, &ds) < 0)
+ if (init_object_disambiguation(r, name, len, algo, &ds) < 0)
return -1;
if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
if (!ds.ambiguous)
ds.fn = NULL;
- repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+ repo_for_each_abbrev(r, ds.hex_pfx, algo, collect_ambiguous, &collect);
sort_ambiguous_oid_array(r, &collect);
if (oid_array_for_each(&collect, show_ambiguous_object, &out))
}
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 oid_array collect = OID_ARRAY_INIT;
struct disambiguate_state ds;
int ret;
- if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
+ if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
return -1;
ds.always_call_fn = 1;
int repo_find_unique_abbrev_r(struct repository *r, char *hex,
const struct object_id *oid, int 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 = r->hash_algo->hexsz;
+ const unsigned hexsz = algo->hexsz;
if (len < 0) {
unsigned long count = repo_approximate_object_count(r);
find_abbrev_len_packed(&mad);
- if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
+ if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
return -1;
ds.fn = repo_extend_abbrev_len;
typedef int each_abbrev_fn(const struct object_id *oid, void *);
-int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix,
+ const struct git_hash_algo *algo, each_abbrev_fn, void *);
int set_disambiguate_hint_config(const char *var, const char *value);
uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
struct oidtree *loose_objects_cache;
+ /* Map between object IDs for loose objects. */
+ struct loose_object_map *loose_map;
+
/*
* This is a temporary object store created by the tmp_objdir
* facility. Disable ref updates since the objects in the store
int write_object_file_flags(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
- unsigned flags);
+ struct object_id *comapt_oid_in, unsigned flags);
static inline int write_object_file(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid)
{
- return write_object_file_flags(buf, len, type, oid, 0);
+ return write_object_file_flags(buf, len, type, oid, NULL, 0);
}
int write_object_file_literally(const void *buf, unsigned long len,
#include "alloc.h"
#include "packfile.h"
#include "commit-graph.h"
+#include "loose.h"
unsigned int get_max_object_index(void)
{
{
free(odb->path);
odb_clear_loose_cache(odb);
+ loose_object_map_clear(&odb->loose_map);
free(odb);
}
void *object_as_type(struct object *obj, enum object_type type, int quiet);
+
+static inline const char *parse_mode(const char *str, uint16_t *modep)
+{
+ unsigned char c;
+ unsigned int mode = 0;
+
+ if (*str == ' ')
+ return NULL;
+
+ while ((c = *str++) != ' ') {
+ if (c < '0' || c > '7')
+ return NULL;
+ mode = (mode << 3) + (c - '0');
+ }
+ *modep = mode;
+ return str;
+}
+
/*
* Returns the object, having parsed it to find out what it is.
*
{
ALLOC_GROW(array->oid, array->nr + 1, array->alloc);
oidcpy(&array->oid[array->nr++], oid);
+ if (!oid->algo)
+ oid_set_algo(&array->oid[array->nr - 1], the_hash_algo);
array->sorted = 0;
}
-static int void_hashcmp(const void *a, const void *b)
+static int void_hashcmp(const void *va, const void *vb)
{
- return oidcmp(a, b);
+ const struct object_id *a = va, *b = vb;
+ int ret;
+ if (a->algo == b->algo)
+ ret = oidcmp(a, b);
+ else
+ ret = a->algo > b->algo ? 1 : -1;
+ return ret;
}
void oid_array_sort(struct oid_array *array)
fuzz-commit-graph
+fuzz-config
fuzz-date
fuzz-pack-headers
fuzz-pack-idx
--- /dev/null
+#include "git-compat-util.h"
+#include "config.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
+static int config_parser_callback(const char *, const char *,
+ const struct config_context *, void *);
+
+static int config_parser_callback(const char *key, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *data UNUSED)
+{
+ /*
+ * Visit every byte of memory we are given to make sure the parser
+ * gave it to us appropriately. We need to unconditionally return 0,
+ * but we also want to prevent the strlen from being optimized away.
+ */
+ size_t c = strlen(key);
+
+ if (value)
+ c += strlen(value);
+ return c == SIZE_MAX;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, const size_t size)
+{
+ struct config_options config_opts = { 0 };
+
+ config_opts.error_action = CONFIG_ERROR_SILENT;
+ git_config_from_mem(config_parser_callback, CONFIG_ORIGIN_BLOB,
+ "fuzztest-config", (const char *)data, size, NULL,
+ CONFIG_SCOPE_UNKNOWN, &config_opts);
+ return 0;
+}
if (parse_tree(tree) < 0)
die("unable to load tree object %s",
oid_to_hex(&tree->object.oid));
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
struct tree *tree = (struct tree *)obj;
struct tree_desc desc;
struct name_entry entry;
- if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+ if (init_tree_desc_gently(&desc, &tree->object.oid,
+ tree->buffer, tree->size, 0))
/*
* Error messages are given when packs are
* verified, so do not print any here.
}
}
-void pp_title_line(struct pretty_print_context *pp,
- const char **msg_p,
- struct strbuf *sb,
- const char *encoding,
- int need_8bit_cte)
+void pp_email_subject(struct pretty_print_context *pp,
+ const char **msg_p,
+ struct strbuf *sb,
+ const char *encoding,
+ int need_8bit_cte)
{
static const int max_length = 78; /* per rfc2047 */
struct strbuf title;
pp->preserve_subject ? "\n" : " ");
strbuf_grow(sb, title.len + 1024);
- if (pp->print_email_subject) {
- if (pp->rev)
- fmt_output_email_subject(sb, pp->rev);
- if (pp->encode_email_headers &&
- needs_rfc2047_encoding(title.buf, title.len))
- add_rfc2047(sb, title.buf, title.len,
- encoding, RFC2047_SUBJECT);
- else
- strbuf_add_wrapped_bytes(sb, title.buf, title.len,
+ fmt_output_email_subject(sb, pp->rev);
+ if (pp->encode_email_headers &&
+ needs_rfc2047_encoding(title.buf, title.len))
+ add_rfc2047(sb, title.buf, title.len,
+ encoding, RFC2047_SUBJECT);
+ else
+ strbuf_add_wrapped_bytes(sb, title.buf, title.len,
-last_line_length(sb), 1, max_length);
- } else {
- strbuf_addbuf(sb, &title);
- }
strbuf_addch(sb, '\n');
if (need_8bit_cte == 0) {
if (pp->after_subject) {
strbuf_addstr(sb, pp->after_subject);
}
- if (cmit_fmt_is_mail(pp->fmt)) {
- strbuf_addch(sb, '\n');
- }
+
+ strbuf_addch(sb, '\n');
if (pp->in_body_headers.nr) {
int i;
}
pp_header(pp, encoding, commit, &msg, sb);
- if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) {
+ if (pp->fmt != CMIT_FMT_ONELINE && !cmit_fmt_is_mail(pp->fmt)) {
strbuf_addch(sb, '\n');
}
msg = skip_blank_lines(msg);
/* These formats treat the title line specially. */
- if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
- pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
+ if (pp->fmt == CMIT_FMT_ONELINE) {
+ msg = format_subject(sb, msg, " ");
+ strbuf_addch(sb, '\n');
+ } else if (cmit_fmt_is_mail(pp->fmt))
+ pp_email_subject(pp, &msg, sb, encoding, need_8bit_cte);
beginning_of_body = sb->len;
if (pp->fmt != CMIT_FMT_ONELINE)
*/
enum cmit_fmt fmt;
int abbrev;
- const char *after_subject;
+ char *after_subject;
int preserve_subject;
struct date_mode date_mode;
unsigned date_mode_explicit:1;
- int print_email_subject;
int expand_tabs_in_log;
int need_8bit_cte;
char *notes_message;
const char *encoding);
/*
- * Format title line of commit message taken from "msg_p" and
+ * Format subject line of commit message taken from "msg_p" and
* put it into "sb".
* First line of "msg_p" is also affected.
*/
-void pp_title_line(struct pretty_print_context *pp, const char **msg_p,
- struct strbuf *sb, const char *encoding,
- int need_8bit_cte);
+void pp_email_subject(struct pretty_print_context *pp, const char **msg_p,
+ struct strbuf *sb, const char *encoding,
+ int need_8bit_cte);
/*
* Get current state of commit message from "msg_p" and continue formatting
tree->buffer = data;
tree->size = size;
}
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!repo_has_object_file(the_repository, &entry.oid) ||
return 0;
while ((d = readdir(dir))) {
- if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
+ /*
+ * Besides skipping over "." and "..", we also need to
+ * skip over other files that have a leading ".". This
+ * is due to behaviour of NFS, which will rename files
+ * to ".nfs*" to emulate delete-on-last-close.
+ *
+ * In any case this should be fine as the reftable
+ * library will never write files with leading dots
+ * anyway.
+ */
+ if (starts_with(d->d_name, "."))
continue;
len++;
}
options.filter = xstrdup(value);
return 0;
} else if (!strcmp(name, "object-format")) {
- int algo;
options.object_format = 1;
- if (strcmp(value, "true")) {
- algo = hash_algo_by_name(value);
- if (algo == GIT_HASH_UNKNOWN)
- die("unknown object format '%s'", value);
- options.hash_algo = &hash_algos[algo];
- }
+ if (strcmp(value, "true"))
+ die(_("unknown value for object-format: %s"), value);
return 0;
} else {
return 1 /* unsupported */;
#include "read-cache-ll.h"
#include "remote.h"
#include "setup.h"
+#include "loose.h"
#include "submodule-config.h"
#include "sparse-index.h"
#include "trace2.h"
repo->hash_algo = &hash_algos[hash_algo];
}
+void repo_set_compat_hash_algo(struct repository *repo, int algo)
+{
+ if (hash_algo_by_ptr(repo->hash_algo) == algo)
+ BUG("hash_algo and compat_hash_algo match");
+ repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
+ if (repo->compat_hash_algo)
+ repo_read_loose_object_map(repo);
+}
+
void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
{
repo->ref_storage_format = format;
goto error;
repo_set_hash_algo(repo, format.hash_algo);
+ repo_set_compat_hash_algo(repo, format.compat_hash_algo);
repo_set_ref_storage_format(repo, format.ref_storage_format);
repo->repository_format_worktree_config = format.worktree_config;
if (worktree)
repo_set_worktree(repo, worktree);
+ if (repo->compat_hash_algo)
+ repo_read_loose_object_map(repo);
+
clear_repository_format(&format);
return 0;
/* Repository's current hash algorithm, as serialized on disk. */
const struct git_hash_algo *hash_algo;
+ /* Repository's compatibility hash algorithm. */
+ const struct git_hash_algo *compat_hash_algo;
+
/* Repository's reference storage format, as serialized on disk. */
unsigned int ref_storage_format;
const struct set_gitdir_args *extra_args);
void repo_set_worktree(struct repository *repo, const char *path);
void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);
void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
void initialize_the_repository(void);
RESULT_MUST_BE_USED
if (parse_tree_gently(tree, 1) < 0)
return;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
if (parse_tree_gently(tree, 1) < 0)
return;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
msg = getenv("GIT_CHERRY_PICK_HELP");
if (msg) {
- advise("%s\n", msg);
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", msg);
/*
* A conflict has occurred but the porcelain
* (typically rebase --interactive) wants to take care
if (show_hint) {
if (opts->no_commit)
- advise(_("after resolving the conflicts, mark the corrected paths\n"
- "with 'git add <paths>' or 'git rm <paths>'"));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("after resolving the conflicts, mark the corrected paths\n"
+ "with 'git add <paths>' or 'git rm <paths>'"));
else if (opts->action == REPLAY_PICK)
- advise(_("After resolving the conflicts, mark them with\n"
- "\"git add/rm <pathspec>\", then run\n"
- "\"git cherry-pick --continue\".\n"
- "You can instead skip this commit with \"git cherry-pick --skip\".\n"
- "To abort and get back to the state before \"git cherry-pick\",\n"
- "run \"git cherry-pick --abort\"."));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("After resolving the conflicts, mark them with\n"
+ "\"git add/rm <pathspec>\", then run\n"
+ "\"git cherry-pick --continue\".\n"
+ "You can instead skip this commit with \"git cherry-pick --skip\".\n"
+ "To abort and get back to the state before \"git cherry-pick\",\n"
+ "run \"git cherry-pick --abort\"."));
else if (opts->action == REPLAY_REVERT)
- advise(_("After resolving the conflicts, mark them with\n"
- "\"git add/rm <pathspec>\", then run\n"
- "\"git revert --continue\".\n"
- "You can instead skip this commit with \"git revert --skip\".\n"
- "To abort and get back to the state before \"git revert\",\n"
- "run \"git revert --abort\"."));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("After resolving the conflicts, mark them with\n"
+ "\"git add/rm <pathspec>\", then run\n"
+ "\"git revert --continue\".\n"
+ "You can instead skip this commit with \"git revert --skip\".\n"
+ "To abort and get back to the state before \"git revert\",\n"
+ "run \"git revert --abort\"."));
else
BUG("unexpected pick action in print_advice()");
}
"extensions.objectformat", value);
data->hash_algo = format;
return EXTENSION_OK;
+ } else if (!strcmp(ext, "compatobjectformat")) {
+ struct string_list_item *item;
+ int format;
+
+ if (!value)
+ return config_error_nonbool(var);
+ format = hash_algo_by_name(value);
+ if (format == GIT_HASH_UNKNOWN)
+ return error(_("invalid value for '%s': '%s'"),
+ "extensions.compatobjectformat", value);
+ /* For now only support compatObjectFormat being specified once. */
+ for_each_string_list_item(item, &data->v1_only_extensions) {
+ if (!strcmp(item->string, "compatobjectformat"))
+ return error(_("'%s' already specified as '%s'"),
+ "extensions.compatobjectformat",
+ hash_algos[data->compat_hash_algo].name);
+ }
+ data->compat_hash_algo = format;
+ return EXTENSION_OK;
} else if (!strcmp(ext, "refstorage")) {
unsigned int format;
}
if (startup_info->have_repository) {
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+ repo_set_compat_hash_algo(the_repository,
+ repo_fmt.compat_hash_algo);
repo_set_ref_storage_format(the_repository,
repo_fmt.ref_storage_format);
the_repository->repository_format_worktree_config =
check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1;
repo_set_hash_algo(the_repository, fmt->hash_algo);
+ repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
repo_set_ref_storage_format(the_repository,
fmt->ref_storage_format);
the_repository->repository_format_worktree_config =
int worktree_config;
int is_bare;
int hash_algo;
+ int compat_hash_algo;
unsigned int ref_storage_format;
int sparse_index;
char *work_tree;
ok 2 - plain with GIT_WORK_TREE
ok 3 - plain bare
+t/Makefile defines a target for each test file, such that you can also use
+shell pattern matching to run a subset of the tests:
+
+ make *checkout*
+
+will run all tests with 'checkout' in their filename.
+
Since the tests all output TAP (see https://testanything.org) they can
be run with any TAP harness. Here's an example of parallel testing
powered by a recent version of prove(1):
Note that we still &&-chain the loop to propagate failures from
earlier commands.
+ - Repeat tests with slightly different arguments in a loop.
+
+ In some cases it may make sense to re-run the same set of tests with
+ different options or commands to ensure that the command behaves
+ despite the different parameters. This can be achieved by looping
+ around a specific parameter:
+
+ for arg in '' "--foo"
+ do
+ test_expect_success "test command ${arg:-without arguments}" '
+ command $arg
+ '
+ done
+
+ Note that while the test title uses double quotes ("), the test body
+ should continue to use single quotes (') to avoid breakage in case the
+ values contain e.g. quoting characters. The loop variable will be
+ accessible regardless of the single quotes as the test body is passed
+ to `eval`.
+
And here are the "don'ts:"
"$(cat file.template)" &&
test_commit --author "B <B@test.git>" \
"change" "$fortran_file" \
- "$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+ "$(sed -e s/ChangeMe/IWasChanged/ file.template)" &&
check_count -f "$fortran_file" -L:RIGHT A 3 B 1
'
--- /dev/null
+#include "test-tool.h"
+#include "gpg-interface.h"
+#include "strbuf.h"
+
+
+int cmd__delete_gpgsig(int argc, const char **argv)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *pattern = "gpgsig";
+ const char *bufptr, *tail, *eol;
+ int deleting = 0;
+ size_t plen;
+
+ if (argc >= 2) {
+ pattern = argv[1];
+ argv++;
+ argc--;
+ }
+
+ plen = strlen(pattern);
+ strbuf_read(&buf, 0, 0);
+
+ if (!strcmp(pattern, "trailer")) {
+ size_t payload_size = parse_signed_buffer(buf.buf, buf.len);
+ fwrite(buf.buf, 1, payload_size, stdout);
+ fflush(stdout);
+ return 0;
+ }
+
+ bufptr = buf.buf;
+ tail = bufptr + buf.len;
+
+ while (bufptr < tail) {
+ /* Find the end of the line */
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ eol = tail;
+
+ /* Drop continuation lines */
+ if (deleting && (bufptr < eol) && (bufptr[0] == ' ')) {
+ bufptr = eol + 1;
+ continue;
+ }
+ deleting = 0;
+
+ /* Does the line match the prefix? */
+ if (((bufptr + plen) < eol) &&
+ !memcmp(bufptr, pattern, plen) &&
+ (bufptr[plen] == ' ')) {
+ deleting = 1;
+ bufptr = eol + 1;
+ continue;
+ }
+
+ /* Print all other lines */
+ fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
+ bufptr = eol + 1;
+ }
+ fflush(stdout);
+
+ return 0;
+}
{ "crontab", cmd__crontab },
{ "csprng", cmd__csprng },
{ "date", cmd__date },
+ { "delete-gpgsig", cmd__delete_gpgsig },
{ "delta", cmd__delta },
{ "dir-iterator", cmd__dir_iterator },
{ "drop-caches", cmd__drop_caches },
int cmd__csprng(int argc, const char **argv);
int cmd__date(int argc, const char **argv);
int cmd__delta(int argc, const char **argv);
+int cmd__delete_gpgsig(int argc, const char **argv);
int cmd__dir_iterator(int argc, const char **argv);
int cmd__drop_caches(int argc, const char **argv);
int cmd__dump_cache_tree(int argc, const char **argv);
find . -type d -name .git -prune -o -type f -print
) | sort >module-git-"$1".list &&
test_cmp module-cvs-"$1".list module-git-"$1".list &&
- cat module-cvs-"$1".list | while read f
+ while read f
do
test_cmp_branch_file "$1" "$f" || return 1
- done
+ done <module-cvs-"$1".list
}
done
git ls-tree -r HEAD >GEN_src_list
-nr_src_files=$(cat GEN_src_list | wc -l)
+nr_src_files=$(wc -l <GEN_src_list)
src_branch=$(git symbolic-ref --short HEAD)
test_expect_success 'check hash-object' '
echo "foo" >bar &&
- SHA=$(cat bar | git hash-object -w --stdin) &&
+ SHA=$(git hash-object -w --stdin <bar) &&
test_path_is_file "$REAL/objects/$(objpath $SHA)"
'
echo value40 >> expect &&
echo size >> in &&
echo 64 39 >> expect &&
- cat in | test-tool hashmap > out &&
+ test-tool hashmap <in >out &&
test_cmp expect out
'
test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
test_when_finished "git reset --hard HEAD^" &&
- cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
- cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+ write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+ write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
cp crlf.utf${i}.raw eol.utf${i} &&
cat >expectIndexLF <<-EOF &&
printf "Bjó til tóma Git lind" >expect &&
LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
test_when_finished "rm -rf repo" &&
- grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+ grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual
'
test_done
run_tests () {
type=$1
- sha1=$2
+ oid=$2
size=$3
content=$4
pretty_content=$5
- batch_output="$sha1 $type $size
+ batch_output="$oid $type $size
$content"
test_expect_success "$type exists" '
- git cat-file -e $sha1
+ git cat-file -e $oid
'
test_expect_success "Type of $type is correct" '
echo $type >expect &&
- git cat-file -t $sha1 >actual &&
+ git cat-file -t $oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of $type is correct" '
echo $size >expect &&
- git cat-file -s $sha1 >actual &&
+ git cat-file -s $oid >actual &&
test_cmp expect actual
'
test_expect_success "Type of $type is correct using --allow-unknown-type" '
echo $type >expect &&
- git cat-file -t --allow-unknown-type $sha1 >actual &&
+ git cat-file -t --allow-unknown-type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of $type is correct using --allow-unknown-type" '
echo $size >expect &&
- git cat-file -s --allow-unknown-type $sha1 >actual &&
+ git cat-file -s --allow-unknown-type $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "Content of $type is correct" '
echo_without_newline "$content" >expect &&
- git cat-file $type $sha1 >actual &&
+ git cat-file $type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Pretty content of $type is correct" '
echo_without_newline "$pretty_content" >expect &&
- git cat-file -p $sha1 >actual &&
+ git cat-file -p $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "--batch output of $type is correct" '
echo "$batch_output" >expect &&
- echo $sha1 | git cat-file --batch >actual &&
+ echo $oid | git cat-file --batch >actual &&
test_cmp expect actual
'
test_expect_success "--batch-check output of $type is correct" '
- echo "$sha1 $type $size" >expect &&
- echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+ echo "$oid $type $size" >expect &&
+ echo_without_newline $oid | git cat-file --batch-check >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "--batch-command $opt output of $type content is correct" '
echo "$batch_output" >expect &&
- test_write_lines "contents $sha1" | git cat-file --batch-command $opt >actual &&
+ test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual &&
test_cmp expect actual
'
test_expect_success "--batch-command $opt output of $type info is correct" '
- echo "$sha1 $type $size" >expect &&
- test_write_lines "info $sha1" |
+ echo "$oid $type $size" >expect &&
+ test_write_lines "info $oid" |
git cat-file --batch-command $opt >actual &&
test_cmp expect actual
'
done
test_expect_success "custom --batch-check format" '
- echo "$type $sha1" >expect &&
- echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+ echo "$type $oid" >expect &&
+ echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
test_cmp expect actual
'
test_expect_success "custom --batch-command format" '
- echo "$type $sha1" >expect &&
- echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+ echo "$type $oid" >expect &&
+ echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
test_cmp expect actual
'
test_expect_success '--batch-check with %(rest)' '
echo "$type this is some extra content" >expect &&
- echo "$sha1 this is some extra content" |
+ echo "$oid this is some extra content" |
git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
test_cmp expect actual
'
echo "$size" &&
echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objectsize)" >actual &&
+ echo $oid | git cat-file --batch="%(objectsize)" >actual &&
test_cmp expect actual
'
echo "$type" &&
echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objecttype)" >actual &&
+ echo $oid | git cat-file --batch="%(objecttype)" >actual &&
test_cmp expect actual
'
}
hello_content="Hello World"
hello_size=$(strlen "$hello_content")
-hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
test_expect_success "setup" '
+ git config core.repositoryformatversion 1 &&
+ git config extensions.objectformat $test_hash_algo &&
+ git config extensions.compatobjectformat $test_compat_hash_algo &&
echo_without_newline "$hello_content" > hello &&
git update-index --add hello
'
-run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+run_blob_tests () {
+ oid=$1
-test_expect_success '--batch-command --buffer with flush for blob info' '
- echo "$hello_sha1 blob $hello_size" >expect &&
- test_write_lines "info $hello_sha1" "flush" |
+ run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
+
+ test_expect_success '--batch-command --buffer with flush for blob info' '
+ echo "$oid blob $hello_size" >expect &&
+ test_write_lines "info $oid" "flush" |
GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
git cat-file --batch-command --buffer >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success '--batch-command --buffer without flush for blob info' '
+ test_expect_success '--batch-command --buffer without flush for blob info' '
touch output &&
- test_write_lines "info $hello_sha1" |
+ test_write_lines "info $oid" |
GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
git cat-file --batch-command --buffer >>output &&
test_must_be_empty output
-'
+ '
+}
+
+hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
+run_blob_tests $hello_oid
+run_blob_tests $hello_compat_oid
test_expect_success '--batch-check without %(rest) considers whole line' '
- echo "$hello_sha1 blob $hello_size" >expect &&
- git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+ echo "$hello_oid blob $hello_size" >expect &&
+ git update-index --add --cacheinfo 100644 $hello_oid "white space" &&
test_when_finished "git update-index --remove \"white space\"" &&
echo ":white space" | git cat-file --batch-check >actual &&
test_cmp expect actual
'
-tree_sha1=$(git write-tree)
+tree_oid=$(git write-tree)
+tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1 hello${LF}"
+tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))
+tree_pretty_content="100644 blob $hello_oid hello${LF}"
+tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}"
-run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
commit_message="Initial commit"
-commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
+commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
commit_size=$(($(test_oid hexsz) + 137))
-commit_content="tree $tree_sha1
+commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
+commit_content="tree $tree_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+$commit_message"
+
+commit_compat_content="tree $tree_compat_oid
author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
$commit_message"
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
-tag_header_without_timestamp="object $hello_sha1
-type blob
+tag_header_without_oid="type blob
tag hellotag
tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_header_without_timestamp="object $hello_oid
+$tag_header_without_oid"
+tag_compat_header_without_timestamp="object $hello_compat_oid
+$tag_header_without_oid"
tag_description="This is a tag"
tag_content="$tag_header_without_timestamp 0 +0000
$tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+$tag_description"
+
+tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
tag_size=$(strlen "$tag_content")
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content"
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
+
+run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
+run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
test_expect_success "Reach a blob from a tag pointing to it" '
echo_without_newline "$hello_content" >expect &&
- git cat-file blob $tag_sha1 >actual &&
+ git cat-file blob $tag_oid >actual &&
test_cmp expect actual
'
-for batch in batch batch-check batch-command
+for oid in $hello_oid $hello_compat_oid
do
- for opt in t s e p
+ for batch in batch batch-check batch-command
do
+ for opt in t s e p
+ do
test_expect_success "Passing -$opt with --$batch fails" '
- test_must_fail git cat-file --$batch -$opt $hello_sha1
+ test_must_fail git cat-file --$batch -$opt $oid
'
test_expect_success "Passing --$batch with -$opt fails" '
- test_must_fail git cat-file -$opt --$batch $hello_sha1
+ test_must_fail git cat-file -$opt --$batch $oid
'
- done
+ done
- test_expect_success "Passing <type> with --$batch fails" '
- test_must_fail git cat-file --$batch blob $hello_sha1
- '
+ test_expect_success "Passing <type> with --$batch fails" '
+ test_must_fail git cat-file --$batch blob $oid
+ '
- test_expect_success "Passing --$batch with <type> fails" '
- test_must_fail git cat-file blob --$batch $hello_sha1
- '
+ test_expect_success "Passing --$batch with <type> fails" '
+ test_must_fail git cat-file blob --$batch $oid
+ '
- test_expect_success "Passing sha1 with --$batch fails" '
- test_must_fail git cat-file --$batch $hello_sha1
- '
+ test_expect_success "Passing oid with --$batch fails" '
+ test_must_fail git cat-file --$batch $oid
+ '
+ done
done
-for opt in t s e p
+for oid in $hello_oid $hello_compat_oid
do
- test_expect_success "Passing -$opt with --follow-symlinks fails" '
- test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ for opt in t s e p
+ do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $oid
'
+ done
done
test_expect_success "--batch-check for a non-existent named object" '
test_expect_success "--batch for an existent and a non-existent hash" '
cat >expect <<-EOF &&
- $tag_sha1 tag $tag_size
+ $tag_oid tag $tag_size
$tag_content
0000000000000000000000000000000000000000 missing
EOF
- printf "$tag_sha1\n0000000000000000000000000000000000000000" >in &&
+ printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
git cat-file --batch <in >actual &&
test_cmp expect actual
'
test_cmp expect actual
'
-batch_input="$hello_sha1
-$commit_sha1
-$tag_sha1
+batch_tests () {
+ boid=$1
+ loid=$2
+ lsize=$3
+ coid=$4
+ csize=$5
+ ccontent=$6
+ toid=$7
+ tsize=$8
+ tcontent=$9
+
+ batch_input="$boid
+$coid
+$toid
deadbeef
"
-printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
+ printf "%s\0" \
+ "$boid blob $hello_size" \
"$hello_content" \
- "$commit_sha1 commit $commit_size" \
- "$commit_content" \
- "$tag_sha1 tag $tag_size" \
- "$tag_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
"deadbeef missing" \
" missing" >batch_output
-test_expect_success '--batch with multiple sha1s gives correct format' '
+ test_expect_success '--batch with multiple oids gives correct format' '
tr "\0" "\n" <batch_output >expect &&
echo_without_newline "$batch_input" >in &&
git cat-file --batch <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success '--batch, -z with multiple sha1s gives correct format' '
+ test_expect_success '--batch, -z with multiple oids gives correct format' '
echo_without_newline_nul "$batch_input" >in &&
tr "\0" "\n" <batch_output >expect &&
git cat-file --batch -z <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success '--batch, -Z with multiple sha1s gives correct format' '
+ test_expect_success '--batch, -Z with multiple oids gives correct format' '
echo_without_newline_nul "$batch_input" >in &&
git cat-file --batch -Z <in >actual &&
test_cmp batch_output actual
-'
+ '
-batch_check_input="$hello_sha1
-$tree_sha1
-$commit_sha1
-$tag_sha1
+batch_check_input="$boid
+$loid
+$coid
+$toid
deadbeef
"
-printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
- "$tree_sha1 tree $tree_size" \
- "$commit_sha1 commit $commit_size" \
- "$tag_sha1 tag $tag_size" \
+ printf "%s\0" \
+ "$boid blob $hello_size" \
+ "$loid tree $lsize" \
+ "$coid commit $csize" \
+ "$toid tag $tsize" \
"deadbeef missing" \
" missing" >batch_check_output
-test_expect_success "--batch-check with multiple sha1s gives correct format" '
+ test_expect_success "--batch-check with multiple oids gives correct format" '
tr "\0" "\n" <batch_check_output >expect &&
echo_without_newline "$batch_check_input" >in &&
git cat-file --batch-check <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
+ test_expect_success "--batch-check, -z with multiple oids gives correct format" '
tr "\0" "\n" <batch_check_output >expect &&
echo_without_newline_nul "$batch_check_input" >in &&
git cat-file --batch-check -z <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success "--batch-check, -Z with multiple sha1s gives correct format" '
+ test_expect_success "--batch-check, -Z with multiple oids gives correct format" '
echo_without_newline_nul "$batch_check_input" >in &&
git cat-file --batch-check -Z <in >actual &&
test_cmp batch_check_output actual
-'
-
-test_expect_success FUNNYNAMES 'setup with newline in input' '
- touch -- "newline${LF}embedded" &&
- git add -- "newline${LF}embedded" &&
- git commit -m "file with newline embedded" &&
- test_tick &&
-
- printf "HEAD:newline${LF}embedded" >in
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
- git cat-file --batch-check -z <in >actual &&
- echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
- test_cmp expect actual
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
- git cat-file --batch-check -Z <in >actual &&
- printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
- test_cmp expect actual
-'
+ '
-batch_command_multiple_info="info $hello_sha1
-info $tree_sha1
-info $commit_sha1
-info $tag_sha1
+batch_command_multiple_info="info $boid
+info $loid
+info $coid
+info $toid
info deadbeef"
-test_expect_success '--batch-command with multiple info calls gives correct format' '
+ test_expect_success '--batch-command with multiple info calls gives correct format' '
cat >expect <<-EOF &&
- $hello_sha1 blob $hello_size
- $tree_sha1 tree $tree_size
- $commit_sha1 commit $commit_size
- $tag_sha1 tag $tag_size
+ $boid blob $hello_size
+ $loid tree $lsize
+ $coid commit $csize
+ $toid tag $tsize
deadbeef missing
EOF
git cat-file --batch-command --buffer -Z <in >actual &&
test_cmp expect_nul actual
-'
+ '
-batch_command_multiple_contents="contents $hello_sha1
-contents $commit_sha1
-contents $tag_sha1
+batch_command_multiple_contents="contents $boid
+contents $coid
+contents $toid
contents deadbeef
flush"
-test_expect_success '--batch-command with multiple command calls gives correct format' '
+ test_expect_success '--batch-command with multiple command calls gives correct format' '
printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
+ "$boid blob $hello_size" \
"$hello_content" \
- "$commit_sha1 commit $commit_size" \
- "$commit_content" \
- "$tag_sha1 tag $tag_size" \
- "$tag_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
"deadbeef missing" >expect_nul &&
tr "\0" "\n" <expect_nul >expect &&
git cat-file --batch-command --buffer -Z <in >actual &&
test_cmp expect_nul actual
+ '
+
+}
+
+batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
+batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
+
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
+ touch -- "newline${LF}embedded" &&
+ git add -- "newline${LF}embedded" &&
+ git commit -m "file with newline embedded" &&
+ test_tick &&
+
+ printf "HEAD:newline${LF}embedded" >in
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+ git cat-file --batch-check -z <in >actual &&
+ echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+ git cat-file --batch-check -Z <in >actual &&
+ printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
'
test_expect_success 'setup blobs which are likely to delta' '
# we will check only that one of the two objects is a delta
# against the other, but not the order. We can do so by just
# asking for the base of both, and checking whether either
-# sha1 appears in the output.
+# oid appears in the output.
test_expect_success '%(deltabase) reports packed delta bases' '
git repack -ad &&
git cat-file --batch-check="%(deltabase)" <blobs >actual &&
bogus_short_type="bogus" &&
bogus_short_content="bogus" &&
bogus_short_size=$(strlen "$bogus_short_content") &&
- bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+ bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
bogus_long_content="bogus" &&
bogus_long_size=$(strlen "$bogus_long_content") &&
- bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+ bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
'
for arg1 in '' --allow-unknown-type
if test "$arg1" = "--allow-unknown-type"
then
- git cat-file $arg1 $arg2 $bogus_short_sha1
+ git cat-file $arg1 $arg2 $bogus_short_oid
else
- test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+ test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
fi
if test "$arg2" = "-p"
then
cat >expect <<-EOF
- error: header for $bogus_long_sha1 too long, exceeds 32 bytes
- fatal: Not a valid object name $bogus_long_sha1
+ error: header for $bogus_long_oid too long, exceeds 32 bytes
+ fatal: Not a valid object name $bogus_long_oid
EOF
else
cat >expect <<-EOF
- error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+ error: header for $bogus_long_oid too long, exceeds 32 bytes
fatal: git cat-file: could not get object info
EOF
fi &&
if test "$arg1" = "--allow-unknown-type"
then
- git cat-file $arg1 $arg2 $bogus_short_sha1
+ git cat-file $arg1 $arg2 $bogus_short_oid
else
- test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+ test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
fi
done
test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
- git cat-file -e $bogus_short_sha1
+ git cat-file -e $bogus_short_oid
'
test_expect_success '-e can not be combined with --allow-unknown-type' '
- test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
+ test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid
'
test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
- test_must_fail git cat-file -p $bogus_short_sha1 &&
- test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+ test_must_fail git cat-file -p $bogus_short_oid &&
+ test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
'
test_expect_success '<type> <hash> does not work with objects of broken types' '
cat >err.expect <<-\EOF &&
fatal: invalid object type "bogus"
EOF
- test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
+ test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual &&
test_cmp err.expect err.actual
'
test_expect_success 'broken types combined with --batch and --batch-check' '
- echo $bogus_short_sha1 >bogus-oid &&
+ echo $bogus_short_oid >bogus-oid &&
cat >err.expect <<-\EOF &&
fatal: invalid object type
cat >expect <<-EOF &&
$bogus_short_type
EOF
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual &&
# Create it manually, as "git replace" will die on bogus
# types.
head=$(git rev-parse --verify HEAD) &&
- test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
- test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
+ test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
cat >expect <<-EOF &&
commit
EOF
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success "Type of broken object is correct" '
echo $bogus_short_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of broken object is correct" '
echo $bogus_short_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success 'clean up broken object' '
- rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+ rm .git/objects/$(test_oid_to_path $bogus_short_oid)
'
test_expect_success "Type of broken object is correct when type is large" '
echo $bogus_long_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of large broken object is correct when type is large" '
echo $bogus_long_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
+ git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'
test_expect_success 'clean up broken object' '
- rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+ rm .git/objects/$(test_oid_to_path $bogus_long_oid)
'
test_expect_success 'cat-file -t and -s on corrupt loose object' '
test_ln_s_add loop2 loop1 &&
git add morx dir/subdir/ind2 dir/ind1 &&
git commit -am "test" &&
- echo $hello_sha1 blob $hello_size >found
+ echo $hello_oid blob $hello_size >found
'
test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
echo HEAD:dirlink/morx >>expect &&
echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp expect actual &&
- echo $hello_sha1 blob $hello_size >expect &&
+ echo $hello_oid blob $hello_size >expect &&
echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp expect actual
'
path0_sha=$(git hash-object --path=file0 file1) &&
test "$file0_sha" = "$path0_sha" &&
test "$file1_sha" = "$path1_sha" &&
- path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
- path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+ path1_sha=$(git hash-object --path=file1 --stdin <file0) &&
+ path0_sha=$(git hash-object --path=file0 --stdin <file1) &&
test "$file0_sha" = "$path0_sha" &&
test "$file1_sha" = "$path1_sha"
'
test_expect_success 'check that --no-filters option works' '
nofilters_file1=$(git hash-object --no-filters file1) &&
test "$file0_sha" = "$nofilters_file1" &&
- nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+ nofilters_file1=$(git hash-object --stdin <file1) &&
test "$file0_sha" = "$nofilters_file1"
'
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2023 Eric Biederman
+#
+
+test_description='Test how well compatObjectFormat works'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+# All of the follow variables must be defined in the environment:
+# GIT_AUTHOR_NAME
+# GIT_AUTHOR_EMAIL
+# GIT_AUTHOR_DATE
+# GIT_COMMITTER_NAME
+# GIT_COMMITTER_EMAIL
+# GIT_COMMITTER_DATE
+#
+# The test relies on these variables being set so that the two
+# different commits in two different repositories encoded with two
+# different hash functions result in the same content in the commits.
+# This means that when the commit is translated between hash functions
+# the commit is identical to the commit in the other repository.
+
+compat_hash () {
+ case "$1" in
+ "sha1")
+ echo "sha256"
+ ;;
+ "sha256")
+ echo "sha1"
+ ;;
+ esac
+}
+
+hello_oid () {
+ case "$1" in
+ "sha1")
+ echo "$hello_sha1_oid"
+ ;;
+ "sha256")
+ echo "$hello_sha256_oid"
+ ;;
+ esac
+}
+
+tree_oid () {
+ case "$1" in
+ "sha1")
+ echo "$tree_sha1_oid"
+ ;;
+ "sha256")
+ echo "$tree_sha256_oid"
+ ;;
+ esac
+}
+
+commit_oid () {
+ case "$1" in
+ "sha1")
+ echo "$commit_sha1_oid"
+ ;;
+ "sha256")
+ echo "$commit_sha256_oid"
+ ;;
+ esac
+}
+
+commit2_oid () {
+ case "$1" in
+ "sha1")
+ echo "$commit2_sha1_oid"
+ ;;
+ "sha256")
+ echo "$commit2_sha256_oid"
+ ;;
+ esac
+}
+
+del_sigcommit () {
+ local delete=$1
+
+ if test "$delete" = "sha256" ; then
+ local pattern="gpgsig-sha256"
+ else
+ local pattern="gpgsig"
+ fi
+ test-tool delete-gpgsig "$pattern"
+}
+
+
+del_sigtag () {
+ local storage=$1
+ local delete=$2
+
+ if test "$storage" = "$delete" ; then
+ local pattern="trailer"
+ elif test "$storage" = "sha256" ; then
+ local pattern="gpgsig"
+ else
+ local pattern="gpgsig-sha256"
+ fi
+ test-tool delete-gpgsig "$pattern"
+}
+
+base=$(pwd)
+for hash in sha1 sha256
+do
+ cd "$base"
+ mkdir -p repo-$hash
+ cd repo-$hash
+
+ test_expect_success "setup $hash repository" '
+ git init --object-format=$hash &&
+ git config core.repositoryformatversion 1 &&
+ git config extensions.objectformat $hash &&
+ git config extensions.compatobjectformat $(compat_hash $hash) &&
+ git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+ echo "Hellow World!" > hello &&
+ eval hello_${hash}_oid=$(git hash-object hello) &&
+ git update-index --add hello &&
+ git commit -m "Initial commit" &&
+ eval commit_${hash}_oid=$(git rev-parse HEAD) &&
+ eval tree_${hash}_oid=$(git rev-parse HEAD^{tree})
+ '
+ test_expect_success "create a $hash tagged blob" '
+ git tag --no-sign -m "This is a tag" hellotag $(hello_oid $hash) &&
+ eval hellotag_${hash}_oid=$(git rev-parse hellotag)
+ '
+ test_expect_success "create a $hash tagged tree" '
+ git tag --no-sign -m "This is a tag" treetag $(tree_oid $hash) &&
+ eval treetag_${hash}_oid=$(git rev-parse treetag)
+ '
+ test_expect_success "create a $hash tagged commit" '
+ git tag --no-sign -m "This is a tag" committag $(commit_oid $hash) &&
+ eval committag_${hash}_oid=$(git rev-parse committag)
+ '
+ test_expect_success GPG2 "create a $hash signed commit" '
+ git commit --gpg-sign --allow-empty -m "This is a signed commit" &&
+ eval signedcommit_${hash}_oid=$(git rev-parse HEAD)
+ '
+ test_expect_success GPG2 "create a $hash signed tag" '
+ git tag -s -m "This is a signed tag" signedtag HEAD &&
+ eval signedtag_${hash}_oid=$(git rev-parse signedtag)
+ '
+ test_expect_success "create a $hash branch" '
+ git checkout -b branch $(commit_oid $hash) &&
+ echo "More more more give me more!" > more &&
+ eval more_${hash}_oid=$(git hash-object more) &&
+ echo "Another and another and another" > another &&
+ eval another_${hash}_oid=$(git hash-object another) &&
+ git update-index --add more another &&
+ git commit -m "Add more files!" &&
+ eval commit2_${hash}_oid=$(git rev-parse HEAD) &&
+ eval tree2_${hash}_oid=$(git rev-parse HEAD^{tree})
+ '
+ test_expect_success GPG2 "create another $hash signed tag" '
+ git tag -s -m "This is another signed tag" signedtag2 $(commit2_oid $hash) &&
+ eval signedtag2_${hash}_oid=$(git rev-parse signedtag2)
+ '
+ test_expect_success GPG2 "merge the $hash branches together" '
+ git merge -S -m "merge some signed tags together" signedtag signedtag2 &&
+ eval signedcommit2_${hash}_oid=$(git rev-parse HEAD)
+ '
+ test_expect_success GPG2 "create additional $hash signed commits" '
+ git commit --gpg-sign --allow-empty -m "This is an additional signed commit" &&
+ git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" &&
+ git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" &&
+ eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) &&
+ eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4)
+ '
+ test_expect_success GPG2 "create additional $hash signed tags" '
+ git tag -s -m "This is an additional signed tag" signedtag34 HEAD &&
+ git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 &&
+ git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 &&
+ eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
+ eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
+ '
+done
+cd "$base"
+
+compare_oids () {
+ test "$#" = 5 && { local PREREQ=$1; shift; } || PREREQ=
+ local type="$1"
+ local name="$2"
+ local sha1_oid="$3"
+ local sha256_oid="$4"
+
+ echo ${sha1_oid} > ${name}_sha1_expected
+ echo ${sha256_oid} > ${name}_sha256_expected
+ echo ${type} > ${name}_type_expected
+
+ git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found
+ git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found
+ local sha1_sha256_oid=$(cat ${name}_sha1_sha256_found)
+ local sha256_sha1_oid=$(cat ${name}_sha256_sha1_found)
+
+ test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" '
+ git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 &&
+ test_cmp ${name}_sha1 ${name}_sha1_expected
+'
+
+ test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" '
+ git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 &&
+ test_cmp ${name}_sha256 ${name}_sha256_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 type" '
+ git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 &&
+ git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 &&
+ test_cmp ${name}_type1 ${name}_type2 &&
+ test_cmp ${name}_type1 ${name}_type_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 type" '
+ git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 &&
+ git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 &&
+ test_cmp ${name}_type3 ${name}_type4 &&
+ test_cmp ${name}_type3 ${name}_type_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 size" '
+ git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 &&
+ git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 &&
+ test_cmp ${name}_size1 ${name}_size2
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 size" '
+ git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 &&
+ git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 &&
+ test_cmp ${name}_size3 ${name}_size4
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" '
+ git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 &&
+ git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 &&
+ test_cmp ${name}_content1 ${name}_content2
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" '
+ git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 &&
+ git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 &&
+ test_cmp ${name}_content3 ${name}_content4
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 content" '
+ git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 &&
+ git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 &&
+ test_cmp ${name}_content5 ${name}_content6
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 content" '
+ git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 &&
+ git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 &&
+ test_cmp ${name}_content7 ${name}_content8
+'
+
+}
+
+compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid"
+compare_oids 'tree' tree "$tree_sha1_oid" "$tree_sha256_oid"
+compare_oids 'commit' commit "$commit_sha1_oid" "$commit_sha256_oid"
+compare_oids GPG2 'commit' signedcommit "$signedcommit_sha1_oid" "$signedcommit_sha256_oid"
+compare_oids 'tag' hellotag "$hellotag_sha1_oid" "$hellotag_sha256_oid"
+compare_oids 'tag' treetag "$treetag_sha1_oid" "$treetag_sha256_oid"
+compare_oids 'tag' committag "$committag_sha1_oid" "$committag_sha256_oid"
+compare_oids GPG2 'tag' signedtag "$signedtag_sha1_oid" "$signedtag_sha256_oid"
+
+compare_oids 'blob' more "$more_sha1_oid" "$more_sha256_oid"
+compare_oids 'blob' another "$another_sha1_oid" "$another_sha256_oid"
+compare_oids 'tree' tree2 "$tree2_sha1_oid" "$tree2_sha256_oid"
+compare_oids 'commit' commit2 "$commit2_sha1_oid" "$commit2_sha256_oid"
+compare_oids GPG2 'tag' signedtag2 "$signedtag2_sha1_oid" "$signedtag2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit2 "$signedcommit2_sha1_oid" "$signedcommit2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit3 "$signedcommit3_sha1_oid" "$signedcommit3_sha256_oid"
+compare_oids GPG2 'commit' signedcommit4 "$signedcommit4_sha1_oid" "$signedcommit4_sha256_oid"
+compare_oids GPG2 'tag' signedtag3 "$signedtag3_sha1_oid" "$signedtag3_sha256_oid"
+compare_oids GPG2 'tag' signedtag4 "$signedtag4_sha1_oid" "$signedtag4_sha256_oid"
+
+test_done
--- /dev/null
+#!/bin/sh
+exec gpg --faked-system-time "20230918T154812" "$@"
git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
>check-rules-file <all-files &&
- cat rules | git -C repo sparse-checkout set --no-cone --stdin &&
+ git -C repo sparse-checkout set --no-cone --stdin <rules &&
git -C repo ls-files -t >out &&
sed -n "/^S /!s/^. //p" out >ls-files &&
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+test_expect_success 'setup whitespace config' '
+ sed -e "s/^|//" \
+ -e "s/[$]$//" \
+ -e "s/X/ /g" >.git/config <<-\EOF
+ [section]
+ | solid = rock
+ | sparse = big XX blue
+ | sparseAndTail = big XX blue $
+ | sparseAndTailQuoted = "big XX blue "
+ | sparseAndBiggerTail = big XX blue X X
+ | sparseAndBiggerTailQuoted = "big XX blue X X"
+ | sparseAndBiggerTailQuotedPlus = "big XX blue X X"X $
+ | headAndTail = Xbig blue $
+ | headAndTailQuoted = "Xbig blue "
+ | headAndTailQuotedPlus = "Xbig blue " $
+ | annotated = big blueX# to be discarded
+ | annotatedQuoted = "big blue"X# to be discarded
+ EOF
+'
+
+test_expect_success 'no internal whitespace' '
+ echo "rock" >expect &&
+ git config --get section.solid >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparse >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparseAndTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace, all quoted' '
+ echo "big QQ blue " | q_to_tab >expect &&
+ git config --get section.sparseAndTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, all quoted' '
+ echo "big QQ blue Q Q" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, not all quoted' '
+ echo "big QQ blue Q Q" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTailQuotedPlus >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace' '
+ echo "big blue" >expect &&
+ git config --get section.headAndTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, all quoted' '
+ echo "Qbig blue " | q_to_tab >expect &&
+ git config --get section.headAndTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, not all quoted' '
+ echo "Qbig blue " | q_to_tab >expect &&
+ git config --get section.headAndTailQuotedPlus >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline comment' '
+ echo "big blue" >expect &&
+ git config --get section.annotated >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline comment, quoted' '
+ echo "big blue" >expect &&
+ git config --get section.annotatedQuoted >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'clear default config' '
rm -f .git/config
'
test_cmp expect result
'
-test_expect_success 'inner whitespace kept verbatim' '
- git config section.val "foo bar" &&
- test_cmp_config "foo bar" section.val
+test_expect_success 'inner whitespace kept verbatim, spaces only' '
+ echo "foo bar" >expect &&
+ git config section.val "foo bar" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
+ echo "fooQQbar" | q_to_tab >expect &&
+ git config section.val "$(cat expect)" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
+ echo "foo Q bar" | q_to_tab >expect &&
+ git config section.val "$(cat expect)" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'symlinked configuration' '
# env might slip through, see test-lib.sh, unset.*PERL_PATH
sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
for cmd in git $BB;do
- ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+ ldd $cmd | sed -n '/\//s,.*\s\(/[^ ]*\).*,\1,p' | while read i; do
mkdir -p "$R$(dirname $i)"
cp "$i" "$R/$i"
done
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
test_description='git restore --patch'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
test_expect_success 'setup' '
test_description='restore --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
# ...and that the comments for those sections are also
# preserved.
- cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+ sed "s/\"source\"/\"dest\"/" config.branch >expect &&
sed -n -e "/Note the lack/,\$p" .git/config >actual &&
test_cmp expect actual
'
${LF}
EOF
- cat expect | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <expect >blob &&
git notes add -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual &&
second-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add --stripspace -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual
third-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add -C $(cat blob) -m "third-line" &&
git notes show >actual &&
test_cmp expect actual
second-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add -m "first-line" -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual
check_resolve_fails
'
+test_expect_success POSIXPERM,SANITY 'unwritable rebased-patches does not leak' '
+ >.git/rebased-patches &&
+ chmod a-w .git/rebased-patches &&
+
+ git checkout -b side HEAD^ &&
+ test_commit unrelated &&
+ test_must_fail git rebase --apply --onto tmp HEAD^
+'
+
test_done
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".
+ hint: Disable this message with "git config advice.mergeConflict false"
EOF
test_commit --append --no-tag "double-add dream" dream dream &&
test_must_fail git revert HEAD^ 2>actual &&
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
+ hint: Disable this message with "git config advice.mergeConflict false"
EOF
test_must_fail git cherry-pick picked 2>actual &&
error: could not apply \$picked... picked
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
+ hint: Disable this message with \"git config advice.mergeConflict false\"
EOF
test_must_fail git cherry-pick --no-commit picked 2>actual &&
create_crlf_ref () {
branch="$1" &&
cat >.crlf-orig-$branch.txt &&
- cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+ append_cr <.crlf-orig-$branch.txt >.crlf-message-$branch.txt &&
grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
git branch -v >tmp &&
# Remove first two columns, and the line for the currently checked out branch
current=$(git branch --show-current) &&
- grep -v $current <tmp | awk "{\$1=\$2=\"\"}1" >actual &&
+ awk "/$current/ { next } { \$1 = \$2 = \"\" } 1" <tmp >actual &&
test_cmp expect actual
'
test_expect_success 'diff can read from stdin' '
test_must_fail git diff --no-index -- MN - < NN |
- grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+ sed "/^index/d; s#/-#/NN#" >.test-a &&
test_must_fail git diff --no-index -- MN NN |
grep -v "^index" >.test-b &&
test_cmp .test-a .test-b
test_cmp expect actual.paths
}
-test_expect_success 'diff-files does not respect diff.noprefix' '
- git -c diff.noprefix diff-files -p >actual &&
+test_expect_success 'diff-files does not respect diff.noPrefix' '
+ git -c diff.noPrefix diff-files -p >actual &&
check_prefix actual a/file0 b/file0
'
check_prefix actual file0 file0
'
-test_expect_success 'diff respects diff.noprefix' '
- git -c diff.noprefix diff >actual &&
+test_expect_success 'diff respects diff.noPrefix' '
+ git -c diff.noPrefix diff >actual &&
check_prefix actual file0 file0
'
-test_expect_success 'diff --default-prefix overrides diff.noprefix' '
- git -c diff.noprefix diff --default-prefix >actual &&
+test_expect_success 'diff --default-prefix overrides diff.noPrefix' '
+ git -c diff.noPrefix diff --default-prefix >actual &&
check_prefix actual a/file0 b/file0
'
-test_expect_success 'diff respects diff.mnemonicprefix' '
- git -c diff.mnemonicprefix diff >actual &&
+test_expect_success 'diff respects diff.mnemonicPrefix' '
+ git -c diff.mnemonicPrefix diff >actual &&
check_prefix actual i/file0 w/file0
'
-test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
- git -c diff.mnemonicprefix diff --default-prefix >actual &&
+test_expect_success 'diff --default-prefix overrides diff.mnemonicPrefix' '
+ git -c diff.mnemonicPrefix diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.srcPrefix' '
+ git -c diff.srcPrefix=x/ diff >actual &&
+ check_prefix actual x/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.dstPrefix' '
+ git -c diff.dstPrefix=y/ diff >actual &&
+ check_prefix actual a/file0 y/file0
+'
+
+test_expect_success 'diff --src-prefix overrides diff.srcPrefix' '
+ git -c diff.srcPrefix=y/ diff --src-prefix=z/ >actual &&
+ check_prefix actual z/file0 b/file0
+'
+
+test_expect_success 'diff --dst-prefix overrides diff.dstPrefix' '
+ git -c diff.dstPrefix=y/ diff --dst-prefix=z/ >actual &&
+ check_prefix actual a/file0 z/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.noPrefix' '
+ git -c diff.dstPrefix=y/ -c diff.srcPrefix=x/ -c diff.noPrefix diff >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.mnemonicPrefix' '
+ git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ -c diff.mnemonicPrefix diff >actual &&
+ check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with --default-prefix' '
+ git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ diff --default-prefix >actual &&
check_prefix actual a/file0 b/file0
'
test_expect_success 'external diff with autocrlf = true' '
test_config core.autocrlf true &&
GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
- test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+ test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
'
test_expect_success 'diff --cached' '
test_expect_success 'skip an empty patch in the middle of an am session' '
git checkout empty-commit^ &&
- test_must_fail git am empty-commit.patch >err &&
- grep "Patch is empty." err &&
+ test_must_fail git am empty-commit.patch >out 2>err &&
+ grep "Patch is empty." out &&
grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
git am --skip &&
test_path_is_missing .git/rebase-apply &&
test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
git checkout empty-commit^ &&
- test_must_fail git am empty-commit.patch >err &&
- grep "Patch is empty." err &&
+ test_must_fail git am empty-commit.patch >out 2>err &&
+ grep "Patch is empty." out &&
grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
git am --allow-empty >output &&
grep "No changes - recorded it as an empty commit." output &&
for r in $revs
do
git show -s --pretty=oneline "$r" >raw &&
- cat raw | lf_to_nul || return 1
+ lf_to_nul <raw || return 1
done >expect &&
# the trailing NUL is already produced so we do not need to
# output another one
# Also, it had the unwanted side-effect of deleting f.
test_expect_success 'try to apply corrupted patch' '
test_when_finished "git am --abort" &&
- test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
+ test_must_fail git -c advice.amWorkDir=false -c advice.mergeConflict=false am bad-patch.diff 2>actual &&
echo "error: git diff header lacks filename information (line 4)" >expected &&
test_path_is_file f &&
test_cmp expected actual
# First, check that the bar that appears at stage 3 does not
# correspond to an individual blob anywhere in history
#
- hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
git rev-list --objects --all >all_blobs &&
! grep $hash all_blobs &&
# First, check that the bar that appears at stage 3 does not
# correspond to an individual blob anywhere in history
#
- hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
git rev-list --objects --all >all_blobs &&
! grep $hash all_blobs &&
# conflict entries do not appear as individual blobs anywhere
# in history.
#
- hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
- hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+ hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
+ hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
git rev-list --objects --all >all_blobs &&
! grep $hash1 all_blobs &&
! grep $hash2 all_blobs &&
git mailsplit -d3 -o. "$DATA/nul-plain" &&
test_cmp "$DATA/nul-plain" 001 &&
- (cat 001 | git mailinfo msg patch) &&
+ git mailinfo msg patch <001 &&
test_line_count = 4 patch
'
must_pass_arg="$2" &&
(
cd strict &&
- test_expect_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack"
+ test_must_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack" &&
git index-pack "$must_pass_arg" "test-$(cat pack-name).pack"
)
}
test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
- for id in `cat expected | sed "s|..|&/|"`
+ for id in `sed "s|..|&/|" expected`
do
rm r1/.git/objects/$id || return 1
done
remote: STDERR post-update
EOF
test_expect_success 'send-pack stderr contains hook messages' '
- grep ^remote: send.err | sed "s/ *\$//" >actual &&
+ sed -n "/^remote:/s/ *\$//p" send.err >actual &&
test_cmp expect actual
'
EOF
sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
) >expect.in &&
- key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+ key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
noop=$(git rev-parse noop) &&
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
+test_expect_success 'clone with includeIf' '
+ test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+
+ test_when_finished "rm \"$HOME\"/.gitconfig" &&
+ cat >"$HOME"/.gitconfig <<-EOF &&
+ [includeIf "onbranch:something"]
+ path = /does/not/exist.inc
+ EOF
+ git clone $HTTPD_URL/smart/repo.git repo
+'
+
test_expect_success 'partial clone using HTTP' '
partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
'
export GIT_DIR
force=
+object_format=
mkdir -p "$dir"
echo
;;
list)
- echo ":object-format $(git rev-parse --show-object-format=storage)"
+ test -n "$object_format" &&
+ echo ":object-format $(git rev-parse --show-object-format=storage)"
git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
head=$(git symbolic-ref HEAD)
echo "@$head HEAD"
awk -f print_2.awk ls_files_result |
sort >expected &&
- for id in `cat expected | sed "s|..|&/|"`
+ for id in `sed "s|..|&/|" expected`
do
rm r1/.git/objects/$id || return 1
done &&
test_expect_success 'Check "ours" is CRLF' '
git reset --hard initial &&
git merge side -s ours &&
- cat file | remove_cr | append_cr >file.temp &&
+ remove_cr <file | append_cr >file.temp &&
test_cmp file file.temp
'
test_expect_success 'Check that conflict file is CRLF' '
git reset --hard a &&
test_must_fail git merge side &&
- cat file | remove_cr | append_cr >file.temp &&
+ remove_cr <file | append_cr >file.temp &&
test_cmp file file.temp
'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
test_cmp merged file
'
+test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >expect &&
+ cat expect >fild &&
+ cat expect >file &&
+ test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err &&
+ test_cmp expect file &&
+ test_cmp expect fild &&
+ echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >expect &&
+ cat expect >fild &&
+ cat expect >file &&
+ test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err &&
+ test_cmp expect file &&
+ test_cmp expect fild &&
+ echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'checkout --no-merge --conflict=diff3 does merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >fild &&
+ echo "none of the above" >file &&
+ git checkout --no-merge --conflict=diff3 -- fild file &&
+ echo "ourside" >expect &&
+ test_cmp expect fild &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ ||||||| base
+ original
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >fild &&
+ echo "none of the above" >file &&
+ git checkout --merge --conflict=diff3 --no-conflict -- fild file &&
+ echo "ourside" >expect &&
+ test_cmp expect fild &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'checkout with invalid conflict style' '
+ test_must_fail git checkout --conflict=bad 2>actual -- file &&
+ echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'failing checkout -b should not break working tree' '
git clean -fd && # Remove untracked files in the way
git reset --hard main &&
Untracked files not listed (use -u option to show untracked files)
EOF
git status -uno >output &&
+ test_cmp expect output &&
+ git status -ufalse >output &&
test_cmp expect output
'
-test_expect_success 'status (status.showUntrackedFiles no)' '
- test_config status.showuntrackedfiles no &&
- git status >output &&
- test_cmp expect output
-'
+for no in no false 0
+do
+ test_expect_success "status (status.showUntrackedFiles $no)" '
+ test_config status.showuntrackedfiles "$no" &&
+ git status >output &&
+ test_cmp expect output
+ '
+done
test_expect_success 'status -uno (advice.statusHints false)' '
cat >expect <<EOF &&
EOF
git status -unormal >output &&
+ test_cmp expect output &&
+ git status -utrue >output &&
+ test_cmp expect output &&
+ git status -uyes >output &&
test_cmp expect output
'
-test_expect_success 'status (status.showUntrackedFiles normal)' '
- test_config status.showuntrackedfiles normal &&
- git status >output &&
- test_cmp expect output
-'
+for normal in normal true 1
+do
+ test_expect_success "status (status.showUntrackedFiles $normal)" '
+ test_config status.showuntrackedfiles $normal &&
+ git status >output &&
+ test_cmp expect output
+ '
+done
cat >expect <<EOF
M dir1/modified
# ...in other words, the combined contents of this
# repository and expired.git should be the same as the
# set of objects we started with.
- cat expired.objects remaining.objects | sort >actual &&
+ sort expired.objects remaining.objects >actual &&
test_cmp expect actual &&
# The "moved" objects (i.e., those in expired.git)
for opt in '' '--dir-diff'
do
- test_expect_success "difftool ${opt} ignores exit code" "
+ test_expect_success "difftool ${opt:-without options} ignores exit code" '
test_config difftool.error.cmd false &&
git difftool ${opt} -y -t error branch
- "
+ '
- test_expect_success "difftool ${opt} forwards exit code with --trust-exit-code" "
+ test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code" '
test_config difftool.error.cmd false &&
test_must_fail git difftool ${opt} -y --trust-exit-code -t error branch
- "
+ '
- test_expect_success "difftool ${opt} forwards exit code with --trust-exit-code for built-ins" "
+ test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code for built-ins" '
test_config difftool.vimdiff.path false &&
test_must_fail git difftool ${opt} -y --trust-exit-code -t vimdiff branch
- "
+ '
- test_expect_success "difftool ${opt} honors difftool.trustExitCode = true" "
+ test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = true" '
test_config difftool.error.cmd false &&
test_config difftool.trustExitCode true &&
test_must_fail git difftool ${opt} -y -t error branch
- "
+ '
- test_expect_success "difftool ${opt} honors difftool.trustExitCode = false" "
+ test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = false" '
test_config difftool.error.cmd false &&
test_config difftool.trustExitCode false &&
git difftool ${opt} -y -t error branch
- "
+ '
- test_expect_success "difftool ${opt} ignores exit code with --no-trust-exit-code" "
+ test_expect_success "difftool ${opt:-without options} ignores exit code with --no-trust-exit-code" '
test_config difftool.error.cmd false &&
test_config difftool.trustExitCode true &&
git difftool ${opt} -y --no-trust-exit-code -t error branch
- "
+ '
- test_expect_success "difftool ${opt} stops on error with --trust-exit-code" "
- test_when_finished 'rm -f for-diff .git/fail-right-file' &&
- test_when_finished 'git reset -- for-diff' &&
+ test_expect_success "difftool ${opt:-without options} stops on error with --trust-exit-code" '
+ test_when_finished "rm -f for-diff .git/fail-right-file" &&
+ test_when_finished "git reset -- for-diff" &&
write_script .git/fail-right-file <<-\EOF &&
echo failed
exit 1
test_must_fail git difftool ${opt} -y --trust-exit-code \
--extcmd .git/fail-right-file branch >actual &&
test_line_count = 1 actual
- "
+ '
- test_expect_success "difftool ${opt} honors exit status if command not found" "
+ test_expect_success "difftool ${opt:-without options} honors exit status if command not found" '
test_config difftool.nonexistent.cmd i-dont-exist &&
test_config difftool.trustExitCode false &&
- if test "${opt}" = '--dir-diff'
+ if test "${opt}" = --dir-diff
then
expected_code=127
else
expected_code=128
fi &&
- test_expect_code \${expected_code} git difftool ${opt} -y -t nonexistent branch
- "
+ test_expect_code ${expected_code} git difftool ${opt} -y -t nonexistent branch
+ '
done
test_expect_success 'difftool honors --gui' '
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
- test uryyb = "$(cat rot13 | remove_cr)"
+ test uryyb = "$(remove_cr <rot13)"
'
test_expect_success '--path=<path> complains without --textconv/--filters' '
git blame --line-porcelain file >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse X >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse X >expect &&
test_cmp expect actual
'
test_expect_success "ignore_rev_changing_lines ($I)" '
git blame --line-porcelain --ignore-rev $I file >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
git rev-parse Y >expect &&
git blame --line-porcelain file --ignore-rev Y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 3/s/ .*//p" blame_raw >actual &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 4/s/ .*//p" blame_raw >actual &&
test_cmp expect actual
'
git rev-parse Y >ignore_y &&
git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
git config --add blame.ignoreRevsFile ignore_x &&
git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
git rev-parse X >expect &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
test_cmp expect actual
'
test_expect_success bad_files_and_revs '
test_merge M B &&
git blame --line-porcelain file --ignore-rev M >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 9/s/ .*//p" blame_raw >actual &&
git rev-parse C >expect &&
test_cmp expect actual
'
# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
# Look at what SVN wound up naming the branch and use that.
# Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
test_when_finished "git update-ref -d refs/heads/L2" &&
git fast-import <input &&
git ls-tree L2 g/b/ >tmp &&
- cat tmp | cut -f 2 >actual &&
+ cut -f 2 <tmp >actual &&
test_cmp expect actual &&
git fsck $(git rev-parse L2)
'
'
test_expect_success 'Q: verify first notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
100644 blob $commit2
100644 blob $commit3
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
'
test_expect_success 'Q: verify second notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
100644 blob $commit2
100644 blob $commit3
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
'
test_expect_success 'Q: verify third notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
'
test_expect_success 'Q: verify fourth notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit2
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
test_expect_success 'set up faked signed tag' '
- cat signed-tag-import | git fast-import
+ git fast-import <signed-tag-import
'
test_expect_success 'set-up a few more tags for tag export tests' '
git checkout -f main &&
- HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
+ HEAD_TREE=$(git show -s --pretty=raw HEAD | sed -n "/tree/s/tree //p") &&
git tag tree_tag -m "tagging a tree" $HEAD_TREE &&
git tag -a tree_tag-obj -m "tagging a tree" $HEAD_TREE &&
git tag tag-obj_tag -m "tagging a tag" tree_tag-obj &&
EOF
test_expect_success 'pserver authentication' '
- cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'pserver authentication failure (non-anonymous user)' '
- if cat request-git | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <request-git >log 2>&1
then
false
else
'
test_expect_success 'pserver authentication success (non-anonymous user with password)' '
- cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <login-git-ok >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'pserver authentication (login)' '
- cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <login-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
- if cat login-git | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <login-git >log 2>&1
then
false
else
EOF
test_expect_success 'req_Root failure (relative pathname)' '
- if cat request-relative | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <request-relative >log 2>&1
then
echo unexpected success
false
'
test_expect_success 'req_Root failure (conflicting roots)' '
- cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <request-conflict >log 2>&1 &&
tail log | grep "^error 1 Conflicting roots specified$"
'
test_expect_success 'req_Root (strict paths)' '
- cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
+ git-cvsserver --strict-paths pserver "$SERVERDIR" <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (strict-paths)' '
- ! cat request-anonymous |
- git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+ ! git-cvsserver --strict-paths pserver "$WORKDIR" <request-anonymous >log 2>&1
'
test_expect_success 'req_Root (w/o strict-paths)' '
- cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
+ git-cvsserver pserver "$WORKDIR/" <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (w/o strict-paths)' '
- ! cat request-anonymous |
- git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+ ! git-cvsserver pserver "$WORKDIR/gitcvs" <request-anonymous >log 2>&1
'
cat >request-base <<EOF
EOF
test_expect_success 'req_Root (base-path)' '
- cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+ git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (base-path)' '
- ! cat request-anonymous |
- git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+ ! git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" <request-anonymous >log 2>&1
'
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
test_expect_success 'req_Root (export-all)' '
- cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
+ git-cvsserver --export-all pserver "$WORKDIR" <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (export-all w/o directory list)' '
- ! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+ ! (git-cvsserver --export-all pserver <request-anonymous >log 2>&1 || false)'
test_expect_success 'req_Root (everything together)' '
- cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+ git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
cp k-text-k k-text-ko &&
p4 add -t text+ko k-text-ko &&
- cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+ iconv -f ascii -t utf-16 <k-text-k >k-utf16-k &&
p4 add -t utf16+k k-utf16-k &&
cp k-utf16-k k-utf16-ko &&
marshal_dump job0 <change &&
marshal_dump job1 <change
) | sort >jobs &&
- cat jobname1 jobname2 | sort >expected &&
+ sort jobname1 jobname2 >expected &&
test_cmp expected jobs
)
'
sed -n '2,2 p' "$FILE" | grep "^oid " &&
sed -n '3,3 p' "$FILE" | grep "^size " &&
test_line_count = 3 "$FILE" &&
- cat "$FILE" | grep "size $SIZE" &&
- HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+ grep "size $SIZE" "$FILE" &&
+ HASH=$(sed -ne "/oid sha256:/s/oid sha256://gp" "$FILE") &&
LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
echo $EXPECTED_CONTENT >expect &&
test_path_is_file "$FILE" &&
# Detect the hash algorithm in use.
test_detect_hash () {
- test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
+ case "$GIT_TEST_DEFAULT_HASH" in
+ "sha256")
+ test_hash_algo=sha256
+ test_compat_hash_algo=sha1
+ ;;
+ *)
+ test_hash_algo=sha1
+ test_compat_hash_algo=sha256
+ ;;
+ esac
}
# Detect the hash algorithm in use.
local algo="${test_hash_algo}" &&
case "$1" in
+ --hash=storage)
+ algo="$test_hash_algo" &&
+ shift;;
+ --hash=compat)
+ algo="$test_compat_hash_algo" &&
+ shift;;
--hash=*)
algo="${1#--hash=}" &&
shift;;
return v ? *v : MISSING;
}
-static void test_prio_queue(int *input, int *result, size_t input_size)
+static void test_prio_queue(int *input, size_t input_size,
+ int *result, size_t result_size)
{
struct prio_queue pq = { intcmp };
+ int j = 0;
- for (int i = 0, j = 0; i < input_size; i++) {
+ for (int i = 0; i < input_size; i++) {
void *peek, *get;
switch(input[i]) {
case GET:
get = prio_queue_get(&pq);
if (!check(peek == get))
return;
- if(!check_int(result[j++], ==, show(get)))
- test_msg("failed at result[] index %d", j-1);
+ if (!check_uint(j, <, result_size))
+ break;
+ if (!check_int(result[j], ==, show(get)))
+ test_msg(" j: %d", j);
+ j++;
break;
case DUMP:
while ((peek = prio_queue_peek(&pq))) {
get = prio_queue_get(&pq);
if (!check(peek == get))
return;
- if(!check_int(result[j++], ==, show(get)))
- test_msg("failed at result[] index %d", j-1);
+ if (!check_uint(j, <, result_size))
+ break;
+ if (!check_int(result[j], ==, show(get)))
+ test_msg(" j: %d", j);
+ j++;
}
break;
case STACK:
break;
}
}
+ check_uint(j, ==, result_size);
clear_prio_queue(&pq);
}
{ \
int input[] = {INPUT}; \
int result[] = {RESULT}; \
- test_prio_queue(input, result, ARRAY_SIZE(input)); \
+ test_prio_queue(input, ARRAY_SIZE(input), \
+ result, ARRAY_SIZE(result)); \
}
TEST_INPUT(BASIC_INPUT, BASIC_RESULT, basic)
data->get_refs_list_called = 1;
helper = get_helper(transport);
- if (data->object_format) {
- write_str_in_full(helper->in, "option object-format\n");
- if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
- exit(128);
- }
+ if (data->object_format)
+ set_helper_option(transport, "object-format", "true");
if (data->push && for_push)
- write_str_in_full(helper->in, "list for-push\n");
+ write_constant(helper->in, "list for-push\n");
else
- write_str_in_full(helper->in, "list\n");
+ write_constant(helper->in, "list\n");
while (1) {
char *eov, *eon;
#include "json-writer.h"
#include "environment.h"
-static const char *get_mode(const char *str, unsigned int *modep)
-{
- unsigned char c;
- unsigned int mode = 0;
-
- if (*str == ' ')
- return NULL;
-
- while ((c = *str++) != ' ') {
- if (c < '0' || c > '7')
- return NULL;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- return str;
-}
-
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
const char *path;
- unsigned int mode, len;
- const unsigned hashsz = the_hash_algo->rawsz;
+ unsigned int len;
+ uint16_t mode;
+ const unsigned hashsz = desc->algo->rawsz;
if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
strbuf_addstr(err, _("too-short tree object"));
return -1;
}
- path = get_mode(buf, &mode);
+ path = parse_mode(buf, &mode);
if (!path) {
strbuf_addstr(err, _("malformed mode in tree entry"));
return -1;
desc->entry.path = path;
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
- oidread(&desc->entry.oid, (const unsigned char *)path + len);
+ oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
+ desc->algo);
return 0;
}
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
- unsigned long size, struct strbuf *err,
+static int init_tree_desc_internal(struct tree_desc *desc,
+ const struct object_id *oid,
+ const void *buffer, unsigned long size,
+ struct strbuf *err,
enum tree_desc_flags flags)
{
+ desc->algo = (oid && oid->algo) ? &hash_algos[oid->algo] : the_hash_algo;
desc->buffer = buffer;
desc->size = size;
desc->flags = flags;
return 0;
}
-void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+ const void *buffer, unsigned long size)
{
struct strbuf err = STRBUF_INIT;
- if (init_tree_desc_internal(desc, buffer, size, &err, 0))
+ if (init_tree_desc_internal(desc, tree_oid, buffer, size, &err, 0))
die("%s", err.buf);
strbuf_release(&err);
}
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+ const void *buffer, unsigned long size,
enum tree_desc_flags flags)
{
struct strbuf err = STRBUF_INIT;
- int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
+ int result = init_tree_desc_internal(desc, oid, buffer, size, &err, flags);
if (result)
error("%s", err.buf);
strbuf_release(&err);
if (!buf)
die(_("unable to read tree (%s)"), oid_to_hex(oid));
}
- init_tree_desc(desc, buf, size);
+ init_tree_desc(desc, oid, buf, size);
return buf;
}
static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
{
const void *buf = desc->buffer;
- const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
+ const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + desc->algo->rawsz;
unsigned long size = desc->size;
unsigned long len = end - (const unsigned char *)buf;
retval = -1;
} else {
struct tree_desc t;
- init_tree_desc(&t, tree, size);
+ init_tree_desc(&t, tree_oid, tree, size);
retval = find_tree_entry(r, &t, name, oid, mode);
}
free(tree);
struct tree_desc t;
int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
- init_tree_desc(&t, NULL, 0UL);
+ init_tree_desc(&t, NULL, NULL, 0UL);
strbuf_addstr(&namebuf, name);
oidcpy(¤t_tree_oid, tree_oid);
goto done;
/* descend */
- init_tree_desc(&t, tree, size);
+ init_tree_desc(&t, ¤t_tree_oid, tree, size);
}
/* Handle symlinks to e.g. a//b by removing leading slashes */
free(parent->tree);
parents_nr--;
parent = &parents[parents_nr - 1];
- init_tree_desc(&t, parent->tree, parent->size);
+ init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
continue;
}
contents_start = contents;
parent = &parents[parents_nr - 1];
- init_tree_desc(&t, parent->tree, parent->size);
+ init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
strbuf_splice(&namebuf, 0, len,
contents_start, link_len);
if (remainder)
* A semi-opaque data structure used to maintain the current state of the walk.
*/
struct tree_desc {
+ const struct git_hash_algo *algo;
/*
* pointer into the memory representation of the tree. It always
* points at the current entry being visited.
* size parameters are assumed to be the same as the buffer and size
* members of `struct tree`.
*/
-void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+ const void *buf, unsigned long size);
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+ const void *buf, unsigned long size,
enum tree_desc_flags flags);
/*
if (parse_tree(tree))
return -1;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (retval != all_entries_interesting) {
if (parse_tree(tree))
return -1;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object *obj = NULL;
};
enum untracked_status_type {
- SHOW_NO_UNTRACKED_FILES,
+ SHOW_UNTRACKED_FILES_ERROR = -1,
+ SHOW_NO_UNTRACKED_FILES = 0,
SHOW_NORMAL_UNTRACKED_FILES,
SHOW_ALL_UNTRACKED_FILES
};
return xdl_recmatch(l1, s1, l2, s2, flags);
}
+int parse_conflict_style_name(const char *value)
+{
+ if (!strcmp(value, "diff3"))
+ return XDL_MERGE_DIFF3;
+ else if (!strcmp(value, "zdiff3"))
+ return XDL_MERGE_ZEALOUS_DIFF3;
+ else if (!strcmp(value, "merge"))
+ return 0;
+ /*
+ * Please update _git_checkout() in git-completion.bash when
+ * you add new merge config
+ */
+ else
+ return -1;
+}
+
int git_xmerge_style = -1;
int git_xmerge_config(const char *var, const char *value,
if (!strcmp(var, "merge.conflictstyle")) {
if (!value)
return config_error_nonbool(var);
- if (!strcmp(value, "diff3"))
- git_xmerge_style = XDL_MERGE_DIFF3;
- else if (!strcmp(value, "zdiff3"))
- git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
- else if (!strcmp(value, "merge"))
- git_xmerge_style = 0;
- /*
- * Please update _git_checkout() in
- * git-completion.bash when you add new merge config
- */
- else
+ git_xmerge_style = parse_conflict_style_name(value);
+ if (git_xmerge_style == -1)
return error(_("unknown style '%s' given for '%s'"),
value, var);
return 0;
void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
void xdiff_clear_find_func(xdemitconf_t *xecfg);
struct config_context;
+int parse_conflict_style_name(const char *value);
int git_xmerge_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
extern int git_xmerge_style;