Code refactoring in the reflog part of refs API.
* ab/reflog-prep:
reflog + refs-backend: move "verbose" out of the backend
refs files-backend: assume cb->newlog if !EXPIRE_REFLOGS_DRY_RUN
reflog: reduce scope of "struct rev_info"
reflog expire: don't use lookup_commit_reference_gently()
reflog expire: refactor & use "tip_commit" only for UE_NORMAL
reflog expire: use "switch" over enum values
reflog: change one->many worktree->refnames to use a string_list
reflog expire: narrow scope of "cb" in cmd_reflog_expire()
reflog delete: narrow scope of "cmd" passed to count_reflog_ent()
* "git name-rev" has been tweaked to give output that is shorter and
easier to understand.
+ * "git apply" has been taught to ignore a message without a patch
+ with the "--allow-empty" option. It also learned to honor the
+ "--quiet" option given from the command line.
+
+ * The "init" and "set" subcommands in "git sparse-checkout" have been
+ unified for a better user experience and performance.
+
+ * Many git commands that deal with working tree files try to remove a
+ directory that becomes empty (i.e. "git switch" from a branch that
+ has the directory to another branch that does not would attempt
+ remove all files in the directory and the directory itself). This
+ drops users into an unfamiliar situation if the command was run in
+ a subdirectory that becomes subject to removal due to the command.
+ The commands have been taught to keep an empty directory if it is
+ the directory they were started in to avoid surprising users.
+
+ * "git am" learns "--empty=(stop|drop|keep)" option to tweak what is
+ done to a piece of e-mail without a patch in it.
+
+ * The default merge message prepared by "git merge" records the name
+ of the current branch; the name can be overridden with a new option
+ to allow users to pretend a merge is made on a different branch.
Performance, Internal Implementation, Development Support etc.
* diff and blame commands have been taught to work better with sparse
index.
+ * The chainlint test script linter in the test suite has been updated.
+
+ * The DEVELOPER=yes build uses -std=gnu99 now.
+
+ * "git format-patch" uses a single rev_info instance and then exits.
+ Mark the structure with UNLEAK() macro to squelch leak sanitizer.
+
+ * New interface into the tmp-objdir API to help in-core use of the
+ quarantine feature.
+
+ * Broken &&-chains in the test scripts have been corrected.
+
+ * The RCS keyword substitution in "git p4" used to be done assuming
+ that the contents are UTF-8 text, which can trigger decoding
+ errors. We now treat the contents as a bytestring for robustness
+ and correctness.
+
+ * The conditions to choose different definitions of the FLEX_ARRAY
+ macro for vendor compilers has been simplified to make it easier to
+ maintain.
+
+ * Correctness and performance update to "diff --color-moved" feature.
+
+ * "git upload-pack" (the other side of "git fetch") used a 8kB buffer
+ but most of its payload came on 64kB "packets". The buffer size
+ has been enlarged so that such a packet fits.
+
Fixes since v2.34
-----------------
to read and honor the settings given by the "--decorate-refs"
option.
+ * "git fetch --set-upstream" did not check if there is a current
+ branch, leading to a segfault when it is run on a detached HEAD,
+ which has been corrected.
+ (merge 17baeaf82d ab/fetch-set-upstream-while-detached later to maint).
+
+ * Among some code paths that ask an yes/no question, only one place
+ gave a prompt that looked different from the others, which has been
+ updated to match what the others create.
+ (merge 0fc8ed154c km/help-prompt-fix later to maint).
+
+ * "git log --invert-grep --author=<name>" used to exclude commits
+ written by the given author, but now "--invert-grep" only affects
+ the matches made by the "--grep=<pattern>" option.
+ (merge 794c000267 rs/log-invert-grep-with-headers later to maint).
+
+ * "git grep --perl-regexp" failed to match UTF-8 characters with
+ wildcard when the pattern consists only of ASCII letters, which has
+ been corrected.
+ (merge 32e3e8bc55 rs/pcre2-utf later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge 74db416c9c cw/protocol-v2-doc-fix later to maint).
(merge f9b2b6684d ja/doc-cleanup later to maint).
(merge 2c68f577fc ew/cbtree-remove-unused-and-broken-cb-unlink later to maint).
(merge eafd6e7e55 ab/die-with-bug later to maint).
(merge 91028f7659 jc/grep-patterntype-default-doc later to maint).
+ (merge 47ca93d071 ds/repack-fixlets later to maint).
+ (merge e6a9bc0c60 rs/t4202-invert-grep-test-fix later to maint).
+ (merge deb5407a42 gh/gpg-doc-markup-fix later to maint).
+ (merge 999bba3e0b rs/daemon-plug-leak later to maint).
* `fully`
* `ultimate`
-gpg.ssh.defaultKeyCommand:
+gpg.ssh.defaultKeyCommand::
This command that will be run when user.signingkey is not set and a ssh
signature is requested. On successful exit a valid ssh public key is
expected in the first line of its output. To automatically use the first
A file containing ssh public keys which you are willing to trust.
The file consists of one or more lines of principals followed by an ssh
public key.
- e.g.: user1@example.com,user2@example.com ssh-rsa AAAAX1...
+ e.g.: `user1@example.com,user2@example.com ssh-rsa AAAAX1...`
See ssh-keygen(1) "ALLOWED SIGNERS" for details.
The principal is only used to identify the key and is available when
verifying a signature.
[--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
[--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
[--quoted-cr=<action>]
+ [--empty=(stop|drop|keep)]
[(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
DESCRIPTION
-----------
--quoted-cr=<action>::
This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+--empty=(stop|drop|keep)::
+ By default, or when the option is set to 'stop', the command
+ errors out on an input e-mail message lacking a patch
+ and stops into the middle of the current am session. When this
+ option is set to 'drop', skip such an e-mail message instead.
+ When this option is set to 'keep', create an empty commit,
+ recording the contents of the e-mail message as its log.
+
-m::
--message-id::
Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
the e-mail message; if `diff`, show the diff portion only.
Defaults to `raw`.
+--allow-empty::
+ After a patch failure on an input e-mail message lacking a patch,
+ create an empty commit with the contents of the e-mail message
+ as its log message.
+
DISCUSSION
----------
[--ignore-space-change | --ignore-whitespace]
[--whitespace=(nowarn|warn|fix|error|error-all)]
[--exclude=<path>] [--include=<path>] [--directory=<root>]
- [--verbose] [--unsafe-paths] [<patch>...]
+ [--verbose | --quiet] [--unsafe-paths] [--allow-empty] [<patch>...]
DESCRIPTION
-----------
current patch being applied will be printed. This option will cause
additional information to be reported.
+-q::
+--quiet::
+ Suppress stderr output. Messages about patch status and progress
+ will not be printed.
+
--recount::
Do not trust the line counts in the hunk headers, but infer them
by inspecting the patch (e.g. after editing the patch without
the `--unsafe-paths` option to override this safety check. This option
has no effect when `--index` or `--cached` is in use.
+--allow-empty::
+ Don't return error for patches containing no diff. This includes
+ empty patches and patches with commit text only.
+
CONFIGURATION
-------------
configuration variables are created.
--sparse::
- Initialize the sparse-checkout file so the working
- directory starts with only the files in the root
- of the repository. The sparse-checkout file can be
- modified to grow the working directory as needed.
+ Employ a sparse-checkout, with only files in the toplevel
+ directory initially being present. The
+ linkgit:git-sparse-checkout[1] command can be used to grow the
+ working directory as needed.
--filter=<filter-spec>::
Use the partial clone feature and request that the server sends
SYNOPSIS
--------
[verse]
-'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log]
+'git fmt-merge-msg' [-m <message>] [--into-name <branch>] [--log[=<n>] | --no-log]
'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
DESCRIPTION
Use <message> instead of the branch names for the first line
of the log message. For use with `--log`.
+--into-name <branch>::
+ Prepare the merge message as if merging to the branch `<branch>`,
+ instead of the name of the real branch to which the merge is made.
+
-F <file>::
--file <file>::
Take the list of merged objects from <file> instead of
[-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files]
[--in-reply-to=<message id>] [--suffix=.<sfx>]
- [--ignore-if-in-upstream]
+ [--ignore-if-in-upstream] [--always]
[--cover-from-description=<mode>]
[--rfc] [--subject-prefix=<subject prefix>]
[(--reroll-count|-v) <n>]
patches being generated, and any patch that matches is
ignored.
+--always::
+ Include patches for commits that do not introduce any change,
+ which are omitted by default.
+
--cover-from-description=<mode>::
Controls which parts of the cover letter will be automatically
populated using the branch's description.
and in the working tree ("w/<eolinfo>") are shown for regular files,
followed by the ("attr/<eolattr>").
+--sparse::
+ If the index is sparse, show the sparse directories without expanding
+ to the contained files. Sparse directories will be shown with a
+ trailing slash, such as "x/" for a sparse directory "x".
+
\--::
Do not interpret any more arguments as options.
'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
- [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
+ [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>]
+ [--into-name <branch>] [<commit>...]
'git merge' (--continue | --abort | --quit)
DESCRIPTION
used to give a good default for automated 'git merge'
invocations. The automated message can include the branch description.
+--into-name <branch>::
+ Prepare the default merge message as if merging to the branch
+ `<branch>`, instead of the name of the real branch to which
+ the merge is made.
+
-F <file>::
--file=<file>::
Read the commit message to be used for the merge commit (in
linkgit:git-pack-objects[1].
-q::
- Pass the `-q` option to 'git pack-objects'. See
- linkgit:git-pack-objects[1].
+--quiet::
+ Show no progress over the standard error stream and pass the `-q`
+ option to 'git pack-objects'. See linkgit:git-pack-objects[1].
-n::
Do not update the server information with
'list'::
Describe the patterns in the sparse-checkout file.
-'init'::
- Enable the `core.sparseCheckout` setting. If the
- sparse-checkout file does not exist, then populate it with
- patterns that match every file in the root directory and
- no other directories, then will remove all directories tracked
- by Git. Add patterns to the sparse-checkout file to
- repopulate the working directory.
+'set'::
+ Enable the necessary config settings
+ (extensions.worktreeConfig, core.sparseCheckout,
+ core.sparseCheckoutCone) if they are not already enabled, and
+ write a set of patterns to the sparse-checkout file from the
+ list of arguments following the 'set' subcommand. Update the
+ working directory to match the new patterns.
+
-To avoid interfering with other worktrees, it first enables the
-`extensions.worktreeConfig` setting and makes sure to set the
-`core.sparseCheckout` setting in the worktree-specific config file.
+When the `--stdin` option is provided, the patterns are read from
+standard in as a newline-delimited list instead of from the arguments.
+
-When `--cone` is provided, the `core.sparseCheckoutCone` setting is
-also set, allowing for better performance with a limited set of
-patterns (see 'CONE PATTERN SET' below).
+When `--cone` is passed or `core.sparseCheckoutCone` is enabled, the
+input list is considered a list of directories instead of
+sparse-checkout patterns. This allows for better performance with a
+limited set of patterns (see 'CONE PATTERN SET' below). Note that the
+set command will write patterns to the sparse-checkout file to include
+all files contained in those directories (recursively) as well as
+files that are siblings of ancestor directories. The input format
+matches the output of `git ls-tree --name-only`. This includes
+interpreting pathnames that begin with a double quote (") as C-style
+quoted strings. This may become the default in the future; --no-cone
+can be passed to request non-cone mode.
+
-Use the `--[no-]sparse-index` option to toggle the use of the sparse
-index format. This reduces the size of the index to be more closely
-aligned with your sparse-checkout definition. This can have significant
-performance advantages for commands such as `git status` or `git add`.
-This feature is still experimental. Some commands might be slower with
-a sparse index until they are properly integrated with the feature.
+Use the `--[no-]sparse-index` option to use a sparse index (the
+default is to not use it). A sparse index reduces the size of the
+index to be more closely aligned with your sparse-checkout
+definition. This can have significant performance advantages for
+commands such as `git status` or `git add`. This feature is still
+experimental. Some commands might be slower with a sparse index until
+they are properly integrated with the feature.
+
**WARNING:** Using a sparse index requires modifying the index in a way
that is not completely understood by external tools. If you have trouble
understand the sparse directory entries index extension and may fail to
interact with your repository until it is disabled.
-'set'::
- Write a set of patterns to the sparse-checkout file, as given as
- a list of arguments following the 'set' subcommand. Update the
- working directory to match the new patterns. Enable the
- core.sparseCheckout config setting if it is not already enabled.
-+
-When the `--stdin` option is provided, the patterns are read from
-standard in as a newline-delimited list instead of from the arguments.
-+
-When `core.sparseCheckoutCone` is enabled, the input list is considered a
-list of directories instead of sparse-checkout patterns. The command writes
-patterns to the sparse-checkout file to include all files contained in those
-directories (recursively) as well as files that are siblings of ancestor
-directories. The input format matches the output of `git ls-tree --name-only`.
-This includes interpreting pathnames that begin with a double quote (") as
-C-style quoted strings.
-
'add'::
Update the sparse-checkout file to include additional patterns.
By default, these patterns are read from the command-line arguments,
cases, it can make sense to run `git sparse-checkout reapply` later
after cleaning up affected paths (e.g. resolving conflicts, undoing
or committing changes, etc.).
++
+The `reapply` command can also take `--[no-]cone` and `--[no-]sparse-index`
+flags, with the same meaning as the flags from the `set` command, in order
+to change which sparsity mode you are using without needing to also respecify
+all sparsity paths.
'disable'::
Disable the `core.sparseCheckout` config setting, and restore the
- working directory to include all files. Leaves the sparse-checkout
- file intact so a later 'git sparse-checkout init' command may
- return the working directory to the same state.
+ working directory to include all files.
+
+'init'::
+ Deprecated command that behaves like `set` with no specified paths.
+ May be removed in the future.
++
+Historically, `set` did not handle all the necessary config settings,
+which meant that both `init` and `set` had to be called. Invoking
+both meant the `init` step would first remove nearly all tracked files
+(and in cone mode, ignored files too), then the `set` step would add
+many of the tracked files (but not ignored files) back. In addition
+to the lost files, the performance and UI of this combination was
+poor.
++
+Also, historically, `init` would not actually initialize the
+sparse-checkout file if it already existed. This meant it was
+possible to return to a sparse-checkout without remembering which
+paths to pass to a subsequent 'set' or 'add' command. However,
+`--cone` and `--sparse-index` options would not be remembered across
+the disable command, so the easy restore of calling a plain `init`
+decreased in utility.
SPARSE CHECKOUT
---------------
It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
Git whether a file in the working directory is worth looking at. If
the skip-worktree bit is set, then the file is ignored in the working
-directory. Git will not populate the contents of those files, which
+directory. Git will avoid populating the contents of those files, which
makes a sparse checkout helpful when working in a repository with many
files, but only a few are important to the current user.
on this file. The files matching the patterns in the file will
appear in the working directory, and the rest will not.
-To enable the sparse-checkout feature, run `git sparse-checkout init` to
-initialize a simple sparse-checkout file and enable the `core.sparseCheckout`
-config setting. Then, run `git sparse-checkout set` to modify the patterns in
-the sparse-checkout file.
+To enable the sparse-checkout feature, run `git sparse-checkout set` to
+set the patterns you want to use.
To repopulate the working directory with all files, use the
`git sparse-checkout disable` command.
endif
ifndef NO_MSGFMT_EXTENDED_OPTIONS
- MSGFMT += --check --statistics
+ MSGFMT += --check
endif
ifdef HAVE_CLOCK_GETTIME
BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
endif
-PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
-PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
-PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
-BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
-
ALL_CFLAGS += $(BASIC_CFLAGS)
ALL_LDFLAGS += $(BASIC_LDFLAGS)
$(filter %.o,$^) $(LIBS)
help.sp help.s help.o: command-list.h
-hook.sp hook.s hook.o: hook-list.h
+builtin/bugreport.sp builtin/bugreport.s builtin/bugreport.o: hook-list.h
-builtin/help.sp builtin/help.s builtin/help.o: config-list.h hook-list.h GIT-PREFIX
+builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
'-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
'-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
+PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
+PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
+PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
+pager.sp pager.s pager.o: EXTRA_CPPFLAGS = \
+ -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
+
version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
version.sp version.s version.o: EXTRA_CPPFLAGS = \
'-DGIT_VERSION="$(GIT_VERSION)"' \
}
if (!list && !skipped_patch) {
- error(_("unrecognized input"));
- res = -128;
+ if (!state->allow_empty) {
+ error(_("No valid patches in input (allow with \"--allow-empty\")"));
+ res = -128;
+ }
goto end;
}
N_("leave the rejected hunks in corresponding *.rej files")),
OPT_BOOL(0, "allow-overlap", &state->allow_overlap,
N_("allow overlapping hunks")),
- OPT__VERBOSE(&state->apply_verbosity, N_("be verbose")),
+ OPT__VERBOSITY(&state->apply_verbosity),
OPT_BIT(0, "inaccurate-eof", options,
N_("tolerate incorrectly detected missing new-line at the end of file"),
APPLY_OPT_INACCURATE_EOF),
OPT_CALLBACK(0, "directory", state, N_("root"),
N_("prepend <root> to all filenames"),
apply_option_parse_directory),
+ OPT_BOOL(0, "allow-empty", &state->allow_empty,
+ N_("don't return error for empty patches")),
OPT_END()
};
int threeway;
int unidiff_zero;
int unsafe_paths;
+ int allow_empty;
/* Other non boolean parameters */
struct repository *repo;
SHOW_PATCH_DIFF = 1,
};
+enum empty_action {
+ STOP_ON_EMPTY_COMMIT = 0, /* output errors and stop in the middle of an am session */
+ DROP_EMPTY_COMMIT, /* skip with a notice message, unless "--quiet" has been passed */
+ KEEP_EMPTY_COMMIT, /* keep recording as empty commits */
+};
+
struct am_state {
/* state directory path */
char *dir;
int message_id;
int scissors; /* enum scissors_type */
int quoted_cr; /* enum quoted_cr_action */
+ int empty_type; /* enum empty_action */
struct strvec git_apply_opts;
const char *resolvemsg;
int committer_date_is_author_date;
return 0;
}
+static int am_option_parse_empty(const struct option *opt,
+ const char *arg, int unset)
+{
+ int *opt_value = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+
+ if (!strcmp(arg, "stop"))
+ *opt_value = STOP_ON_EMPTY_COMMIT;
+ else if (!strcmp(arg, "drop"))
+ *opt_value = DROP_EMPTY_COMMIT;
+ else if (!strcmp(arg, "keep"))
+ *opt_value = KEEP_EMPTY_COMMIT;
+ else
+ return error(_("Invalid value for --empty: %s"), arg);
+
+ return 0;
+}
+
/**
* Returns path relative to the am_state directory.
*/
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);
+
+ 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);
+
printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
}
goto finish;
}
- if (is_empty_or_missing_file(am_path(state, "patch"))) {
- printf_ln(_("Patch is empty."));
- die_user_resolve(state);
- }
-
strbuf_addstr(&msg, "\n\n");
strbuf_addbuf(&msg, &mi.log_message);
strbuf_stripspace(&msg, 0);
while (state->cur <= state->last) {
const char *mail = am_path(state, msgnum(state));
int apply_status;
+ int to_keep;
reset_ident_date();
if (state->interactive && do_interactive(state))
goto next;
+ to_keep = 0;
+ if (is_empty_or_missing_file(am_path(state, "patch"))) {
+ switch (state->empty_type) {
+ case DROP_EMPTY_COMMIT:
+ say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+ goto next;
+ break;
+ case KEEP_EMPTY_COMMIT:
+ to_keep = 1;
+ say(state, stdout, _("Creating an empty commit: %.*s"),
+ linelen(state->msg), state->msg);
+ break;
+ case STOP_ON_EMPTY_COMMIT:
+ printf_ln(_("Patch is empty."));
+ die_user_resolve(state);
+ break;
+ }
+ }
+
if (run_applypatch_msg_hook(state))
exit(1);
+ if (to_keep)
+ goto commit;
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
die_user_resolve(state);
}
+commit:
do_commit(state);
next:
/**
* Resume the current am session after patch application failure. The user did
* all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
*/
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
{
validate_resume_state(state);
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
if (!repo_index_has_changes(the_repository, NULL, NULL)) {
- printf_ln(_("No changes - did you forget to use 'git add'?\n"
- "If there is nothing left to stage, chances are that something else\n"
- "already introduced the same changes; you might want to skip this patch."));
- die_user_resolve(state);
+ if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
+ printf_ln(_("No changes - recorded it as an empty commit."));
+ } else {
+ printf_ln(_("No changes - did you forget to use 'git add'?\n"
+ "If there is nothing left to stage, chances are that something else\n"
+ "already introduced the same changes; you might want to skip this patch."));
+ die_user_resolve(state);
+ }
}
if (unmerged_cache()) {
RESUME_SKIP,
RESUME_ABORT,
RESUME_QUIT,
- RESUME_SHOW_PATCH
+ RESUME_SHOW_PATCH,
+ RESUME_ALLOW_EMPTY,
};
struct resume_mode {
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+ OPT_CMDMODE(0, "allow-empty", &resume.mode,
+ N_("record the empty patch as an empty commit"),
+ RESUME_ALLOW_EMPTY),
OPT_BOOL(0, "committer-date-is-author-date",
&state.committer_date_is_author_date,
N_("lie about committer date")),
{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+ N_("how to handle empty patches"),
+ PARSE_OPT_NONEG, am_option_parse_empty),
OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
N_("(internal use for git-rebase)")),
OPT_END()
am_run(&state, 1);
break;
case RESUME_RESOLVED:
- am_resolve(&state);
+ case RESUME_ALLOW_EMPTY:
+ am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
break;
case RESUME_SKIP:
am_skip(&state);
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
+static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
+static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n");
enum color_clean {
CLEAN_COLOR_RESET = 0,
{
DIR *dir;
struct strbuf quoted = STRBUF_INIT;
+ struct strbuf realpath = STRBUF_INIT;
+ struct strbuf real_ocwd = STRBUF_INIT;
struct dirent *e;
int res = 0, ret = 0, gone = 1, original_len = path->len, len;
struct string_list dels = STRING_LIST_INIT_DUP;
strbuf_setlen(path, original_len);
if (*dir_gone) {
- res = dry_run ? 0 : rmdir(path->buf);
- if (!res)
- *dir_gone = 1;
- else {
- int saved_errno = errno;
- quote_path(path->buf, prefix, "ed, 0);
- errno = saved_errno;
- warning_errno(_(msg_warn_remove_failed), quoted.buf);
+ /*
+ * Normalize path components in path->buf, e.g. change '\' to
+ * '/' on Windows.
+ */
+ strbuf_realpath(&realpath, path->buf, 1);
+
+ /*
+ * path and realpath are absolute; for comparison, we would
+ * like to transform startup_info->original_cwd to an absolute
+ * path too.
+ */
+ if (startup_info->original_cwd)
+ strbuf_realpath(&real_ocwd,
+ startup_info->original_cwd, 1);
+
+ if (!strbuf_cmp(&realpath, &real_ocwd)) {
+ printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd));
*dir_gone = 0;
- ret = 1;
+ } else {
+ res = dry_run ? 0 : rmdir(path->buf);
+ if (!res)
+ *dir_gone = 1;
+ else {
+ int saved_errno = errno;
+ quote_path(path->buf, prefix, "ed, 0);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
+ *dir_gone = 0;
+ ret = 1;
+ }
}
}
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
}
out:
+ strbuf_release(&realpath);
+ strbuf_release(&real_ocwd);
strbuf_release("ed);
string_list_clear(&dels, 0);
return ret;
{
struct strvec argv = STRVEC_INIT;
int result = 0;
- strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+ strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL);
/*
* We must apply the setting in the current process
static struct decoration idnums;
static uint32_t last_idnum;
-
-static int has_unshown_parent(struct commit *commit)
-{
- struct commit_list *parent;
-
- for (parent = commit->parents; parent; parent = parent->next)
- if (!(parent->item->object.flags & SHOWN) &&
- !(parent->item->object.flags & UNINTERESTING))
- return 1;
- return 0;
-}
-
struct anonymized_entry {
struct hashmap_entry hash;
const char *anon;
return strbuf_detach(&out, NULL);
}
-static void handle_tail(struct object_array *commits, struct rev_info *revs,
- struct string_list *paths_of_changed_objects)
-{
- struct commit *commit;
- while (commits->nr) {
- commit = (struct commit *)object_array_pop(commits);
- if (has_unshown_parent(commit)) {
- /* Queue again, to be handled later */
- add_object_array(&commit->object, NULL, commits);
- return;
- }
- handle_commit(commit, revs, paths_of_changed_objects);
- }
-}
static void handle_tag(const char *name, struct tag *tag)
{
int cmd_fast_export(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct object_array commits = OBJECT_ARRAY_INIT;
struct commit *commit;
char *export_filename = NULL,
*import_filename = NULL,
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
+
+ revs.reverse = 1;
revs.diffopt.format_callback = show_filemodify;
revs.diffopt.format_callback_data = &paths_of_changed_objects;
revs.diffopt.flags.recursive = 1;
- while ((commit = get_revision(&revs))) {
- if (has_unshown_parent(commit)) {
- add_object_array(&commit->object, NULL, &commits);
- }
- else {
- handle_commit(commit, &revs, &paths_of_changed_objects);
- handle_tail(&commits, &revs, &paths_of_changed_objects);
- }
- }
+ while ((commit = get_revision(&revs)))
+ handle_commit(commit, &revs, &paths_of_changed_objects);
handle_tags_and_duplicates(&extra_refs);
handle_tags_and_duplicates(&tag_refs);
}
}
if (source_ref) {
+ if (!branch) {
+ const char *shortname = source_ref->name;
+ skip_prefix(shortname, "refs/heads/", &shortname);
+
+ warning(_("could not set upstream of HEAD to '%s' from '%s' when "
+ "it does not point to any branch."),
+ shortname, transport->remote->name);
+ goto skip;
+ }
+
if (!strcmp(source_ref->name, "HEAD") ||
starts_with(source_ref->name, "refs/heads/"))
install_branch_config(0,
}
git_config(git_fetch_config, NULL);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
{
const char *inpath = NULL;
const char *message = NULL;
+ char *into_name = NULL;
int shortlog_len = -1;
struct option options[] = {
{ OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
DEFAULT_MERGE_LOG_LEN },
OPT_STRING('m', "message", &message, N_("text"),
N_("use <text> as start of message")),
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target branch")),
OPT_FILENAME('F', "file", &inpath, N_("file to read from")),
OPT_END()
};
opts.add_title = !message;
opts.credit_people = 1;
opts.shortlog_len = shortlog_len;
+ opts.into_name = into_name;
ret = fmt_merge_msg(&input, &output, &opts);
if (ret)
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
+ UNLEAK(rev);
return 0;
}
static int show_eol;
static int recurse_submodules;
static int skipping_duplicates;
+static int show_sparse_dirs;
static const char *prefix;
static int max_prefix_len;
if (!(show_cached || show_stage || show_deleted || show_modified))
return;
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(repo->index);
+
+ if (!show_sparse_dirs)
+ ensure_full_index(repo->index);
+
for (i = 0; i < repo->index->cache_nr; i++) {
const struct cache_entry *ce = repo->index->cache[i];
struct stat st;
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_BOOL(0, "deduplicate", &skipping_duplicates,
N_("suppress duplicate entries")),
+ OPT_BOOL(0, "sparse", &show_sparse_dirs,
+ N_("show sparse directories in the presence of a sparse index")),
OPT_END()
};
int ret = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(ls_files_usage, builtin_ls_files_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
static const char *sign_commit;
static int autostash;
static int no_verify;
+static char *into_name;
static struct strategy all_strategy[] = {
{ "recursive", NO_TRIVIAL },
{ OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
N_("read message from file"), PARSE_OPT_NONEG,
NULL, 0, option_read_message },
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target")),
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
opts.add_title = !have_message;
opts.shortlog_len = shortlog_len;
opts.credit_people = (0 < option_edit);
+ opts.into_name = into_name;
fmt_merge_msg(merge_names, merge_msg, &opts);
if (merge_msg->len)
return error("Could not stat '%s'", fullpath);
if (st.st_mtime > expire)
return 0;
- if (show_only || verbose)
- printf("Removing stale temporary file %s\n", fullpath);
- if (!show_only)
- unlink_or_warn(fullpath);
+ if (S_ISDIR(st.st_mode)) {
+ if (show_only || verbose)
+ printf("Removing stale temporary directory %s\n", fullpath);
+ if (!show_only) {
+ struct strbuf remove_dir_buf = STRBUF_INIT;
+
+ strbuf_addstr(&remove_dir_buf, fullpath);
+ remove_dir_recursively(&remove_dir_buf, 0);
+ strbuf_release(&remove_dir_buf);
+ }
+ } else {
+ if (show_only || verbose)
+ printf("Removing stale temporary file %s\n", fullpath);
+ if (!show_only)
+ unlink_or_warn(fullpath);
+ }
return 0;
}
set_reflog_message(argc, argv);
git_config(git_pull_config, NULL);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
strvec_push(&child.args, alt_shallow_file);
}
- tmp_objdir = tmp_objdir_create();
+ tmp_objdir = tmp_objdir_create("incoming");
if (!tmp_objdir) {
if (err_fd > 0)
close(err_fd);
struct tempfile *refs_snapshot = NULL;
int i, ext, ret;
FILE *out;
- int show_progress = isatty(2);
+ int show_progress;
/* variables to be filled by option parsing */
int pack_everything = 0;
write_bitmaps = 0;
}
if (pack_kept_objects < 0)
- pack_kept_objects = write_bitmaps > 0;
+ pack_kept_objects = write_bitmaps > 0 && !write_midx;
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
prepare_pack_objects(&cmd, &po_args);
+ show_progress = !po_args.quiet && isatty(2);
+
strvec_push(&cmd.args, "--keep-true-parents");
if (!pack_kept_objects)
strvec_push(&cmd.args, "--honor-pack-keep");
}
strbuf_release(&buf);
}
- if (!po_args.quiet && show_progress)
+ if (show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
if (!index_only) {
int removed = 0, gitmodules_modified = 0;
struct strbuf buf = STRBUF_INIT;
+ int flag = force ? REMOVE_DIR_PURGE_ORIGINAL_CWD : 0;
for (i = 0; i < list.nr; i++) {
const char *path = list.entry[i].name;
if (list.entry[i].is_submodule) {
strbuf_reset(&buf);
strbuf_addstr(&buf, path);
- if (remove_dir_recursively(&buf, 0))
+ if (remove_dir_recursively(&buf, flag))
die(_("could not remove '%s'"), path);
removed = 1;
char *sparse_filename;
int res;
+ if (!core_apply_sparse_checkout)
+ die(_("this worktree is not sparse"));
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_list_options,
builtin_sparse_checkout_list_usage, 0);
return 0;
}
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+ int mode, record_mode;
+
+ /* Determine if we need to record the mode; ensure sparse checkout on */
+ record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+ /* If not specified, use previous definition of cone mode */
+ if (*cone_mode == -1 && core_apply_sparse_checkout)
+ *cone_mode = core_sparse_checkout_cone;
+
+ /* Set cone/non-cone mode appropriately */
+ core_apply_sparse_checkout = 1;
+ if (*cone_mode == 1) {
+ mode = MODE_CONE_PATTERNS;
+ core_sparse_checkout_cone = 1;
+ } else {
+ mode = MODE_ALL_PATTERNS;
+ }
+ if (record_mode && set_config(mode))
+ return 1;
+
+ /* Set sparse-index/non-sparse-index mode if specified */
+ if (*sparse_index >= 0) {
+ if (set_sparse_index_config(the_repository, *sparse_index) < 0)
+ die(_("failed to modify sparse-index config"));
+
+ /* force an index rewrite */
+ repo_read_index(the_repository);
+ the_repository->index->updated_workdir = 1;
+ }
+
+ return 0;
+}
+
static char const * const builtin_sparse_checkout_init_usage[] = {
N_("git sparse-checkout init [--cone] [--[no-]sparse-index]"),
NULL
char *sparse_filename;
int res;
struct object_id oid;
- int mode;
struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
repo_read_index(the_repository);
+ init_opts.cone_mode = -1;
init_opts.sparse_index = -1;
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_init_options,
builtin_sparse_checkout_init_usage, 0);
- if (init_opts.cone_mode) {
- mode = MODE_CONE_PATTERNS;
- core_sparse_checkout_cone = 1;
- } else
- mode = MODE_ALL_PATTERNS;
-
- if (set_config(mode))
+ if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index))
return 1;
memset(&pl, 0, sizeof(pl));
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
- if (init_opts.sparse_index >= 0) {
- if (set_sparse_index_config(the_repository, init_opts.sparse_index) < 0)
- die(_("failed to modify sparse-index config"));
-
- /* force an index rewrite */
- repo_read_index(the_repository);
- the_repository->index->updated_workdir = 1;
- }
-
- core_apply_sparse_checkout = 1;
-
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
char *oldpattern = e->pattern;
size_t newlen;
- if (slash == e->pattern)
+ if (!slash || slash == e->pattern)
break;
newlen = slash - e->pattern;
insert_recursive_pattern(pl, line);
}
-static char const * const builtin_sparse_checkout_set_usage[] = {
- N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
- NULL
-};
-
-static struct sparse_checkout_set_opts {
- int use_stdin;
-} set_opts;
-
static void add_patterns_from_input(struct pattern_list *pl,
- int argc, const char **argv)
+ int argc, const char **argv,
+ int use_stdin)
{
int i;
if (core_sparse_checkout_cone) {
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
- if (set_opts.use_stdin) {
+ if (use_stdin) {
struct strbuf unquoted = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
if (line.buf[0] == '"') {
}
}
} else {
- if (set_opts.use_stdin) {
+ if (use_stdin) {
struct strbuf line = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
};
static void add_patterns_cone_mode(int argc, const char **argv,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int use_stdin)
{
struct strbuf buffer = STRBUF_INIT;
struct pattern_entry *pe;
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
+ if (!existing.use_cone_patterns)
+ die(_("existing sparse-checkout patterns do not use cone mode"));
+
hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern, &buffer) ||
}
static void add_patterns_literal(int argc, const char **argv,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int use_stdin)
{
char *sparse_filename = get_sparse_checkout_filename();
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
pl, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
}
-static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
+static int modify_pattern_list(int argc, const char **argv, int use_stdin,
+ enum modify_type m)
{
int result;
int changed_config = 0;
switch (m) {
case ADD:
if (core_sparse_checkout_cone)
- add_patterns_cone_mode(argc, argv, pl);
+ add_patterns_cone_mode(argc, argv, pl, use_stdin);
else
- add_patterns_literal(argc, argv, pl);
+ add_patterns_literal(argc, argv, pl, use_stdin);
break;
case REPLACE:
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
break;
}
return result;
}
-static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
- enum modify_type m)
+static char const * const builtin_sparse_checkout_add_usage[] = {
+ N_("git sparse-checkout add (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_add_opts {
+ int use_stdin;
+} add_opts;
+
+static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
{
- static struct option builtin_sparse_checkout_set_options[] = {
- OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+ static struct option builtin_sparse_checkout_add_options[] = {
+ OPT_BOOL(0, "stdin", &add_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
};
+ if (!core_apply_sparse_checkout)
+ die(_("no sparse-checkout to add to"));
+
repo_read_index(the_repository);
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_add_options,
+ builtin_sparse_checkout_add_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD);
+}
+
+static char const * const builtin_sparse_checkout_set_usage[] = {
+ N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_set_opts {
+ int cone_mode;
+ int sparse_index;
+ int use_stdin;
+} set_opts;
+
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+{
+ int default_patterns_nr = 2;
+ const char *default_patterns[] = {"/*", "!/*/", NULL};
+
+ static struct option builtin_sparse_checkout_set_options[] = {
+ OPT_BOOL(0, "cone", &set_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_BOOL(0, "sparse-index", &set_opts.sparse_index,
+ N_("toggle the use of a sparse index")),
+ OPT_BOOL_F(0, "stdin", &set_opts.use_stdin,
+ N_("read patterns from standard in"),
+ PARSE_OPT_NONEG),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+
+ set_opts.cone_mode = -1;
+ set_opts.sparse_index = -1;
+
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
PARSE_OPT_KEEP_UNKNOWN);
- return modify_pattern_list(argc, argv, m);
+ if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
+ return 1;
+
+ /*
+ * Cone mode automatically specifies the toplevel directory. For
+ * non-cone mode, if nothing is specified, manually select just the
+ * top-level directory (much as 'init' would do).
+ */
+ if (!core_sparse_checkout_cone && argc == 0) {
+ argv = default_patterns;
+ argc = default_patterns_nr;
+ }
+
+ return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE);
}
static char const * const builtin_sparse_checkout_reapply_usage[] = {
- N_("git sparse-checkout reapply"),
+ N_("git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]"),
NULL
};
+static struct sparse_checkout_reapply_opts {
+ int cone_mode;
+ int sparse_index;
+} reapply_opts;
+
static int sparse_checkout_reapply(int argc, const char **argv)
{
static struct option builtin_sparse_checkout_reapply_options[] = {
+ OPT_BOOL(0, "cone", &reapply_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_BOOL(0, "sparse-index", &reapply_opts.sparse_index,
+ N_("toggle the use of a sparse index")),
OPT_END(),
};
+ if (!core_apply_sparse_checkout)
+ die(_("must be in a sparse-checkout to reapply sparsity patterns"));
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_reapply_options,
builtin_sparse_checkout_reapply_usage, 0);
repo_read_index(the_repository);
+
+ reapply_opts.cone_mode = -1;
+ reapply_opts.sparse_index = -1;
+
+ if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index))
+ return 1;
+
return update_working_directory(NULL);
}
struct pattern_list pl;
struct strbuf match_all = STRBUF_INIT;
+ /*
+ * We do not exit early if !core_apply_sparse_checkout; due to the
+ * ability for users to manually muck things up between
+ * direct editing of .git/info/sparse-checkout
+ * running read-tree -m u HEAD or update-index --skip-worktree
+ * direct toggling of config options
+ * users might end up with an index with SKIP_WORKTREE bit set on
+ * some files and not know how to undo it. So, here we just
+ * forcibly return to a dense checkout regardless of initial state.
+ */
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_disable_options,
builtin_sparse_checkout_disable_usage, 0);
if (!strcmp(argv[0], "init"))
return sparse_checkout_init(argc, argv);
if (!strcmp(argv[0], "set"))
- return sparse_checkout_set(argc, argv, prefix, REPLACE);
+ return sparse_checkout_set(argc, argv, prefix);
if (!strcmp(argv[0], "add"))
- return sparse_checkout_set(argc, argv, prefix, ADD);
+ return sparse_checkout_add(argc, argv, prefix);
if (!strcmp(argv[0], "reapply"))
return sparse_checkout_reapply(argc, argv);
if (!strcmp(argv[0], "disable"))
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
+ if (startup_info->original_cwd)
+ cp.dir = startup_info->original_cwd;
strvec_pushl(&cp.args, "clean", "--force",
- "--quiet", "-d", NULL);
+ "--quiet", "-d", ":/", NULL);
if (include_untracked == INCLUDE_ALL_FILES)
strvec_push(&cp.args, "-x");
if (run_command(&cp)) {
if (argc) {
force_assume = !strcmp(argv[0], "-p");
argc = parse_options(argc, argv, prefix, options,
+ push_assumed ? git_stash_usage :
git_stash_push_usage,
PARSE_OPT_KEEP_DASHDASH);
}
struct startup_info {
int have_repository;
const char *prefix;
+ const char *original_cwd;
};
extern struct startup_info *startup_info;
+extern const char *tmp_original_cwd;
/* merge.c */
struct commit_list;
int main(int argc, const char **argv)
{
int result;
+ struct strbuf tmp = STRBUF_INIT;
trace2_initialize_clock();
trace2_cmd_start(argv);
trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
- result = cmd_main(argc, argv);
+ if (!strbuf_getcwd(&tmp))
+ tmp_original_cwd = strbuf_detach(&tmp, NULL);
- trace2_cmd_exit(result);
+ result = cmd_main(argc, argv);
- return result;
+ /*
+ * We define exit() to call trace2_cmd_exit_fl() in
+ * git-compat-util.h. Whether we reach this or exit()
+ * elsewhere we'll always run our trace2 exit handler.
+ */
+ exit(result);
}
endif
endif
endif
+
+ifneq ($(or $(filter gcc6,$(COMPILER_FEATURES)),$(filter clang7,$(COMPILER_FEATURES))),)
+DEVELOPER_CFLAGS += -std=gnu99
+endif
+
DEVELOPER_CFLAGS += -Wdeclaration-after-statement
DEVELOPER_CFLAGS += -Wformat-security
DEVELOPER_CFLAGS += -Wold-style-definition
for i in $(test_seq 501)
do
echo "creating revision $i" &&
- wiki_editpage foo "revision $i<br/>" true
+ wiki_editpage foo "revision $i<br/>" true || return 1
done
'
) &&
test_create_commit "$test_count" folder_subtree/0 &&
test_create_commit "$test_count" folder_subtree/b &&
- cherry=$(cd "$test_count"; git rev-parse HEAD) &&
+ cherry=$(cd "$test_count" && git rev-parse HEAD) &&
(
cd "$test_count" &&
git checkout branch
rlen = strlcpy(interp_path, expanded_path.buf,
sizeof(interp_path));
+ strbuf_release(&expanded_path);
if (rlen >= sizeof(interp_path)) {
logerror("interpolated path too large: %s",
interp_path);
return NULL;
}
- strbuf_release(&expanded_path);
loginfo("Interpolated dir '%s'", interp_path);
dir = interp_path;
#include "submodule-config.h"
#include "submodule.h"
#include "hashmap.h"
+#include "mem-pool.h"
#include "ll-merge.h"
#include "string-list.h"
#include "strvec.h"
int flags;
int indent_off; /* Offset to first non-whitespace character */
int indent_width; /* The visual width of the indentation */
+ unsigned id;
enum diff_symbol s;
};
#define EMITTED_DIFF_SYMBOL_INIT { 0 }
}
struct moved_entry {
- struct hashmap_entry ent;
const struct emitted_diff_symbol *es;
struct moved_entry *next_line;
+ struct moved_entry *next_match;
};
struct moved_block {
int wsd; /* The whitespace delta of this block */
};
-static void moved_block_clear(struct moved_block *b)
-{
- memset(b, 0, sizeof(*b));
-}
-
#define INDENT_BLANKLINE INT_MIN
static void fill_es_indent_data(struct emitted_diff_symbol *es)
}
static int compute_ws_delta(const struct emitted_diff_symbol *a,
- const struct emitted_diff_symbol *b,
- int *out)
-{
- int a_len = a->len,
- b_len = b->len,
- a_off = a->indent_off,
- a_width = a->indent_width,
- b_off = b->indent_off,
+ const struct emitted_diff_symbol *b)
+{
+ int a_width = a->indent_width,
b_width = b->indent_width;
- int delta;
- if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) {
- *out = INDENT_BLANKLINE;
- return 1;
- }
-
- if (a->s == DIFF_SYMBOL_PLUS)
- delta = a_width - b_width;
- else
- delta = b_width - a_width;
-
- if (a_len - a_off != b_len - b_off ||
- memcmp(a->line + a_off, b->line + b_off, a_len - a_off))
- return 0;
+ if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE)
+ return INDENT_BLANKLINE;
- *out = delta;
-
- return 1;
+ return a_width - b_width;
}
-static int cmp_in_block_with_wsd(const struct diff_options *o,
- const struct moved_entry *cur,
- const struct moved_entry *match,
- struct moved_block *pmb,
- int n)
-{
- struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
- int al = cur->es->len, bl = match->es->len, cl = l->len;
- const char *a = cur->es->line,
- *b = match->es->line,
- *c = l->line;
- int a_off = cur->es->indent_off,
- a_width = cur->es->indent_width,
- c_off = l->indent_off,
- c_width = l->indent_width;
+static int cmp_in_block_with_wsd(const struct moved_entry *cur,
+ const struct emitted_diff_symbol *l,
+ struct moved_block *pmb)
+{
+ int a_width = cur->es->indent_width, b_width = l->indent_width;
int delta;
- /*
- * We need to check if 'cur' is equal to 'match'. As those
- * are from the same (+/-) side, we do not need to adjust for
- * indent changes. However these were found using fuzzy
- * matching so we do have to check if they are equal. Here we
- * just check the lengths. We delay calling memcmp() to check
- * the contents until later as if the length comparison for a
- * and c fails we can avoid the call all together.
- */
- if (al != bl)
+ /* The text of each line must match */
+ if (cur->es->id != l->id)
return 1;
- /* If 'l' and 'cur' are both blank then they match. */
- if (a_width == INDENT_BLANKLINE && c_width == INDENT_BLANKLINE)
+ /*
+ * If 'l' and 'cur' are both blank then we don't need to check the
+ * indent. We only need to check cur as we know the strings match.
+ * */
+ if (a_width == INDENT_BLANKLINE)
return 0;
/*
* The indent changes of the block are known and stored in pmb->wsd;
* however we need to check if the indent changes of the current line
- * match those of the current block and that the text of 'l' and 'cur'
- * after the indentation match.
+ * match those of the current block.
*/
- if (cur->es->s == DIFF_SYMBOL_PLUS)
- delta = a_width - c_width;
- else
- delta = c_width - a_width;
+ delta = b_width - a_width;
/*
* If the previous lines of this block were all blank then set its
if (pmb->wsd == INDENT_BLANKLINE)
pmb->wsd = delta;
- return !(delta == pmb->wsd && al - a_off == cl - c_off &&
- !memcmp(a, b, al) && !
- memcmp(a + a_off, c + c_off, al - a_off));
+ return delta != pmb->wsd;
}
-static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata)
+struct interned_diff_symbol {
+ struct hashmap_entry ent;
+ struct emitted_diff_symbol *es;
+};
+
+static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
+ const void *keydata)
{
const struct diff_options *diffopt = hashmap_cmp_fn_data;
- const struct moved_entry *a, *b;
+ const struct emitted_diff_symbol *a, *b;
unsigned flags = diffopt->color_moved_ws_handling
& XDF_WHITESPACE_FLAGS;
- a = container_of(eptr, const struct moved_entry, ent);
- b = container_of(entry_or_key, const struct moved_entry, ent);
-
- if (diffopt->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- /*
- * As there is not specific white space config given,
- * we'd need to check for a new block, so ignore all
- * white space. The setup of the white space
- * configuration for the next block is done else where
- */
- flags |= XDF_IGNORE_WHITESPACE;
+ a = container_of(eptr, const struct interned_diff_symbol, ent)->es;
+ b = container_of(entry_or_key, const struct interned_diff_symbol, ent)->es;
- return !xdiff_compare_lines(a->es->line, a->es->len,
- b->es->line, b->es->len,
- flags);
+ return !xdiff_compare_lines(a->line + a->indent_off,
+ a->len - a->indent_off,
+ b->line + b->indent_off,
+ b->len - b->indent_off, flags);
}
-static struct moved_entry *prepare_entry(struct diff_options *o,
- int line_no)
+static void prepare_entry(struct diff_options *o, struct emitted_diff_symbol *l,
+ struct interned_diff_symbol *s)
{
- struct moved_entry *ret = xmalloc(sizeof(*ret));
- struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
- unsigned int hash = xdiff_hash_string(l->line, l->len, flags);
-
- hashmap_entry_init(&ret->ent, hash);
- ret->es = l;
- ret->next_line = NULL;
+ unsigned int hash = xdiff_hash_string(l->line + l->indent_off,
+ l->len - l->indent_off, flags);
- return ret;
+ hashmap_entry_init(&s->ent, hash);
+ s->es = l;
}
-static void add_lines_to_move_detection(struct diff_options *o,
- struct hashmap *add_lines,
- struct hashmap *del_lines)
+struct moved_entry_list {
+ struct moved_entry *add, *del;
+};
+
+static struct moved_entry_list *add_lines_to_move_detection(struct diff_options *o,
+ struct mem_pool *entry_mem_pool)
{
struct moved_entry *prev_line = NULL;
-
+ struct mem_pool interned_pool;
+ struct hashmap interned_map;
+ struct moved_entry_list *entry_list = NULL;
+ size_t entry_list_alloc = 0;
+ unsigned id = 0;
int n;
+
+ hashmap_init(&interned_map, interned_diff_symbol_cmp, o, 8096);
+ mem_pool_init(&interned_pool, 1024 * 1024);
+
for (n = 0; n < o->emitted_symbols->nr; n++) {
- struct hashmap *hm;
- struct moved_entry *key;
+ struct interned_diff_symbol key;
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+ struct interned_diff_symbol *s;
+ struct moved_entry *entry;
- switch (o->emitted_symbols->buf[n].s) {
- case DIFF_SYMBOL_PLUS:
- hm = add_lines;
- break;
- case DIFF_SYMBOL_MINUS:
- hm = del_lines;
- break;
- default:
+ if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS) {
prev_line = NULL;
continue;
}
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- fill_es_indent_data(&o->emitted_symbols->buf[n]);
- key = prepare_entry(o, n);
- if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
- prev_line->next_line = key;
+ fill_es_indent_data(l);
- hashmap_add(hm, &key->ent);
- prev_line = key;
+ prepare_entry(o, l, &key);
+ s = hashmap_get_entry(&interned_map, &key, ent, &key.ent);
+ if (s) {
+ l->id = s->es->id;
+ } else {
+ l->id = id;
+ ALLOC_GROW_BY(entry_list, id, 1, entry_list_alloc);
+ hashmap_add(&interned_map,
+ memcpy(mem_pool_alloc(&interned_pool,
+ sizeof(key)),
+ &key, sizeof(key)));
+ }
+ entry = mem_pool_alloc(entry_mem_pool, sizeof(*entry));
+ entry->es = l;
+ entry->next_line = NULL;
+ if (prev_line && prev_line->es->s == l->s)
+ prev_line->next_line = entry;
+ prev_line = entry;
+ if (l->s == DIFF_SYMBOL_PLUS) {
+ entry->next_match = entry_list[l->id].add;
+ entry_list[l->id].add = entry;
+ } else {
+ entry->next_match = entry_list[l->id].del;
+ entry_list[l->id].del = entry;
+ }
}
+
+ hashmap_clear(&interned_map);
+ mem_pool_discard(&interned_pool, 0);
+
+ return entry_list;
}
static void pmb_advance_or_null(struct diff_options *o,
- struct moved_entry *match,
- struct hashmap *hm,
+ struct emitted_diff_symbol *l,
struct moved_block *pmb,
- int pmb_nr)
+ int *pmb_nr)
{
- int i;
- for (i = 0; i < pmb_nr; i++) {
+ int i, j;
+
+ for (i = 0, j = 0; i < *pmb_nr; i++) {
+ int match;
struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
- if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {
- pmb[i].match = cur;
- } else {
- pmb[i].match = NULL;
- }
- }
-}
-static void pmb_advance_or_null_multi_match(struct diff_options *o,
- struct moved_entry *match,
- struct hashmap *hm,
- struct moved_block *pmb,
- int pmb_nr, int n)
-{
- int i;
- char *got_match = xcalloc(1, pmb_nr);
-
- hashmap_for_each_entry_from(hm, match, ent) {
- for (i = 0; i < pmb_nr; i++) {
- struct moved_entry *prev = pmb[i].match;
- struct moved_entry *cur = (prev && prev->next_line) ?
- prev->next_line : NULL;
- if (!cur)
- continue;
- if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n))
- got_match[i] |= 1;
- }
- }
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ match = cur &&
+ !cmp_in_block_with_wsd(cur, l, &pmb[i]);
+ else
+ match = cur && cur->es->id == l->id;
- for (i = 0; i < pmb_nr; i++) {
- if (got_match[i]) {
- /* Advance to the next line */
- pmb[i].match = pmb[i].match->next_line;
- } else {
- moved_block_clear(&pmb[i]);
+ if (match) {
+ pmb[j] = pmb[i];
+ pmb[j++].match = cur;
}
}
-
- free(got_match);
+ *pmb_nr = j;
}
-static int shrink_potential_moved_blocks(struct moved_block *pmb,
- int pmb_nr)
-{
- int lp, rp;
-
- /* Shrink the set of potential block to the remaining running */
- for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
- while (lp < pmb_nr && pmb[lp].match)
- lp++;
- /* lp points at the first NULL now */
+static void fill_potential_moved_blocks(struct diff_options *o,
+ struct moved_entry *match,
+ struct emitted_diff_symbol *l,
+ struct moved_block **pmb_p,
+ int *pmb_alloc_p, int *pmb_nr_p)
- while (rp > -1 && !pmb[rp].match)
- rp--;
- /* rp points at the last non-NULL */
+{
+ struct moved_block *pmb = *pmb_p;
+ int pmb_alloc = *pmb_alloc_p, pmb_nr = *pmb_nr_p;
- if (lp < pmb_nr && rp > -1 && lp < rp) {
- pmb[lp] = pmb[rp];
- memset(&pmb[rp], 0, sizeof(pmb[rp]));
- rp--;
- lp++;
- }
+ /*
+ * The current line is the start of a new block.
+ * Setup the set of potential blocks.
+ */
+ for (; match; match = match->next_match) {
+ ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ pmb[pmb_nr].wsd = compute_ws_delta(l, match->es);
+ else
+ pmb[pmb_nr].wsd = 0;
+ pmb[pmb_nr++].match = match;
}
- /* Remember the number of running sets */
- return rp + 1;
+ *pmb_p = pmb;
+ *pmb_alloc_p = pmb_alloc;
+ *pmb_nr_p = pmb_nr;
}
/*
* NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
* Think of a way to unify them.
*/
+#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
+ (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
static int adjust_last_block(struct diff_options *o, int n, int block_length)
{
int i, alnum_count = 0;
}
}
for (i = 1; i < block_length + 1; i++)
- o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+ o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK;
return 0;
}
/* Find blocks of moved code, delegate actual coloring decision to helper */
static void mark_color_as_moved(struct diff_options *o,
- struct hashmap *add_lines,
- struct hashmap *del_lines)
+ struct moved_entry_list *entry_list)
{
struct moved_block *pmb = NULL; /* potentially moved blocks */
int pmb_nr = 0, pmb_alloc = 0;
int n, flipped_block = 0, block_length = 0;
+ enum diff_symbol moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
for (n = 0; n < o->emitted_symbols->nr; n++) {
- struct hashmap *hm = NULL;
- struct moved_entry *key;
struct moved_entry *match = NULL;
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
- enum diff_symbol last_symbol = 0;
switch (l->s) {
case DIFF_SYMBOL_PLUS:
- hm = del_lines;
- key = prepare_entry(o, n);
- match = hashmap_get_entry(hm, key, ent, NULL);
- free(key);
+ match = entry_list[l->id].del;
break;
case DIFF_SYMBOL_MINUS:
- hm = add_lines;
- key = prepare_entry(o, n);
- match = hashmap_get_entry(hm, key, ent, NULL);
- free(key);
+ match = entry_list[l->id].add;
break;
default:
flipped_block = 0;
}
- if (!match) {
- int i;
-
- adjust_last_block(o, n, block_length);
- for(i = 0; i < pmb_nr; i++)
- moved_block_clear(&pmb[i]);
+ if (pmb_nr && (!match || l->s != moved_symbol)) {
+ if (!adjust_last_block(o, n, block_length) &&
+ block_length > 1) {
+ /*
+ * Rewind in case there is another match
+ * starting at the second line of the block
+ */
+ match = NULL;
+ n -= block_length;
+ }
pmb_nr = 0;
block_length = 0;
flipped_block = 0;
- last_symbol = l->s;
+ }
+ if (!match) {
+ moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
continue;
}
if (o->color_moved == COLOR_MOVED_PLAIN) {
- last_symbol = l->s;
l->flags |= DIFF_SYMBOL_MOVED_LINE;
continue;
}
- if (o->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
- else
- pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
-
- pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
+ pmb_advance_or_null(o, l, pmb, &pmb_nr);
if (pmb_nr == 0) {
- /*
- * The current line is the start of a new block.
- * Setup the set of potential blocks.
- */
- hashmap_for_each_entry_from(hm, match, ent) {
- ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
- if (o->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
- if (compute_ws_delta(l, match->es,
- &pmb[pmb_nr].wsd))
- pmb[pmb_nr++].match = match;
- } else {
- pmb[pmb_nr].wsd = 0;
- pmb[pmb_nr++].match = match;
- }
- }
+ int contiguous = adjust_last_block(o, n, block_length);
+
+ if (!contiguous && block_length > 1)
+ /*
+ * Rewind in case there is another match
+ * starting at the second line of the block
+ */
+ n -= block_length;
+ else
+ fill_potential_moved_blocks(o, match, l,
+ &pmb, &pmb_alloc,
+ &pmb_nr);
- if (adjust_last_block(o, n, block_length) &&
- pmb_nr && last_symbol != l->s)
+ if (contiguous && pmb_nr && moved_symbol == l->s)
flipped_block = (flipped_block + 1) % 2;
else
flipped_block = 0;
+ if (pmb_nr)
+ moved_symbol = l->s;
+ else
+ moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
+
block_length = 0;
}
if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
}
- last_symbol = l->s;
}
adjust_last_block(o, n, block_length);
- for(n = 0; n < pmb_nr; n++)
- moved_block_clear(&pmb[n]);
free(pmb);
}
-#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
- (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
static void dim_moved_lines(struct diff_options *o)
{
int n;
static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
const char *line, int len, unsigned flags)
{
- struct emitted_diff_symbol e = {line, len, flags, 0, 0, s};
+ struct emitted_diff_symbol e = {
+ .line = line, .len = len, .flags = flags, .s = s
+ };
if (o->emitted_symbols)
append_emitted_diff_symbol(o, &e);
if (o->emitted_symbols) {
if (o->color_moved) {
- struct hashmap add_lines, del_lines;
-
- if (o->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
-
- hashmap_init(&del_lines, moved_entry_cmp, o, 0);
- hashmap_init(&add_lines, moved_entry_cmp, o, 0);
+ struct mem_pool entry_pool;
+ struct moved_entry_list *entry_list;
- add_lines_to_move_detection(o, &add_lines, &del_lines);
- mark_color_as_moved(o, &add_lines, &del_lines);
+ mem_pool_init(&entry_pool, 1024 * 1024);
+ entry_list = add_lines_to_move_detection(o,
+ &entry_pool);
+ mark_color_as_moved(o, entry_list);
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
dim_moved_lines(o);
- hashmap_clear_and_free(&add_lines, struct moved_entry,
- ent);
- hashmap_clear_and_free(&del_lines, struct moved_entry,
- ent);
+ mem_pool_discard(&entry_pool, 0);
+ free(entry_list);
}
for (i = 0; i < esm.nr; i++)
}
if (given->patternlen < 2 ||
- *given->pattern == '*' ||
+ *given->pattern != '/' ||
strstr(given->pattern, "**")) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
/* we already included this at the parent level */
warning(_("your sparse-checkout file may have issues: pattern '%s' is repeated"),
given->pattern);
- hashmap_remove(&pl->parent_hashmap, &translated->ent, &data);
- free(data);
- free(translated);
+ goto clear_hashmaps;
}
return;
int ret = 0, original_len = path->len, len, kept_down = 0;
int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
+ int purge_original_cwd = (flag & REMOVE_DIR_PURGE_ORIGINAL_CWD);
struct object_id submodule_head;
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
closedir(dir);
strbuf_setlen(path, original_len);
- if (!ret && !keep_toplevel && !kept_down)
- ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
- else if (kept_up)
+ if (!ret && !keep_toplevel && !kept_down) {
+ if (!purge_original_cwd &&
+ startup_info->original_cwd &&
+ !strcmp(startup_info->original_cwd, path->buf))
+ ret = -1; /* Do not remove current working directory */
+ else
+ ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
+ } else if (kept_up)
/*
* report the uplevel that it is not an error that we
* did not rmdir() our directory.
slash = dirs + (slash - name);
do {
*slash = '\0';
+ if (startup_info->original_cwd &&
+ !strcmp(startup_info->original_cwd, dirs))
+ break;
} while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
free(dirs);
}
/* Remove the contents of path, but leave path itself. */
#define REMOVE_DIR_KEEP_TOPLEVEL 04
+/* Remove the_original_cwd too */
+#define REMOVE_DIR_PURGE_ORIGINAL_CWD 0x08
+
/*
* Remove path and its contents, recursively. flags is a combination
* of the above REMOVE_DIR_* constants. Return 0 on success.
*/
int remove_dir_recursively(struct strbuf *path, int flag);
-/* tries to remove the path with empty directories along it, ignores ENOENT */
+/*
+ * Tries to remove the path, along with leading empty directories so long as
+ * those empty directories are not startup_info->original_cwd. Ignores
+ * ENOENT.
+ */
int remove_path(const char *path);
int fspathcmp(const char *a, const char *b);
#include "commit.h"
#include "strvec.h"
#include "object-store.h"
+#include "tmp-objdir.h"
#include "chdir-notify.h"
#include "shallow.h"
args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
+ if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+ args.disable_ref_updates = 1;
+ }
+
repo_set_gitdir(the_repository, git_dir, &args);
strvec_clear(&to_free);
void *data)
{
char *path = reparent_relative_path(old_cwd, new_cwd, get_git_dir());
+ struct tmp_objdir *tmp_objdir = tmp_objdir_unapply_primary_odb();
+
trace_printf_key(&trace_setup_key,
"setup: move $GIT_DIR to '%s'",
path);
set_git_dir_1(path);
+ if (tmp_objdir)
+ tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
free(path);
}
memset(&merge_parents, 0, sizeof(merge_parents));
- /* get current branch */
+ /* learn the commit that we merge into and the current branch name */
current_branch = current_branch_to_free =
resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
if (!current_branch)
die("No current branch");
- if (starts_with(current_branch, "refs/heads/"))
+
+ if (opts->into_name)
+ current_branch = opts->into_name;
+ else if (starts_with(current_branch, "refs/heads/"))
current_branch += 11;
find_merge_parents(&merge_parents, in, &head_oid);
unsigned add_title:1,
credit_people:1;
int shortlog_len;
+ const char *into_name;
};
extern int merge_log_config;
/*
* See if our compiler is known to support flexible array members.
*/
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580))
-# define FLEX_ARRAY /* empty */
+
+/*
+ * Check vendor specific quirks first, before checking the
+ * __STDC_VERSION__, as vendor compilers can lie and we need to be
+ * able to work them around. Note that by not defining FLEX_ARRAY
+ * here, we can fall back to use the "safer but a bit wasteful" one
+ * later.
+ */
+#if defined(__SUNPRO_C) && (__SUNPRO_C <= 0x580)
#elif defined(__GNUC__)
# if (__GNUC__ >= 3)
# define FLEX_ARRAY /* empty */
# else
# define FLEX_ARRAY 0 /* older GNU extension */
# endif
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARRAY /* empty */
#endif
/*
p4_access_checked = False
+re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$')
+re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$')
+
+def format_size_human_readable(num):
+ """ Returns a number of units (typically bytes) formatted as a human-readable
+ string.
+ """
+ if num < 1024:
+ return '{:d} B'.format(num)
+ for unit in ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
+ num /= 1024.0
+ if num < 1024.0:
+ return "{:3.1f} {}B".format(num, unit)
+ return "{:.1f} YiB".format(num)
+
def p4_build_cmd(cmd):
"""Build a suitable p4 command line.
real_cmd = p4_build_cmd(c)
return read_pipe(real_cmd, ignore_error, raw=raw)
-def read_pipe_lines(c):
+def read_pipe_lines(c, raw=False):
if verbose:
sys.stderr.write('Reading pipe: %s\n' % str(c))
expand = not isinstance(c, list)
p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
pipe = p.stdout
- val = [decode_text_stream(line) for line in pipe.readlines()]
+ lines = pipe.readlines()
+ if not raw:
+ lines = [decode_text_stream(line) for line in lines]
if pipe.close() or p.wait():
die('Command failed: %s' % str(c))
- return val
+ return lines
def p4_read_pipe_lines(c):
"""Specifically invoke p4 on the command supplied. """
#
def p4_keywords_regexp_for_type(base, type_mods):
if base in ("text", "unicode", "binary"):
- kwords = None
if "ko" in type_mods:
- kwords = 'Id|Header'
+ return re_ko_keywords
elif "k" in type_mods:
- kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
+ return re_k_keywords
else:
return None
- pattern = r"""
- \$ # Starts with a dollar, followed by...
- (%s) # one of the keywords, followed by...
- (:[^$\n]+)? # possibly an old expansion, followed by...
- \$ # another dollar
- """ % kwords
- return pattern
else:
return None
except IOError:
self.getUserMapFromPerforceServer()
-class P4Debug(Command):
- def __init__(self):
- Command.__init__(self)
- self.options = []
- self.description = "A tool to debug the output of p4 -G."
- self.needsGit = False
-
- def run(self, args):
- j = 0
- for output in p4CmdList(args):
- print('Element: %d' % j)
- j += 1
- print(output)
- return True
-
-class P4RollBack(Command):
- def __init__(self):
- Command.__init__(self)
- self.options = [
- optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
- ]
- self.description = "A tool to debug the multi-branch import. Don't use :)"
- self.rollbackLocalBranches = False
-
- def run(self, args):
- if len(args) != 1:
- return False
- maxChange = int(args[0])
-
- if "p4ExitCode" in p4Cmd("changes -m 1"):
- die("Problems executing p4");
-
- if self.rollbackLocalBranches:
- refPrefix = "refs/heads/"
- lines = read_pipe_lines("git rev-parse --symbolic --branches")
- else:
- refPrefix = "refs/remotes/"
- lines = read_pipe_lines("git rev-parse --symbolic --remotes")
-
- for line in lines:
- if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
- line = line.strip()
- ref = refPrefix + line
- log = extractLogMessageFromGitCommit(ref)
- settings = extractSettingsGitLog(log)
-
- depotPaths = settings['depot-paths']
- change = settings['change']
-
- changed = False
-
- if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange)
- for p in depotPaths]))) == 0:
- print("Branch %s did not exist at change %s, deleting." % (ref, maxChange))
- system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
- continue
-
- while change and int(change) > maxChange:
- changed = True
- if self.verbose:
- print("%s is at %s ; rewinding towards %s" % (ref, change, maxChange))
- system("git update-ref %s \"%s^\"" % (ref, ref))
- log = extractLogMessageFromGitCommit(ref)
- settings = extractSettingsGitLog(log)
-
-
- depotPaths = settings['depot-paths']
- change = settings['change']
-
- if changed:
- print("%s rewound to %s" % (ref, change))
-
- return True
-
class P4Submit(Command, P4UserMap):
conflict_behavior_choices = ("ask", "skip", "quit")
return result
- def patchRCSKeywords(self, file, pattern):
- # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
+ def patchRCSKeywords(self, file, regexp):
+ # Attempt to zap the RCS keywords in a p4 controlled file matching the given regex
(handle, outFileName) = tempfile.mkstemp(dir='.')
try:
- outFile = os.fdopen(handle, "w+")
- inFile = open(file, "r")
- regexp = re.compile(pattern, re.VERBOSE)
- for line in inFile.readlines():
- line = regexp.sub(r'$\1$', line)
- outFile.write(line)
- inFile.close()
- outFile.close()
+ with os.fdopen(handle, "wb") as outFile, open(file, "rb") as inFile:
+ for line in inFile.readlines():
+ outFile.write(regexp.sub(br'$\1$', line))
# Forcibly overwrite the original file
os.unlink(file)
shutil.move(outFileName, file)
# the patch to see if that's possible.
if gitConfigBool("git-p4.attemptRCSCleanup"):
file = None
- pattern = None
kwfiles = {}
for file in editedFiles | filesToDelete:
# did this file's delta contain RCS keywords?
- pattern = p4_keywords_regexp_for_file(file)
-
- if pattern:
+ regexp = p4_keywords_regexp_for_file(file)
+ if regexp:
# this file is a possibility...look for RCS keywords.
- regexp = re.compile(pattern, re.VERBOSE)
- for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
+ for line in read_pipe_lines(
+ ["git", "diff", "%s^..%s" % (id, id), file],
+ raw=True):
if regexp.search(line):
if verbose:
- print("got keyword match on %s in %s in %s" % (pattern, line, file))
- kwfiles[file] = pattern
+ print("got keyword match on %s in %s in %s" % (regex.pattern, line, file))
+ kwfiles[file] = regexp
break
- for file in kwfiles:
+ for file, regexp in kwfiles.items():
if verbose:
- print("zapping %s with %s" % (line,pattern))
+ print("zapping %s with %s" % (line, regexp.pattern))
# File is being deleted, so not open in p4. Must
# disable the read-only bit on windows.
if self.isWindows and file not in editedFiles:
size = int(self.stream_file['fileSize'])
else:
size = 0 # deleted files don't get a fileSize apparently
- sys.stdout.write('\r%s --> %s (%i MB)\n' % (file_path, relPath, size/1024/1024))
+ sys.stdout.write('\r%s --> %s (%s)\n' % (
+ file_path, relPath, format_size_human_readable(size)))
sys.stdout.flush()
(type_base, type_mods) = split_p4_type(file["type"])
# Note that we do not try to de-mangle keywords on utf16 files,
# even though in theory somebody may want that.
- pattern = p4_keywords_regexp_for_type(type_base, type_mods)
- if pattern:
- regexp = re.compile(pattern, re.VERBOSE)
- text = ''.join(decode_text_stream(c) for c in contents)
- text = regexp.sub(r'$\1$', text)
- contents = [ encode_text_stream(text) ]
+ regexp = p4_keywords_regexp_for_type(type_base, type_mods)
+ if regexp:
+ contents = [regexp.sub(br'$\1$', c) for c in contents]
if self.largeFileSystem:
(git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)
if not err and 'fileSize' in self.stream_file:
required_bytes = int((4 * int(self.stream_file["fileSize"])) - calcDiskFree())
if required_bytes > 0:
- err = 'Not enough space left on %s! Free at least %i MB.' % (
- os.getcwd(), required_bytes/1024/1024
- )
+ err = 'Not enough space left on %s! Free at least %s.' % (
+ os.getcwd(), format_size_human_readable(required_bytes))
if err:
f = None
size = int(self.stream_file["fileSize"])
if size > 0:
progress = 100*self.stream_file['streamContentSize']/size
- sys.stdout.write('\r%s %d%% (%i MB)' % (self.stream_file['depotFile'], progress, int(size/1024/1024)))
+ sys.stdout.write('\r%s %d%% (%s)' % (
+ self.stream_file['depotFile'], progress,
+ format_size_human_readable(size)))
sys.stdout.flush()
self.stream_have_file_info = True
self.updateOptionDict(description)
if not self.silent:
- sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+ sys.stdout.write("\rImporting revision %s (%d%%)" % (
+ change, (cnt * 100) // len(changes)))
sys.stdout.flush()
cnt = cnt + 1
print("")
commands = {
- "debug" : P4Debug,
"submit" : P4Submit,
"commit" : P4Submit,
"sync" : P4Sync,
"rebase" : P4Rebase,
"clone" : P4Clone,
- "rollback" : P4RollBack,
"branches" : P4Branches,
"unshelve" : P4Unshelve,
}
int jitret;
int patinforet;
size_t jitsizearg;
+ int literal = !opt->ignore_case && (p->fixed || p->is_fixed);
/*
* Call pcre2_general_context_create() before calling any
}
options |= PCRE2_CASELESS;
}
- if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) &&
- !(!opt->ignore_case && (p->fixed || p->is_fixed)))
+ if (!opt->ignore_locale && is_utf8_locale() && !literal)
options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
#ifdef GIT_PCRE2_VERSION_10_36_OR_HIGHER
return compile_pattern_or(list);
}
+static struct grep_expr *grep_not_expr(struct grep_expr *expr)
+{
+ struct grep_expr *z = xcalloc(1, sizeof(*z));
+ z->node = GREP_NODE_NOT;
+ z->u.unary = expr;
+ return z;
+}
+
static struct grep_expr *grep_true_expr(void)
{
struct grep_expr *z = xcalloc(1, sizeof(*z));
}
}
- if (opt->all_match || header_expr)
+ if (opt->all_match || opt->no_body_match || header_expr)
opt->extended = 1;
else if (!opt->extended)
return;
if (p)
die("incomplete pattern expression: %s", p->pattern);
+ if (opt->no_body_match && opt->pattern_expression)
+ opt->pattern_expression = grep_not_expr(opt->pattern_expression);
+
if (!header_expr)
return;
if (h && (*col < 0 || tmp.rm_so < *col))
*col = tmp.rm_so;
}
+ if (x->u.atom->token == GREP_PATTERN_BODY)
+ opt->body_hit |= h;
break;
case GREP_NODE_NOT:
/*
* we do not have to do the two-pass grep when we do not check
* buffer-wide "all-match".
*/
- if (!opt->all_match)
+ if (!opt->all_match && !opt->no_body_match)
return grep_source_1(opt, gs, 0);
/* Otherwise the toplevel "or" terms hit a bit differently.
* We first clear hit markers from them.
*/
clr_hit_marker(opt->pattern_expression);
+ opt->body_hit = 0;
grep_source_1(opt, gs, 1);
- if (!chk_hit_marker(opt->pattern_expression))
+ if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
+ return 0;
+ if (opt->no_body_match && opt->body_hit)
return 0;
return grep_source_1(opt, gs, 0);
int word_regexp;
int fixed;
int all_match;
+ int no_body_match;
+ int body_hit;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
else if (autocorrect == AUTOCORRECT_PROMPT) {
char *answer;
struct strbuf msg = STRBUF_INIT;
- strbuf_addf(&msg, _("Run '%s' instead? (y/N)"), assumed);
+ strbuf_addf(&msg, _("Run '%s' instead [y/N]? "), assumed);
answer = git_prompt(msg.buf, PROMPT_ECHO);
strbuf_release(&msg);
if (!(starts_with(answer, "y") ||
'\n', NULL, 0);
}
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)
+{
+ struct object_directory *new_odb;
+
+ /*
+ * Make sure alternates are initialized, or else our entry may be
+ * overwritten when they are.
+ */
+ prepare_alt_odb(the_repository);
+
+ /*
+ * Make a new primary odb and link the old primary ODB in as an
+ * alternate
+ */
+ new_odb = xcalloc(1, sizeof(*new_odb));
+ new_odb->path = xstrdup(dir);
+
+ /*
+ * Disable ref updates while a temporary odb is active, since
+ * the objects in the database may roll back.
+ */
+ new_odb->disable_ref_updates = 1;
+ new_odb->will_destroy = will_destroy;
+ new_odb->next = the_repository->objects->odb;
+ the_repository->objects->odb = new_odb;
+ return new_odb->next;
+}
+
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)
+{
+ struct object_directory *cur_odb = the_repository->objects->odb;
+
+ if (strcmp(old_path, cur_odb->path))
+ BUG("expected %s as primary object store; found %s",
+ old_path, cur_odb->path);
+
+ if (cur_odb->next != restore_odb)
+ BUG("we expect the old primary object store to be the first alternate");
+
+ the_repository->objects->odb = restore_odb;
+ free_object_directory(cur_odb);
+}
+
/*
* Compute the exact path an alternate is at and returns it. In case of
* error NULL is returned and the human readable error is added to `err`
/* Finalize a file on disk, and close it. */
static void close_loose_object(int fd)
{
- if (fsync_object_files)
- fsync_or_die(fd, "loose object file");
+ if (!the_repository->objects->odb->will_destroy) {
+ if (fsync_object_files)
+ fsync_or_die(fd, "loose object file");
+ }
+
if (close(fd) != 0)
die_errno(_("error when closing loose object file"));
}
uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
struct oidtree *loose_objects_cache;
+ /*
+ * This is a temporary object store created by the tmp_objdir
+ * facility. Disable ref updates since the objects in the store
+ * might be discarded on rollback.
+ */
+ int disable_ref_updates;
+
+ /*
+ * This object store is ephemeral, so there is no need to fsync.
+ */
+ int will_destroy;
+
/*
* Path to the alternative object store. If this is a relative path,
* it is relative to the current working directory.
*/
void add_to_alternates_memory(const char *dir);
+/*
+ * Replace the current writable object directory with the specified temporary
+ * object directory; returns the former primary object directory.
+ */
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
+
+/*
+ * Restore a previous ODB replaced by set_temporary_main_odb.
+ */
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
+
/*
* Populate and return the loose object cache array corresponding to the
* given object ID.
/* Empty the loose object cache for the specified object directory. */
void odb_clear_loose_cache(struct object_directory *odb);
+/* Clear and free the specified object directory */
+void free_object_directory(struct object_directory *odb);
+
struct packed_git {
struct hashmap_entry packmap_ent;
struct packed_git *next;
return o;
}
-static void free_object_directory(struct object_directory *odb)
+void free_object_directory(struct object_directory *odb)
{
free(odb->path);
odb_clear_loose_cache(odb);
- Adjust the strings so that they're easy to translate. Most of the
advice in `info '(gettext)Preparing Strings'` applies here.
+- Strings referencing numbers of items may need to be split into singular and
+ plural forms; see the Q\_() wrapper in the C sub-section below for an
+ example.
+
- If something is unclear or ambiguous you can use a "TRANSLATORS"
comment to tell the translators what to make of it. These will be
extracted by xgettext(1) and put in the "po/\*.po" files, e.g. from
return refs;
}
-void base_ref_store_init(struct ref_store *refs,
- const struct ref_storage_be *be)
+void base_ref_store_init(struct ref_store *refs, struct repository *repo,
+ const char *path, const struct ref_storage_be *be)
{
refs->be = be;
+ refs->repo = repo;
+ refs->gitdir = xstrdup(path);
}
/* backend functions */
break;
}
- if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+ if (refs->repo->objects->odb->disable_ref_updates) {
strbuf_addstr(err,
_("ref updates forbidden inside quarantine environment"));
return -1;
be_copy->name = store->be->name;
trace_printf_key(&trace_refs, "ref_store for %s\n", gitdir);
res->refs = store;
- base_ref_store_init((struct ref_store *)res, be_copy);
+ base_ref_store_init((struct ref_store *)res, store->repo, gitdir,
+ be_copy);
return (struct ref_store *)res;
}
transaction->ref_store = drefs->refs;
res = drefs->refs->be->transaction_prepare(drefs->refs, transaction,
err);
- trace_printf_key(&trace_refs, "transaction_prepare: %d\n", res);
+ trace_printf_key(&trace_refs, "transaction_prepare: %d \"%s\"\n", res,
+ err->buf);
return res;
}
struct ref_store *ref_store = (struct ref_store *)refs;
struct strbuf sb = STRBUF_INIT;
- ref_store->repo = repo;
- ref_store->gitdir = xstrdup(gitdir);
- base_ref_store_init(ref_store, &refs_be_files);
+ base_ref_store_init(ref_store, repo, gitdir, &refs_be_files);
refs->store_flags = flags;
-
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
- strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
- refs->packed_ref_store = packed_ref_store_create(repo, sb.buf, flags);
- strbuf_release(&sb);
+ refs->packed_ref_store =
+ packed_ref_store_create(repo, refs->gitcommondir, flags);
chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
chdir_notify_reparent("files-backend $GIT_COMMONDIR",
}
struct ref_store *packed_ref_store_create(struct repository *repo,
- const char *path,
+ const char *gitdir,
unsigned int store_flags)
{
struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
+ struct strbuf sb = STRBUF_INIT;
- base_ref_store_init(ref_store, &refs_be_packed);
- ref_store->repo = repo;
- ref_store->gitdir = xstrdup(path);
+ base_ref_store_init(ref_store, repo, gitdir, &refs_be_packed);
refs->store_flags = store_flags;
- refs->path = xstrdup(path);
+ strbuf_addf(&sb, "%s/packed-refs", gitdir);
+ refs->path = strbuf_detach(&sb, NULL);
chdir_notify_reparent("packed-refs", &refs->path);
-
return ref_store;
}
*/
struct ref_store *packed_ref_store_create(struct repository *repo,
- const char *path,
+ const char *gitdir,
unsigned int store_flags);
/*
* Fill in the generic part of refs and add it to our collection of
* reference stores.
*/
-void base_ref_store_init(struct ref_store *refs,
- const struct ref_storage_be *be);
+void base_ref_store_init(struct ref_store *refs, struct repository *repo,
+ const char *path, const struct ref_storage_be *be);
/*
* Support GIT_TRACE_REFS by optionally wrapping the given ref_store instance.
expand_base_dir(&repo->objects->odb->path, o->object_dir,
repo->commondir, "objects");
+ repo->objects->odb->disable_ref_updates = o->disable_ref_updates;
+
free(repo->objects->alternate_db);
repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
expand_base_dir(&repo->graft_file, o->graft_file,
const char *graft_file;
const char *index_file;
const char *alternate_db;
+ int disable_ref_updates;
};
void repo_set_gitdir(struct repository *repo, const char *root,
} else if (!strcmp(arg, "--all-match")) {
revs->grep_filter.all_match = 1;
} else if (!strcmp(arg, "--invert-grep")) {
- revs->invert_grep = 1;
+ revs->grep_filter.no_body_match = 1;
} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
if (strcmp(optarg, "none"))
git_log_output_encoding = xstrdup(optarg);
(char *)message, strlen(message));
strbuf_release(&buf);
unuse_commit_buffer(commit, message);
- return opt->invert_grep ? !retval : retval;
+ return retval;
}
static inline int want_ancestry(const struct rev_info *revs)
/* Filter by commit log message */
struct grep_opt grep_filter;
- /* Negate the match of grep_filter */
- int invert_grep;
/* Display history graph */
struct git_graph *graph;
cmd.git_cmd = 1;
+ if (startup_info->original_cwd)
+ cmd.dir = startup_info->original_cwd;
strvec_push(&cmd.args, "checkout");
strvec_push(&cmd.args, commit);
strvec_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
static struct startup_info the_startup_info;
struct startup_info *startup_info = &the_startup_info;
+const char *tmp_original_cwd;
/*
* The input parameter must contain an absolute path, and it must already be
initialized = 1;
}
+static void setup_original_cwd(void)
+{
+ struct strbuf tmp = STRBUF_INIT;
+ const char *worktree = NULL;
+ int offset = -1;
+
+ if (!tmp_original_cwd)
+ return;
+
+ /*
+ * startup_info->original_cwd points to the current working
+ * directory we inherited from our parent process, which is a
+ * directory we want to avoid removing.
+ *
+ * For convience, we would like to have the path relative to the
+ * worktree instead of an absolute path.
+ *
+ * Yes, startup_info->original_cwd is usually the same as 'prefix',
+ * but differs in two ways:
+ * - prefix has a trailing '/'
+ * - if the user passes '-C' to git, that modifies the prefix but
+ * not startup_info->original_cwd.
+ */
+
+ /* Normalize the directory */
+ strbuf_realpath(&tmp, tmp_original_cwd, 1);
+ free((char*)tmp_original_cwd);
+ tmp_original_cwd = NULL;
+ startup_info->original_cwd = strbuf_detach(&tmp, NULL);
+
+ /*
+ * Get our worktree; we only protect the current working directory
+ * if it's in the worktree.
+ */
+ worktree = get_git_work_tree();
+ if (!worktree)
+ goto no_prevention_needed;
+
+ offset = dir_inside_of(startup_info->original_cwd, worktree);
+ if (offset >= 0) {
+ /*
+ * If startup_info->original_cwd == worktree, that is already
+ * protected and we don't need original_cwd as a secondary
+ * protection measure.
+ */
+ if (!*(startup_info->original_cwd + offset))
+ goto no_prevention_needed;
+
+ /*
+ * original_cwd was inside worktree; precompose it just as
+ * we do prefix so that built up paths will match
+ */
+ startup_info->original_cwd = \
+ precompose_string_if_needed(startup_info->original_cwd
+ + offset);
+ return;
+ }
+
+no_prevention_needed:
+ free((char*)startup_info->original_cwd);
+ startup_info->original_cwd = NULL;
+}
+
static int read_worktree_config(const char *var, const char *value, void *vdata)
{
struct repository_format *data = vdata;
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
}
+ setup_original_cwd();
strbuf_release(&dir);
strbuf_release(&gitdir);
{
while (removal.len > new_len) {
removal.buf[removal.len] = '\0';
- if (rmdir(removal.buf))
+ if ((startup_info->original_cwd &&
+ !strcmp(removal.buf, startup_info->original_cwd)) ||
+ rmdir(removal.buf))
break;
do {
removal.len--;
{
int match_len, last_slash, i, previous_slash;
+ if (startup_info->original_cwd &&
+ !strcmp(name, startup_info->original_cwd))
+ return; /* Do not remove the current working directory */
+
match_len = last_slash = i =
longest_path_match(name, len, removal.buf, removal.len,
&previous_slash);
check-chainlint:
@mkdir -p '$(CHAINLINTTMP_SQ)' && \
- err=0 && \
- for i in $(CHAINLINTTESTS); do \
- $(CHAINLINT) <chainlint/$$i.test | \
- sed -e '/^# LINT: /d' >'$(CHAINLINTTMP_SQ)'/$$i.actual && \
- diff -u chainlint/$$i.expect '$(CHAINLINTTMP_SQ)'/$$i.actual || err=1; \
- done && exit $$err
+ sed -e '/^# LINT: /d' $(patsubst %,chainlint/%.test,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/tests && \
+ sed -e '/^[ ]*$$/d' $(patsubst %,chainlint/%.expect,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/expect && \
+ $(CHAINLINT) '$(CHAINLINTTMP_SQ)'/tests | grep -v '^[ ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
+ diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
test-lint-filenames
GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
git commit -a -m "$i$j" &&
commit=$(git rev-parse --verify HEAD) &&
- graft="$graft$commit "
+ graft="$graft$commit " || return 1
done
done &&
printf "%s " $graft >.git/info/grafts &&
# in order to avoid misinterpreting the ")" in constructs such as "x=$(...)"
# and "case $x in *)" as ending the subshell.
#
-# Lines missing a final "&&" are flagged with "?!AMP?!", and lines which chain
-# commands with ";" internally rather than "&&" are flagged "?!SEMI?!". A line
-# may be flagged for both violations.
+# Lines missing a final "&&" are flagged with "?!AMP?!", as are lines which
+# chain commands with ";" internally rather than "&&". A line may be flagged
+# for both violations.
#
# Detection of a missing &&-link in a multi-line subshell is complicated by the
# fact that the last statement before the closing ")" must not end with "&&".
# "?!AMP?!" violation is removed from the "bar" line (retrieved from the "hold"
# area) since the final statement of a subshell must not end with "&&". The
# final line of a subshell may still break the &&-chain by using ";" internally
-# to chain commands together rather than "&&", so "?!SEMI?!" is never removed
-# from a line (even though "?!AMP?!" might be).
+# to chain commands together rather than "&&", but an internal "?!AMP?!" is
+# never removed from a line even though a line-ending "?!AMP?!" might be.
#
# Care is taken to recognize the last _statement_ of a multi-line subshell, not
# necessarily the last textual _line_ within the subshell, since &&-chaining
# receives similar treatment.
#
# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
-# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
-# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
+# line such as "cat <<EOF" is seen, the here-doc tag is copied to the front of
+# the line enclosed in angle brackets as a sentinel, giving "<EOF>cat <<EOF".
# As each subsequent line is read, it is appended to the target line and a
# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
# the content inside "<...>" matches the entirety of the newly-read line. For
# instance, if the next line read is "some data", when concatenated with the
-# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
+# target line, it becomes "<EOF>cat <<EOF\nsome data", and a match is attempted
# to see if "EOF" matches "some data". Since it doesn't, the next line is
# attempted. When a line consisting of only "EOF" (and possible whitespace) is
-# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
+# encountered, it is appended to the target line giving "<EOF>cat <<EOF\nEOF",
# in which case the "EOF" inside "<...>" does match the text following the
# newline, thus the closing here-doc tag has been found. The closing tag line
# and the "<...>" prefix on the target line are then discarded, leaving just
-# the target line "cat >out".
-#
-# To facilitate regression testing (and manual debugging), a ">" annotation is
-# applied to the line containing ")" which closes a subshell, ">>" to a line
-# closing a nested subshell, and ">>>" to a line closing both at once. This
-# makes it easy to detect whether the heuristics correctly identify
-# end-of-subshell.
+# the target line "cat <<EOF".
#------------------------------------------------------------------------------
# incomplete line -- slurp up next line
# here-doc -- swallow it to avoid false hits within its body (but keep the
# command to which it was attached)
-/<<[ ]*[-\\'"]*[A-Za-z0-9_]/ {
- s/^\(.*\)<<[ ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
- s/[ ]*<<//
+/<<-*[ ]*[\\'"]*[A-Za-z0-9_]/ {
+ /"[^"]*<<[^"]*"/bnotdoc
+ s/^\(.*<<-*[ ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1\2/
:hered
N
/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
s/^<[^>]*>//
s/\n.*$//
}
+:notdoc
# one-liner "(...) &&"
/^[ ]*!*[ ]*(..*)[ ]*&&[ ]*$/boneline
# "&&" (but not ";" in a string)
:oneline
/;/{
- /"[^"]*;[^"]*"/!s/^/?!SEMI?!/
+ /"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
}
b
h
bnextln
}
-# "(..." line -- split off and stash "(", then process "..." as its own line
+# "(..." line -- "(" opening subshell cuddled with command; temporarily replace
+# "(" with sentinel "^" and process the line as if "(" had been seen solo on
+# the preceding line; this temporary replacement prevents several rules from
+# accidentally thinking "(" introduces a nested subshell; "^" is changed back
+# to "(" at output time
x
-s/.*/(/
+s/.*//
x
-s/(//
+s/(/^/
bslurp
:nextln
/"[^'"]*'[^'"]*"/!bsqstr
}
:folded
-# here-doc -- swallow it
-/<<[ ]*[-\\'"]*[A-Za-z0-9_]/bheredoc
+# here-doc -- swallow it (but not "<<" in a string)
+/<<-*[ ]*[\\'"]*[A-Za-z0-9_]/{
+ /"[^"]*<<[^"]*"/!bheredoc
+}
# comment or empty line -- discard since final non-comment, non-empty line
# before closing ")", "done", "elsif", "else", or "fi" will need to be
# re-visited to drop "suspect" marking since final line of those constructs
/"[^"]*#[^"]*"/!s/[ ]#.*$//
}
# one-liner "case ... esac"
-/^[ ]*case[ ]*..*esac/bchkchn
+/^[ ^]*case[ ]*..*esac/bchkchn
# multi-line "case ... esac"
-/^[ ]*case[ ]..*[ ]in/bcase
+/^[ ^]*case[ ]..*[ ]in/bcase
# multi-line "for ... done" or "while ... done"
-/^[ ]*for[ ]..*[ ]in/bcont
-/^[ ]*while[ ]/bcont
+/^[ ^]*for[ ]..*[ ]in/bcont
+/^[ ^]*while[ ]/bcont
/^[ ]*do[ ]/bcont
/^[ ]*do[ ]*$/bcont
/;[ ]*do/bcont
/||[ ]*exit[ ]/bcont
/||[ ]*exit[ ]*$/bcont
# multi-line "if...elsif...else...fi"
-/^[ ]*if[ ]/bcont
+/^[ ^]*if[ ]/bcont
/^[ ]*then[ ]/bcont
/^[ ]*then[ ]*$/bcont
/;[ ]*then/bcont
/^[ ]*fi[ ]*[<>|]/bdone
/^[ ]*fi[ ]*)/bdone
# nested one-liner "(...) &&"
-/^[ ]*(.*)[ ]*&&[ ]*$/bchkchn
+/^[ ^]*(.*)[ ]*&&[ ]*$/bchkchn
# nested one-liner "(...)"
-/^[ ]*(.*)[ ]*$/bchkchn
+/^[ ^]*(.*)[ ]*$/bchkchn
# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
-/^[ ]*(.*)[ ]*[0-9]*[<>|]/bchkchn
+/^[ ^]*(.*)[ ]*[0-9]*[<>|]/bchkchn
# nested multi-line "(...\n...)"
-/^[ ]*(/bnest
+/^[ ^]*(/bnest
# multi-line "{...\n...}"
-/^[ ]*{/bblock
+/^[ ^]*{/bblock
# closing ")" on own line -- exit subshell
/^[ ]*)/bclssolo
# "$((...))" -- arithmetic expansion; not closing ")"
# string and not ";;" in one-liner "case...esac")
/;/{
/;;/!{
- /"[^"]*;[^"]*"/!s/^/?!SEMI?!/
+ /"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
}
}
# line ends with pipe "...|" -- valid; not missing "&&"
/|[ ]*$/bcont
# missing end-of-line "&&" -- mark suspect
-/&&[ ]*$/!s/^/?!AMP?!/
+/&&[ ]*$/!s/$/ ?!AMP?!/
:cont
# retrieve and print previous line
x
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
n
bslurp
# found here-doc -- swallow it to avoid false hits within its body (but keep
# the command to which it was attached)
:heredoc
-s/^\(.*\)<<[ ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
-s/[ ]*<<//
+s/^\(.*\)<<\(-*[ ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\3>\1?!HERE?!\2\3/
:hdocsub
N
/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
# found "case ... in" -- pass through untouched
:case
x
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
n
+:cascom
+/^[ ]*#/{
+ N
+ s/.*\n//
+ bcascom
+}
/^[ ]*esac/bslurp
bcase
# that line legitimately lacks "&&"
:else
x
-s/?!AMP?!//
+s/\( ?!AMP?!\)* ?!AMP?!$//
x
bcont
# "suspect" from final contained line since that line legitimately lacks "&&"
:done
x
-s/?!AMP?!//
+s/\( ?!AMP?!\)* ?!AMP?!$//
x
# is 'done' or 'fi' cuddled with ")" to close subshell?
/done.*)/bclose
:nest
x
:nstslrp
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
n
+:nstcom
+# comment -- not closing ")" if in comment
+/^[ ]*#/{
+ N
+ s/.*\n//
+ bnstcom
+}
# closing ")" on own line -- stop nested slurp
/^[ ]*)/bnstcl
-# comment -- not closing ")" if in comment
-/^[ ]*#/bnstcnt
# "$((...))" -- arithmetic expansion; not closing ")"
/\$(([^)][^)]*))[^)]*$/bnstcnt
# "$(...)" -- command substitution; not closing ")"
x
bnstslrp
:nstcl
-s/^/>>/
# is it "))" which closes nested and parent subshells?
/)[ ]*)/bslurp
bchkchn
# found multi-line "{...\n...}" block -- pass through untouched
:block
x
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
n
+:blkcom
+/^[ ]*#/{
+ N
+ s/.*\n//
+ bblkcom
+}
# closing "}" -- stop block slurp
/}/bchkchn
bblock
# since that line legitimately lacks "&&" and exit subshell loop
:clssolo
x
-s/?!AMP?!//
+s/\( ?!AMP?!\)* ?!AMP?!$//
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
p
x
-s/^/>/
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
b
# found closing "...)" -- exit subshell loop
:close
x
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
p
x
-s/^/>/
+s/^\([ ]*\)^/\1(/
+s/?!HERE?!/<</g
b
foo &&
bar=$((42 + 1)) &&
baz
->) &&
+) &&
(
-?!AMP?! bar=$((42 + 1))
+ bar=$((42 + 1)) ?!AMP?!
baz
->)
+)
foo &&
bar=(gumbo stumbo wumbo) &&
baz
->) &&
+) &&
(
foo &&
bar=${#bar[@]} &&
baz
->)
+)
(
nothing &&
something
->)
+)
nothing &&
something
-# LINT: swallow blank lines since final _statement_ before subshell end is
+# LINT: ignore blank lines since final _statement_ before subshell end is
# LINT: significant to "&&"-check, not final _line_ (which might be blank)
--- /dev/null
+(
+ {
+ echo a &&
+ echo b
+ }
+)
--- /dev/null
+(
+ {
+ # show a
+ echo a &&
+ # show b
+ echo b
+ }
+)
bar &&
{
echo c
-?!AMP?! }
+ } ?!AMP?!
baz
->)
+)
(
-# LINT: missing "&&" in block not currently detected (for consistency with
-# LINT: --chain-lint at top level and to provide escape hatch if needed)
+# LINT: missing "&&" after first "echo"
foo &&
{
echo a
(
foo &&
-?!AMP?! bar
+ bar ?!AMP?!
baz &&
wop
->)
+)
(
foo &&
-# LINT: missing "&&" from 'bar'
+# LINT: missing "&&" from "bar"
bar
baz &&
# LINT: final statement before closing ")" legitimately lacks "&&"
--- /dev/null
+(
+ case "$x" in
+ x) foo ;;
+ *)
+ bar
+ ;;
+ esac
+)
--- /dev/null
+(
+ case "$x" in
+ # found foo
+ x) foo ;;
+ # found other
+ *)
+ # treat it as bar
+ bar
+ ;;
+ esac
+)
*) bar ;;
esac &&
foobar
->) &&
+) &&
(
case "$x" in
x) foo ;;
*) bar ;;
-?!AMP?! esac
+ esac ?!AMP?!
foobar
->) &&
+) &&
(
case "$x" in 1) true;; esac &&
-?!AMP?! case "$y" in 2) false;; esac
+ case "$y" in 2) false;; esac ?!AMP?!
foobar
->)
+)
(
-# LINT: "...)" arms in 'case' not misinterpreted as subshell-closing ")"
+# LINT: "...)" arms in "case" not misinterpreted as subshell-closing ")"
case "$x" in
x) foo ;;
*) bar ;;
foobar
) &&
(
-# LINT: missing "&&" on 'esac'
+# LINT: missing "&&" on "esac"
case "$x" in
x) foo ;;
*) bar ;;
foobar
) &&
(
-# LINT: "...)" arm in one-liner 'case' not misinterpreted as closing ")"
+# LINT: "...)" arm in one-liner "case" not misinterpreted as closing ")"
case "$x" in 1) true;; esac &&
# LINT: same but missing "&&"
case "$y" in 2) false;; esac
-(
-cd foo &&
+(cd foo &&
(bar &&
->>> baz))
+ baz))
(
foo
->) &&
+) &&
(
bar
->) >out &&
+) >out &&
(
baz
->) 2>err &&
+) 2>err &&
(
boo
->) <input &&
+) <input &&
(
bip
->) | wuzzle &&
+) | wuzzle &&
(
bop
->) | fazz fozz &&
+) | fazz fozz &&
(
bup
->) |
+) |
fuzzle &&
(
yop
->)
+)
foo &&
bar=$(gobble) &&
baz
->) &&
+) &&
(
-?!AMP?! bar=$(gobble blocks)
+ bar=$(gobble blocks) ?!AMP?!
baz
->)
+)
(
nothing &&
something
->)
+)
-(
-for i in a b c; do
+(for i in a b c; do
if test "$(echo $(waffle bat))" = "eleventeen" &&
test "$x" = "$y"; then
:
else
echo >file
fi
-> done) &&
+ done) &&
test ! -f file
-# LINT: 'for' loop cuddled with "(" and ")" and nested 'if' with complex
+# LINT: "for" loop cuddled with "(" and ")" and nested "if" with complex
# LINT: multi-line condition; indented with spaces, not tabs
(for i in a b c; do
if test "$(echo $(waffle bat))" = "eleventeen" &&
-(
-if test -z ""; then
+(if test -z ""; then
echo empty
else
echo bizzy
-> fi) &&
+ fi) &&
echo foobar
-# LINT: 'if' cuddled with "(" and ")"; indented with spaces, not tabs
+# LINT: "if" cuddled with "(" and ")"; indented with spaces, not tabs
(if test -z ""; then
echo empty
else
-(
- while read x
+( while read x
do foobar bop || exit 1
-> done <file ) &&
+ done <file ) &&
outside subshell
-# LINT: 'while' loop cuddled with "(" and ")", with embedded (allowed)
+# LINT: "while" loop cuddled with "(" and ")", with embedded (allowed)
# LINT: "|| exit {n}" to exit loop early, and using redirection "<" to feed
# LINT: loop; indented with spaces, not tabs
( while read x
-(
-cd foo &&
+(cd foo &&
bar
->) &&
+) &&
-(
-?!AMP?!cd foo
+(cd foo ?!AMP?!
bar
->) &&
+) &&
(
cd foo &&
-> bar) &&
+ bar) &&
-(
-cd foo &&
-> bar) &&
+(cd foo &&
+ bar) &&
-(
-?!AMP?!cd foo
-> bar)
+(cd foo ?!AMP?!
+ bar)
-# LINT: first subshell statement cuddled with opening "("; for implementation
-# LINT: simplicity, "(..." is split into two lines, "(" and "..."
+# LINT: first subshell statement cuddled with opening "("
(cd foo &&
bar
) &&
bar &&
baz
done
->) &&
+) &&
(
while true
do
bar &&
baz
done
->) &&
+) &&
(
i=0 &&
while test $i -lt 10
echo $i || exit
i=$(($i + 1))
done
->)
+)
foo || exit 1
bar &&
baz
->)
+)
(
for i in a b c
do
-?!AMP?! echo $i
- cat
-?!AMP?! done
+ echo $i ?!AMP?!
+ cat <<-EOF
+ done ?!AMP?!
for i in a b c; do
echo $i &&
cat $i
done
->)
+)
(
-# LINT: 'for', 'do', 'done' do not need "&&"
+# LINT: "for", "do", "done" do not need "&&"
for i in a b c
do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo $i
# LINT: last statement of while does not need "&&"
cat <<-\EOF
bar
EOF
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
-# LINT: 'do' on same line as 'for'
+# LINT: "do" on same line as "for"
for i in a b c; do
echo $i &&
cat $i
(
-> cat)
+ cat <<-INPUT)
(
- x=$(bobble &&
-?!AMP?!>> wiffle)
+ x=$(bobble <<-END &&
+ wiffle) ?!AMP?!
echo $x
->)
+)
(
-?!AMP?! cat && echo "multi-line string"
+ cat <<-TXT && echo "multi-line string" ?!AMP?!
bap
->)
+)
-boodle wobba gorgo snoot wafta snurb &&
+boodle wobba gorgo snoot wafta snurb <<EOF &&
-cat >foo &&
+cat <<-Arbitrary_Tag_42 >foo &&
-cat >bar &&
+cat <<zump >boo &&
-cat >boo &&
-
-horticulture
+horticulture <<EOF
woz
Arbitrary_Tag_42
-# LINT: swallow 'quoted' here-doc
-cat <<'FUMP' >bar &&
-snoz
-boz
-woz
-FUMP
-
# LINT: swallow "quoted" here-doc
cat <<"zump" >boo &&
snoz
do
if false
then
-?!AMP?! echo "err"
+ echo "err" ?!AMP?!
exit 1
-?!AMP?! fi
+ fi ?!AMP?!
foo
-?!AMP?! done
+ done ?!AMP?!
bar
->)
+)
do
if false
then
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo "err"
exit 1
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
fi
foo
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
bar
)
(
if test -n ""
then
-?!AMP?! echo very
+ echo very ?!AMP?!
echo empty
elif test -z ""
+ then
echo foo
else
echo foo &&
- cat
-?!AMP?! fi
+ cat <<-EOF
+ fi ?!AMP?!
echo poodle
->) &&
+) &&
(
if test -n ""; then
echo very &&
-?!AMP?! echo empty
- if
->)
+ echo empty
+ fi
+)
(
-# LINT: 'if', 'then', 'elif', 'else', 'fi' do not need "&&"
+# LINT: "if", "then", "elif", "else", "fi" do not need "&&"
if test -n ""
then
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo very
-# LINT: last statement before 'elif' does not need "&&"
+# LINT: last statement before "elif" does not need "&&"
echo empty
elif test -z ""
-# LINT: last statement before 'else' does not need "&&"
+ then
+# LINT: last statement before "else" does not need "&&"
echo foo
else
echo foo &&
-# LINT: last statement before 'fi' does not need "&&"
+# LINT: last statement before "fi" does not need "&&"
cat <<-\EOF
bar
EOF
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
fi
echo poodle
) &&
(
-# LINT: 'then' on same line as 'if'
+# LINT: "then" on same line as "if"
if test -n ""; then
echo very &&
echo empty
- if
+ fi
)
line 1 line 2 line 3 line 4 &&
(
line 5 line 6 line 7 line 8
->)
+)
(
foobar &&
-?!AMP?! barfoo
+ barfoo ?!AMP?!
flibble "not a # comment"
->) &&
+) &&
-(
-cd foo &&
-> flibble "not a # comment")
+(cd foo &&
+ flibble "not a # comment")
then
while true
do
-?!AMP?! echo "pop"
+ echo "pop" ?!AMP?!
echo "glup"
-?!AMP?! done
+ done ?!AMP?!
foo
-?!AMP?! fi
+ fi ?!AMP?!
bar
->)
+)
then
while true
do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo "pop"
echo "glup"
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
foo
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
fi
bar
)
x=$(
echo bar |
cat
->> ) &&
+ ) &&
echo ok
->) |
+) |
sort &&
(
bar &&
x=$(echo bar |
cat
->> ) &&
+ ) &&
y=$(echo baz |
->> fip) &&
+ fip) &&
echo fail
->)
+)
(
x="line 1 line 2 line 3" &&
-?!AMP?! y='line 1 line2'
+ y="line 1 line2" ?!AMP?!
foobar
->) &&
-(
- echo "there's nothing to see here" &&
- exit
->) &&
+) &&
(
echo "xyz" "abc def ghi" &&
- echo 'xyz' 'abc def ghi' &&
- echo 'xyz' "abc def ghi" &&
barfoo
->)
+)
line 2
line 3" &&
# LINT: missing "&&" on assignment
- y='line 1
- line2'
+ y="line 1
+ line2"
foobar
) &&
-(
-# LINT: apostrophe (in a contraction) within string not misinterpreted as
-# LINT: starting multi-line single-quoted string
- echo "there's nothing to see here" &&
- exit
-) &&
(
echo "xyz" "abc
def
ghi" &&
- echo 'xyz' 'abc
- def
- ghi' &&
- echo 'xyz' "abc
- def
- ghi" &&
barfoo
)
! (foo && bar) &&
! (foo && bar) >baz &&
-?!SEMI?!! (foo; bar) &&
-?!SEMI?!! (foo; bar) >baz
+! (foo; ?!AMP?! bar) &&
+! (foo; ?!AMP?! bar) >baz
(
(cd foo &&
bar
->> ) &&
+ ) &&
(cd foo &&
bar
-?!AMP?!>> )
+ ) ?!AMP?!
(
cd foo &&
->> bar) &&
+ bar) &&
(
cd foo &&
-?!AMP?!>> bar)
+ bar) ?!AMP?!
(cd foo &&
->> bar) &&
+ bar) &&
(cd foo &&
-?!AMP?!>> bar)
+ bar) ?!AMP?!
foobar
->)
+)
-cat >foop &&
+cat <<ARBITRARY >foop &&
(
- cat &&
-?!AMP?! cat
+ cat <<-INPUT_END &&
+ cat <<-EOT ?!AMP?!
foobar
->)
+)
foo &&
(
bar &&
- # bottles wobble while fiddles gobble
- # minor numbers of cows (or do they?)
baz &&
snaff
-?!AMP?!>> )
+ ) ?!AMP?!
fuzzy
->)
+)
# minor numbers of cows (or do they?)
baz &&
snaff
-# LINT: missing "&&" on ')'
+# LINT: missing "&&" on ")"
)
fuzzy
)
(
echo a &&
echo b
->> ) >file &&
+ ) >file &&
cd foo &&
(
echo a
echo b
->> ) >file
->)
+ ) >file
+)
cd foo &&
(
-# LINT: nested multi-line subshell not presently checked for missing "&&"
echo a
echo b
) >file
--- /dev/null
+echo "<<<<<<< ours" &&
+echo ourside &&
+echo "=======" &&
+echo theirside &&
+echo ">>>>>>> theirs" &&
+
+(
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
+ echo ">>>>>>> theirs" ?!AMP?!
+ poodle
+) >merged
--- /dev/null
+# LINT: "<< ours" inside string is not here-doc
+echo "<<<<<<< ours" &&
+echo ourside &&
+echo "=======" &&
+echo theirside &&
+echo ">>>>>>> theirs" &&
+
+(
+# LINT: "<< ours" inside string is not here-doc
+ echo "<<<<<<< ours" &&
+ echo ourside &&
+ echo "=======" &&
+ echo theirside &&
+ echo ">>>>>>> theirs"
+ poodle
+) >merged
(foo && bar) |
(foo && bar) >baz &&
-?!SEMI?!(foo; bar) &&
-?!SEMI?!(foo; bar) |
-?!SEMI?!(foo; bar) >baz
+(foo; ?!AMP?! bar) &&
+(foo; ?!AMP?! bar) |
+(foo; ?!AMP?! bar) >baz &&
(foo "bar; baz")
(foo && bar) |
(foo && bar) >baz &&
-# LINT: top-level one-liner subshell missing internal "&&"
+# LINT: top-level one-liner subshell missing internal "&&" and broken &&-chain
(foo; bar) &&
(foo; bar) |
-(foo; bar) >baz
+(foo; bar) >baz &&
# LINT: ";" in string not misinterpreted as broken &&-chain
(foo "bar; baz")
(
p4 print -1 //depot/fiddle#42 >file &&
foobar
->)
+)
bar |
baz &&
fish |
-?!AMP?! cow
+ cow ?!AMP?!
sunder
->)
+)
bar |
baz &&
-# LINT: final line of pipe sequence ('cow') lacking "&&"
+# LINT: final line of pipe sequence ("cow") lacking "&&"
fish |
cow
(
-?!AMP?!?!SEMI?! cat foo ; echo bar
-?!SEMI?! cat foo ; echo bar
->) &&
+ cat foo ; ?!AMP?! echo bar ?!AMP?!
+ cat foo ; ?!AMP?! echo bar
+) &&
(
-?!SEMI?! cat foo ; echo bar &&
-?!SEMI?! cat foo ; echo bar
->) &&
+ cat foo ; ?!AMP?! echo bar &&
+ cat foo ; ?!AMP?! echo bar
+) &&
(
echo "foo; bar" &&
-?!SEMI?! cat foo; echo bar
->) &&
+ cat foo; ?!AMP?! echo bar
+) &&
(
-?!SEMI?! foo;
->) &&
-(
-cd foo &&
+ foo;
+) &&
+(cd foo &&
for i in a b c; do
-?!SEMI?! echo;
-> done)
+ echo;
+ done)
cat foo; echo bar
) &&
(
-# LINT: unnecessary terminating semicolon
+# LINT: semicolon unnecessary but legitimate
foo;
) &&
(cd foo &&
for i in a b c; do
-# LINT: unnecessary terminating semicolon
+# LINT: semicolon unnecessary but legitimate
echo;
done)
(
- echo wobba gorgo snoot wafta snurb &&
-?!AMP?! cat >bip
- echo >bop
->) &&
+ echo wobba gorgo snoot wafta snurb <<-EOF &&
+ cat <<EOF >bip ?!AMP?!
+ echo <<-EOF >bop
+) &&
(
- cat >bup &&
- cat >bup2 &&
- cat >bup3 &&
+ cat <<-ARBITRARY >bup &&
+ cat <<-ARBITRARY3 >bup3 &&
meep
->)
+)
nevermore...
EOF
-# LINT: missing "&&" on 'cat'
+# LINT: missing "&&" on "cat"
cat <<EOF >bip
fish fly high
- EOF
+EOF
# LINT: swallow here-doc (EOF is last line of subshell)
echo <<-\EOF >bop
glink
FIZZ
ARBITRARY
- cat <<-'ARBITRARY2' >bup2 &&
- glink
- FIZZ
- ARBITRARY2
cat <<-"ARBITRARY3" >bup3 &&
glink
FIZZ
(foo && bar) &&
(foo && bar) |
(foo && bar) >baz &&
-?!SEMI?! (foo; bar) &&
-?!SEMI?! (foo; bar) |
-?!SEMI?! (foo; bar) >baz &&
+ (foo; ?!AMP?! bar) &&
+ (foo; ?!AMP?! bar) |
+ (foo; ?!AMP?! bar) >baz &&
(foo || exit 1) &&
(foo || exit 1) |
(foo || exit 1) >baz &&
-?!AMP?! (foo && bar)
-?!AMP?!?!SEMI?! (foo && bar; baz)
+ (foo && bar) ?!AMP?!
+ (foo && bar; ?!AMP?! baz) ?!AMP?!
foobar
->)
+)
(
chks="sub1sub2sub3sub4" &&
- chks_sub=$(cat | sed 's,^,sub dir/,'
->>) &&
+ chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+) &&
chkms="main-sub1main-sub2main-sub3main-sub4" &&
- chkms_sub=$(cat | sed 's,^,sub dir/,'
->>) &&
+ chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+) &&
subfiles=$(git ls-files) &&
check_equal "$subfiles" "$chkms$chks"
->)
+)
sub2
sub3
sub4" &&
- chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+ chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
$chks
TXT
) &&
main-sub2
main-sub3
main-sub4" &&
- chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+ chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
$chkms
TXT
) &&
(
while true
do
-?!AMP?! echo foo
- cat
-?!AMP?! done
+ echo foo ?!AMP?!
+ cat <<-EOF
+ done ?!AMP?!
while true; do
echo foo &&
cat bar
done
->)
+)
(
-# LINT: 'while, 'do', 'done' do not need "&&"
+# LINT: "while", "do", "done" do not need "&&"
while true
do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
echo foo
# LINT: last statement of while does not need "&&"
cat <<-\EOF
bar
EOF
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
done
-# LINT: 'do' on same line as 'while'
+# LINT: "do" on same line as "while"
while true; do
echo foo &&
cat bar
#include "test-tool.h"
#include "cache.h"
#include "config.h"
-#include "blob.h"
-#include "commit.h"
-#include "tree.h"
-#include "sparse-index.h"
-
-static void print_cache_entry(struct cache_entry *ce)
-{
- const char *type;
- printf("%06o ", ce->ce_mode & 0177777);
-
- if (S_ISSPARSEDIR(ce->ce_mode))
- type = tree_type;
- else if (S_ISGITLINK(ce->ce_mode))
- type = commit_type;
- else
- type = blob_type;
-
- printf("%s %s\t%s\n",
- type,
- oid_to_hex(&ce->oid),
- ce->name);
-}
-
-static void print_cache(struct index_state *istate)
-{
- int i;
- for (i = 0; i < istate->cache_nr; i++)
- print_cache_entry(istate->cache[i]);
-}
int cmd__read_cache(int argc, const char **argv)
{
- struct repository *r = the_repository;
int i, cnt = 1;
const char *name = NULL;
- int table = 0, expand = 0;
initialize_the_repository();
- for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
- if (skip_prefix(*argv, "--print-and-refresh=", &name))
- continue;
- if (!strcmp(*argv, "--table"))
- table = 1;
- else if (!strcmp(*argv, "--expand"))
- expand = 1;
+ if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
+ argc--;
+ argv++;
}
- if (argc == 1)
- cnt = strtol(argv[0], NULL, 0);
+ if (argc == 2)
+ cnt = strtol(argv[1], NULL, 0);
setup_git_directory();
git_config(git_default_config, NULL);
- prepare_repo_settings(r);
- r->settings.command_requires_full_index = 0;
-
for (i = 0; i < cnt; i++) {
- repo_read_index(r);
-
- if (expand)
- ensure_full_index(r->index);
-
+ read_cache();
if (name) {
int pos;
- refresh_index(r->index, REFRESH_QUIET,
+ refresh_index(&the_index, REFRESH_QUIET,
NULL, NULL, NULL);
- pos = index_name_pos(r->index, name, strlen(name));
+ pos = index_name_pos(&the_index, name, strlen(name));
if (pos < 0)
die("%s not in index", name);
printf("%s is%s up to date\n", name,
- ce_uptodate(r->index->cache[pos]) ? "" : " not");
+ ce_uptodate(the_index.cache[pos]) ? "" : " not");
write_file(name, "%d\n", i);
}
- if (table)
- print_cache(r->index);
- discard_index(r->index);
+ discard_cache();
}
return 0;
}
struct object_id old_oid;
if (get_oid_hex(sha1_buf, &old_oid))
- die("not sha-1");
+ die("cannot parse %s as %s", sha1_buf, the_hash_algo->name);
return refs_delete_ref(refs, msg, refname, &old_oid, flags);
}
struct object_id old_oid;
struct object_id new_oid;
- if (get_oid_hex(old_sha1_buf, &old_oid) ||
- get_oid_hex(new_sha1_buf, &new_oid))
- die("not sha-1");
+ if (get_oid_hex(old_sha1_buf, &old_oid))
+ die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+ if (get_oid_hex(new_sha1_buf, &new_oid))
+ die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name);
return refs_update_ref(refs, msg, refname,
&new_oid, &old_oid,
* [] the "cmd_name" event has been generated.
* [] this writes various "def_param" events for interesting config values.
*
- * We further assume that if we return (rather than exit()), trace2_cmd_exit()
- * will be called by test-tool.c:cmd_main().
+ * We return from here and let test-tool.c::cmd_main() pass the exit
+ * code to common-main.c::main(), which will use it to call
+ * trace2_cmd_exit().
*/
int cmd__trace2(int argc, const char **argv)
{
test_expect_success 'determine default pager' '
test_might_fail git config --unset core.pager &&
less=$(
- unset PAGER GIT_PAGER;
+ sane_unset PAGER GIT_PAGER &&
git var GIT_PAGER
) &&
test -n "$less"
test_expect_success "setup repo" '
if git rev-parse --verify refs/heads/p0006-ballast^{commit}
then
- echo Assuming synthetic repo from many-files.sh
- git branch br_base master
- git branch br_ballast p0006-ballast
- git config --local core.sparsecheckout 1
+ echo Assuming synthetic repo from many-files.sh &&
+ git branch br_base master &&
+ git branch br_ballast p0006-ballast &&
+ git config --local core.sparsecheckout 1 &&
cat >.git/info/sparse-checkout <<-EOF
/*
!ballast/*
EOF
else
- echo Assuming non-synthetic repo...
- git branch br_base $(git rev-list HEAD | tail -n 1)
+ echo Assuming non-synthetic repo... &&
+ git branch br_base $(git rev-list HEAD | tail -n 1) &&
git branch br_ballast HEAD
fi &&
git checkout -q br_ballast &&
test_expect_success "setup repo" '
if git rev-parse --verify refs/heads/p0006-ballast^{commit}
then
- echo Assuming synthetic repo from many-files.sh
- git branch br_base master
- git branch br_ballast p0006-ballast^
- git branch br_ballast_alias p0006-ballast^
- git branch br_ballast_plus_1 p0006-ballast
- git config --local core.sparsecheckout 1
+ echo Assuming synthetic repo from many-files.sh &&
+ git branch br_base master &&
+ git branch br_ballast p0006-ballast^ &&
+ git branch br_ballast_alias p0006-ballast^ &&
+ git branch br_ballast_plus_1 p0006-ballast &&
+ git config --local core.sparsecheckout 1 &&
cat >.git/info/sparse-checkout <<-EOF
/*
!ballast/*
EOF
else
- echo Assuming non-synthetic repo...
- git branch br_base $(git rev-list HEAD | tail -n 1)
- git branch br_ballast HEAD^ || error "no ancestor commit from current head"
- git branch br_ballast_alias HEAD^
+ echo Assuming non-synthetic repo... &&
+ git branch br_base $(git rev-list HEAD | tail -n 1) &&
+ git branch br_ballast HEAD^ || error "no ancestor commit from current head" &&
+ git branch br_ballast_alias HEAD^ &&
git branch br_ballast_plus_1 HEAD
fi &&
git checkout -q br_ballast &&
test_expect_success "setup repo" '
if git rev-parse --verify refs/heads/p0006-ballast^{commit}
then
- echo Assuming synthetic repo from many-files.sh
- git config --local core.sparsecheckout 1
+ echo Assuming synthetic repo from many-files.sh &&
+ git config --local core.sparsecheckout 1 &&
cat >.git/info/sparse-checkout <<-EOF
/*
!ballast/*
printf "a" >>refname &&
for j in $(test_seq 1 $i)
do
- printf "a*" >>refglob.$i
+ printf "a*" >>refglob.$i || return 1
done &&
- echo b >>refglob.$i
+ echo b >>refglob.$i || return 1
done &&
test_commit test $(cat refname).t "" $(cat refname).t
'
do
printf "start\ncreate refs/heads/%d PRE\ncommit\n" $i &&
printf "start\nupdate refs/heads/%d POST PRE\ncommit\n" $i &&
- printf "start\ndelete refs/heads/%d POST\ncommit\n" $i
+ printf "start\ndelete refs/heads/%d POST\ncommit\n" $i || return 1
done >instructions
'
do
git update-ref refs/heads/branch PRE &&
git update-ref refs/heads/branch POST PRE &&
- git update-ref -d refs/heads/branch
+ git update-ref -d refs/heads/branch || return 1
done
'
echo "committer C <c@example.com> 1234567890 +0000" &&
echo "data <<EOF" &&
echo "$i.Q." &&
- echo "EOF"
+ echo "EOF" || return 1
done | q_to_nul | git fast-import
'
git add unrelated-file$i &&
test_tick &&
git commit -m commit$i-reverse unrelated-file$i ||
- break
+ return 1
done &&
git checkout to-rebase &&
test_commit our-patch interesting-file
--- /dev/null
+#!/bin/sh
+
+test_description='Tests diff --color-moved performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# The endpoints of the diff can be customized by setting TEST_REV_A
+# and TEST_REV_B in the environment when running this test.
+
+rev="${TEST_REV_A:-v2.28.0}"
+if ! rev_a="$(git rev-parse --quiet --verify "$rev")"
+then
+ skip_all="skipping because '$rev' was not found. \
+ Use TEST_REV_A and TEST_REV_B to set the revs to use"
+ test_done
+fi
+rev="${TEST_REV_B:-v2.29.0}"
+if ! rev_b="$(git rev-parse --quiet --verify "$rev")"
+then
+ skip_all="skipping because '$rev' was not found. \
+ Use TEST_REV_A and TEST_REV_B to set the revs to use"
+ test_done
+fi
+
+GIT_PAGER_IN_USE=1
+test_export GIT_PAGER_IN_USE rev_a rev_b
+
+test_perf 'diff --no-color-moved --no-color-moved-ws large change' '
+ git diff --no-color-moved --no-color-moved-ws $rev_a $rev_b
+'
+
+test_perf 'diff --color-moved --no-color-moved-ws large change' '
+ git diff --color-moved=zebra --no-color-moved-ws $rev_a $rev_b
+'
+
+test_perf 'diff --color-moved-ws=allow-indentation-change large change' '
+ git diff --color-moved=zebra --color-moved-ws=allow-indentation-change \
+ $rev_a $rev_b
+'
+
+test_perf 'log --no-color-moved --no-color-moved-ws' '
+ git log --no-color-moved --no-color-moved-ws --no-merges --patch \
+ -n1000 $rev_b
+'
+
+test_perf 'log --color-moved --no-color-moved-ws' '
+ git log --color-moved=zebra --no-color-moved-ws --no-merges --patch \
+ -n1000 $rev_b
+'
+
+test_perf 'log --color-moved-ws=allow-indentation-change' '
+ git log --color-moved=zebra --color-moved-ws=allow-indentation-change \
+ --no-merges --patch -n1000 $rev_b
+'
+
+test_done
threads= &&
while test $t -gt 0
do
- threads="$t $threads"
- t=$((t / 2))
+ threads="$t $threads" &&
+ t=$((t / 2)) || return 1
done
'
# Measure pack loading with 10,000 packs.
test_expect_success 'generate lots of packs' '
for i in $(test_seq 10000); do
- echo "blob"
- echo "data <<EOF"
- echo "blob $i"
- echo "EOF"
- echo "checkpoint"
+ echo "blob" &&
+ echo "data <<EOF" &&
+ echo "blob $i" &&
+ echo "EOF" &&
+ echo "checkpoint" || return 1
done |
git -c fastimport.unpackLimit=0 fast-import
'
fi &&
mkdir 1_file 10_files 100_files 1000_files 10000_files &&
- for i in $(test_seq 1 10); do touch 10_files/$i; done &&
- for i in $(test_seq 1 100); do touch 100_files/$i; done &&
- for i in $(test_seq 1 1000); do touch 1000_files/$i; done &&
- for i in $(test_seq 1 10000); do touch 10000_files/$i; done &&
+ for i in $(test_seq 1 10); do touch 10_files/$i || return 1; done &&
+ for i in $(test_seq 1 100); do touch 100_files/$i || return 1; done &&
+ for i in $(test_seq 1 1000); do touch 1000_files/$i || return 1; done &&
+ for i in $(test_seq 1 10000); do touch 10000_files/$i || return 1; done &&
git add 1_file 10_files 100_files 1000_files 10000_files &&
git commit -qm "Add files" &&
test_cleanup=:
test_export_="test_cleanup"
export test_cleanup test_export_
- "$GTIME" -f "%E %U %S" -o test_time.$i "$SHELL" -c '
+ "$GTIME" -f "%E %U %S" -o test_time.$i "$TEST_SHELL_PATH" -c '
. '"$TEST_DIRECTORY"/test-lib-functions.sh'
test_export () {
test_export_="$test_export_ $*"
'
test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
- OUT=$( ((trap "" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'
do
: >$dir/not-ignored &&
: >$dir/ignored-and-untracked &&
- : >$dir/ignored-but-in-index
+ : >$dir/ignored-but-in-index || return 1
done &&
git add -f ignored-but-in-index a/ignored-but-in-index &&
cat <<-\EOF >a/.gitignore &&
for n in $(test_seq 51)
do
echo put key$n value$n >> in &&
- echo NULL >> expect
+ echo NULL >> expect || return 1
done &&
echo size >> in &&
echo 64 51 >> expect &&
for n in $(test_seq 12)
do
echo remove key$n >> in &&
- echo value$n >> expect
+ echo value$n >> expect || return 1
done &&
echo size >> in &&
echo 256 40 >> expect &&
git config core.autocrlf false &&
- for w in Hello world how are you; do echo $w; done >one &&
+ test_write_lines Hello world how are you >one &&
mkdir dir &&
- for w in I am very very fine thank you; do echo $w; done >dir/two &&
- for w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three &&
+ test_write_lines I am very very fine thank you >dir/two &&
+ test_write_lines Oh here is NULQin text here | q_to_nul >three &&
git add . &&
git commit -m initial &&
two=$(git rev-parse HEAD:dir/two) &&
three=$(git rev-parse HEAD:three) &&
- for w in Some extra lines here; do echo $w; done >>one &&
+ test_write_lines Some extra lines here >>one &&
git diff >patch.file &&
patched=$(git hash-object --stdin <one) &&
git read-tree --reset -u HEAD
git config core.autocrlf input &&
git config core.safecrlf true &&
- for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ test_write_lines I am all CRLF | append_cr >allcrlf &&
test_must_fail git add allcrlf
'
git config core.autocrlf input &&
git config core.safecrlf true &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
test_must_fail git add mixed
'
git config core.autocrlf true &&
git config core.safecrlf true &&
- for w in I am all LF; do echo $w; done >alllf &&
+ test_write_lines I am all LF >alllf &&
test_must_fail git add alllf
'
git config core.autocrlf true &&
git config core.safecrlf true &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
test_must_fail git add mixed
'
git config core.autocrlf input &&
git config core.safecrlf warn &&
- for w in I am all LF; do echo $w; done >doublewarn &&
+ test_write_lines I am all LF >doublewarn &&
git add doublewarn &&
git commit -m "nowarn" &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >doublewarn &&
git add doublewarn 2>err &&
grep "CRLF will be replaced by LF" err >err.warnings &&
test_line_count = 1 err.warnings
git config core.autocrlf input &&
git config core.safecrlf false &&
- for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ test_write_lines I am all CRLF | append_cr >allcrlf &&
git add allcrlf 2>err &&
test_must_be_empty err
'
git config core.autocrlf false &&
git config core.safecrlf false &&
rm -rf .????* * &&
- for w in I am all LF; do echo $w; done >alllf &&
- for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
- for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ test_write_lines I am all LF >alllf &&
+ test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
+ test_write_lines I am all CRLF | append_cr >allcrlf &&
git add -A . &&
git commit -m "alllf, allcrlf and mixed only" &&
git tag -a -m "message" autocrlf-checkpoint
git config filter.rot13.clean ./rot13.sh &&
{
- echo "*.t filter=rot13"
+ echo "*.t filter=rot13" &&
echo "*.i ident"
} >.gitattributes &&
{
- echo a b c d e f g h i j k l m
- echo n o p q r s t u v w x y z
+ echo a b c d e f g h i j k l m &&
+ echo n o p q r s t u v w x y z &&
echo '\''$Id$'\''
} >test &&
cat test >test.t &&
# If an expanded ident ever gets into the repository, we want to make sure that
# it is collapsed before being expanded again on checkout
test_expect_success expanded_in_repo '
- {
- echo "File with expanded keywords"
- echo "\$Id\$"
- echo "\$Id:\$"
- echo "\$Id: 0000000000000000000000000000000000000000 \$"
- echo "\$Id: NoSpaceAtEnd\$"
- echo "\$Id:NoSpaceAtFront \$"
- echo "\$Id:NoSpaceAtEitherEnd\$"
- echo "\$Id: NoTerminatingSymbol"
- echo "\$Id: Foreign Commit With Spaces \$"
- } >expanded-keywords.0 &&
+ cat >expanded-keywords.0 <<-\EOF &&
+ File with expanded keywords
+ $Id$
+ $Id:$
+ $Id: 0000000000000000000000000000000000000000 $
+ $Id: NoSpaceAtEnd$
+ $Id:NoSpaceAtFront $
+ $Id:NoSpaceAtEitherEnd$
+ $Id: NoTerminatingSymbol
+ $Id: Foreign Commit With Spaces $
+ EOF
{
cat expanded-keywords.0 &&
git commit -m "File with keywords expanded" &&
id=$(git rev-parse --verify :expanded-keywords) &&
- {
- echo "File with expanded keywords"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: $id \$"
- echo "\$Id: NoTerminatingSymbol"
- echo "\$Id: Foreign Commit With Spaces \$"
- } >expected-output.0 &&
+ cat >expected-output.0 <<-EOF &&
+ File with expanded keywords
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: $id \$
+ \$Id: NoTerminatingSymbol
+ \$Id: Foreign Commit With Spaces \$
+ EOF
{
cat expected-output.0 &&
printf "\$Id: NoTerminatingSymbolAtEOF"
printf "\$Id: NoTerminatingSymbolAtEOF"
} >expected-output-crlf &&
{
- echo "expanded-keywords ident"
+ echo "expanded-keywords ident" &&
echo "expanded-keywords-crlf ident text eol=crlf"
} >>.gitattributes &&
test_expect_success 'filtering large input to small output should use little memory' '
test_config filter.devnull.clean "cat >/dev/null" &&
test_config filter.devnull.required true &&
- for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+ for i in $(test_seq 1 30); do printf "%1048576d" 1 || return 1; done >30MB &&
echo "30MB filter=devnull" >.gitattributes &&
GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
'
test_expect_success EXPENSIVE 'filter large file' '
test_config filter.largefile.smudge cat &&
test_config filter.largefile.clean cat &&
- for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
+ for i in $(test_seq 1 2048); do printf "%1048576d" 1 || return 1; done >2GB &&
echo "2GB filter=largefile" >.gitattributes &&
git add 2GB 2>err &&
test_must_be_empty err &&
for FILE in "$TEST_ROOT"/*.file
do
cp "$FILE" . &&
- rot13.sh <"$FILE" >"$FILE.rot13"
+ rot13.sh <"$FILE" >"$FILE.rot13" || return 1
done &&
echo "*.file filter=protocol" >.gitattributes &&
for FILE in *.file
do
- test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE
+ test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE || return 1
done
)
'
echo "one text" > .gitattributes &&
- for w in Hello world how are you; do echo $w; done >one &&
- for w in I am very very fine thank you; do echo $w; done >two &&
+ test_write_lines Hello world how are you >one &&
+ test_write_lines I am very very fine thank you >two &&
git add . &&
git commit -m initial &&
mkdir second &&
ln -s ../first second/other &&
mkdir third &&
- dir="$(cd .git; pwd -P)" &&
+ dir="$(cd .git && pwd -P)" &&
dir2=third/../second/other/.git &&
test "$dir" = "$(test-tool path-utils real_path $dir2)" &&
file="$dir"/index &&
basename=blub &&
test "$dir/$basename" = "$(cd .git && test-tool path-utils real_path "$basename")" &&
ln -s ../first/file .git/syml &&
- sym="$(cd first; pwd -P)"/file &&
+ sym="$(cd first && pwd -P)"/file &&
test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
'
EOF
{
echoid insert 444 1 2 3 4 5 a b c d e &&
- echoid contains 44 441 440 444 4440 4444
+ echoid contains 44 441 440 444 4440 4444 &&
echo clear
} | test-tool oidtree >actual &&
test_cmp expect actual
test_expect_success 'oidtree each' '
echoid "" 123 321 321 >expect &&
{
- echoid insert f 9 8 123 321 a b c d e
- echo each 12300
- echo each 3211
- echo each 3210
- echo each 32100
+ echoid insert f 9 8 123 321 a b c d e &&
+ echo each 12300 &&
+ echo each 3211 &&
+ echo each 3210 &&
+ echo each 32100 &&
echo clear
} | test-tool oidtree >actual &&
test_cmp expect actual
mkdir smallDir &&
for i in $(test_seq 0 9)
do
- echo $i >smallDir/$i
+ echo $i >smallDir/$i || return 1
done &&
git add smallDir &&
git commit -m "commit with 10 changes" &&
mkdir bigDir &&
for i in $(test_seq 0 511)
do
- echo $i >bigDir/$i
+ echo $i >bigDir/$i || return 1
done &&
git add bigDir &&
git commit -m "commit with 513 changes" &&
git -C repo rev-list --ignore-missing --objects \
--exclude-promisor-objects "$OBJ" &&
git -C repo rev-list --ignore-missing --objects-edge-aggressive \
- --exclude-promisor-objects "$OBJ"
+ --exclude-promisor-objects "$OBJ" || return 1
done
'
test_expect_success "--batch-check for a non-existent named object" '
test "foobar42 missing
foobar84 missing" = \
- "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)"
+ "$( ( echo foobar42 && echo_without_newline foobar84 ) | git cat-file --batch-check)"
'
test_expect_success "--batch-check for a non-existent hash" '
test "0000000000000000000000000000000000000042 missing
0000000000000000000000000000000000000084 missing" = \
- "$( ( echo 0000000000000000000000000000000000000042;
- echo_without_newline 0000000000000000000000000000000000000084; ) |
+ "$( ( echo 0000000000000000000000000000000000000042 &&
+ echo_without_newline 0000000000000000000000000000000000000084 ) |
git cat-file --batch-check)"
'
test "$tag_sha1 tag $tag_size
$tag_content
0000000000000000000000000000000000000000 missing" = \
- "$( ( echo $tag_sha1;
- echo_without_newline 0000000000000000000000000000000000000000; ) |
+ "$( ( echo $tag_sha1 &&
+ echo_without_newline 0000000000000000000000000000000000000000 ) |
git cat-file --batch)"
'
test_expect_success 'setup blobs which are likely to delta' '
test-tool genrandom foo 10240 >foo &&
- { cat foo; echo plus; } >foo-plus &&
+ { cat foo && echo plus; } >foo-plus &&
git add foo foo-plus &&
git commit -m foo &&
cat >blobs <<-\EOF
. ./test-lib.sh
test_expect_success setup '
- for d in a a. a0
+ for d in a a- a0
do
mkdir "$d" && echo "$d/one" >"$d/one" &&
- git add "$d"
+ git add "$d" || return 1
done &&
echo zero >one &&
git update-index --add --info-only one &&
test_expect_success setup '
long="a b c d e f g h i j k l m n o p q r s t u v w x y z" &&
- for c in $long; do echo $c; done >one &&
+ test_write_lines $long >one &&
mkdir dir &&
- for c in x y z $long a b c; do echo $c; done >dir/two &&
+ test_write_lines x y z $long a b c >dir/two &&
cp one original.one &&
cp dir/two original.two
'
git update-index --add one &&
case "$(git ls-files)" in
one) echo pass one ;;
- *) echo bad one; exit 1 ;;
+ *) echo bad one; return 1 ;;
esac &&
(
cd dir &&
) &&
case "$(git ls-files)" in
dir/two"$LF"one) echo pass both ;;
- *) echo bad; exit 1 ;;
+ *) echo bad; return 1 ;;
esac
'
echo d >>dir/two &&
case "$(git diff-files --name-only)" in
dir/two"$LF"one) echo pass top ;;
- *) echo bad top; exit 1 ;;
+ *) echo bad top; return 1 ;;
esac &&
# diff should not omit leading paths
(
test_expect_success 'add a large file or two' '
git add large1 huge large2 &&
# make sure we got a single packfile and no loose objects
- bad= count=0 idx= &&
+ count=0 idx= &&
for p in .git/objects/pack/pack-*.pack
do
- count=$(( $count + 1 ))
- if test_path_is_file "$p" &&
- idx=${p%.pack}.idx && test_path_is_file "$idx"
- then
- continue
- fi
- bad=t
+ count=$(( $count + 1 )) &&
+ test_path_is_file "$p" &&
+ idx=${p%.pack}.idx &&
+ test_path_is_file "$idx" || return 1
done &&
- test -z "$bad" &&
test $count = 1 &&
cnt=$(git show-index <"$idx" | wc -l) &&
test $cnt = 2 &&
for l in .git/objects/$OIDPATH_REGEX
do
- test_path_is_file "$l" || continue
- bad=t
+ test_path_is_missing "$l" || return 1
done &&
- test -z "$bad" &&
# attempt to add another copy of the same
git add large3 &&
bad= count=0 &&
for p in .git/objects/pack/pack-*.pack
do
- count=$(( $count + 1 ))
- if test_path_is_file "$p" &&
- idx=${p%.pack}.idx && test_path_is_file "$idx"
- then
- continue
- fi
- bad=t
+ count=$(( $count + 1 )) &&
+ test_path_is_file "$p" &&
+ idx=${p%.pack}.idx &&
+ test_path_is_file "$idx" || return 1
done &&
- test -z "$bad" &&
test $count = 1
'
count=0 &&
for pi in .git/objects/pack/pack-*.idx
do
- test_path_is_file "$pi" && count=$(( $count + 1 ))
+ test_path_is_file "$pi" && count=$(( $count + 1 )) || return 1
done &&
test $count = 2 &&
for pi in .git/objects/pack/pack-*.idx
do
- git show-index <"$pi"
+ git show-index <"$pi" || return 1
done |
sed -e "s/^[0-9]* \([0-9a-f]*\) .*/\1/" |
sort >actual &&
)
'
-test_expect_success 'git sparse-checkout list (empty)' '
+test_expect_success 'git sparse-checkout list (not sparse)' '
+ test_must_fail git -C repo sparse-checkout list >list 2>err &&
+ test_must_be_empty list &&
+ test_i18ngrep "this worktree is not sparse" err
+'
+
+test_expect_success 'git sparse-checkout list (not sparse)' '
+ git -C repo sparse-checkout set &&
+ rm repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout list >list 2>err &&
test_must_be_empty list &&
test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
check_files clone a
'
+test_expect_success 'switching to cone mode with non-cone mode patterns' '
+ git init bad-patterns &&
+ (
+ cd bad-patterns &&
+ git sparse-checkout init &&
+ git sparse-checkout add dir &&
+ git config core.sparseCheckoutCone true &&
+ test_must_fail git sparse-checkout add dir 2>err &&
+ grep "existing sparse-checkout patterns do not use cone mode" err
+ )
+'
+
test_expect_success 'interaction with clone --no-checkout (unborn index)' '
git clone --no-checkout "file://$(pwd)/repo" clone_no_checkout &&
git -C clone_no_checkout sparse-checkout init --cone &&
'
test_expect_success 'add to sparse-checkout' '
- cat repo/.git/info/sparse-checkout >expect &&
+ cat repo/.git/info/sparse-checkout >old &&
+ test_when_finished cp old repo/.git/info/sparse-checkout &&
cat >add <<-\EOF &&
pattern1
/folder1/
pattern2
EOF
+ cat old >expect &&
cat add >>expect &&
git -C repo sparse-checkout add --stdin <add &&
git -C repo sparse-checkout list >actual &&
git -C repo sparse-checkout init --cone --sparse-index &&
test_cmp_config -C repo true index.sparse &&
- test-tool -C repo read-cache --table >cache &&
- grep " tree " cache &&
-
+ git -C repo ls-files --sparse >sparse &&
git -C repo sparse-checkout disable &&
- test-tool -C repo read-cache --table >cache &&
- ! grep " tree " cache &&
+ git -C repo ls-files --sparse >full &&
+
+ cat >expect <<-\EOF &&
+ @@ -1,4 +1,7 @@
+ a
+ -deep/
+ -folder1/
+ -folder2/
+ +deep/a
+ +deep/deeper1/a
+ +deep/deeper1/deepest/a
+ +deep/deeper2/a
+ +folder1/a
+ +folder2/a
+ EOF
+
+ diff -u sparse full | tail -n +3 >actual &&
+ test_cmp expect actual &&
+
git -C repo config --list >config &&
! grep index.sparse config
)
!/*/
something$c-else/
EOF
- check_read_tree_errors repo "a" "disabling cone pattern matching"
+ check_read_tree_errors repo "a" "disabling cone pattern matching" || return 1
done
'
test_cmp expect out
'
+test_expect_success 'malformed cone-mode patterns' '
+ git -C repo sparse-checkout init --cone &&
+ mkdir -p repo/foo/bar &&
+ touch repo/foo/bar/x repo/foo/y &&
+ cat >repo/.git/info/sparse-checkout <<-\EOF &&
+ /*
+ !/*/
+ /foo/
+ !/foo/*/
+ /foo/\*/
+ EOF
+
+ # Listing the patterns will notice the duplicate pattern and
+ # emit a warning. It will list the patterns directly instead
+ # of using the cone-mode translation to a set of directories.
+ git -C repo sparse-checkout list >actual 2>err &&
+ test_cmp repo/.git/info/sparse-checkout actual &&
+ grep "warning: your sparse-checkout file may have issues: pattern .* is repeated" err &&
+ grep "warning: disabling cone pattern matching" err
+'
+
test_done
test_expect_success 'sparse-index contents' '
init_repos &&
- test-tool -C sparse-index read-cache --table >cache &&
+ git -C sparse-index ls-files --sparse --stage >cache &&
for dir in folder1 folder2 x
do
TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 tree $TREE $dir/" cache \
+ grep "040000 $TREE 0 $dir/" cache \
|| return 1
done &&
git -C sparse-index sparse-checkout set folder1 &&
- test-tool -C sparse-index read-cache --table >cache &&
+ git -C sparse-index ls-files --sparse --stage >cache &&
for dir in deep folder2 x
do
TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 tree $TREE $dir/" cache \
+ grep "040000 $TREE 0 $dir/" cache \
|| return 1
done &&
git -C sparse-index sparse-checkout set deep/deeper1 &&
- test-tool -C sparse-index read-cache --table >cache &&
+ git -C sparse-index ls-files --sparse --stage >cache &&
for dir in deep/deeper2 folder1 folder2 x
do
TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
- grep "040000 tree $TREE $dir/" cache \
+ grep "040000 $TREE 0 $dir/" cache \
|| return 1
done &&
- # Disabling the sparse-index removes tree entries with full ones
+ # Disabling the sparse-index replaces tree entries with full ones
git -C sparse-index sparse-checkout init --no-sparse-index &&
-
- test-tool -C sparse-index read-cache --table >cache &&
- ! grep "040000 tree" cache &&
- test_sparse_match test-tool read-cache --table
+ test_sparse_match git ls-files --stage --sparse
'
test_expect_success 'expanded in-memory index matches full index' '
init_repos &&
- test_sparse_match test-tool read-cache --expand --table
+ test_sparse_match git ls-files --stage
'
test_expect_success 'status with options' '
# having a submodule prevents "modules" from collapse
test_sparse_match git sparse-checkout set deep/deeper1 &&
- test-tool -C sparse-index read-cache --table >cache &&
- grep "100644 blob .* modules/a" cache &&
- grep "160000 commit $(git -C initial-repo rev-parse HEAD) modules/sub" cache
+ git -C sparse-index ls-files --sparse --stage >cache &&
+ grep "100644 .* modules/a" cache &&
+ grep "160000 $(git -C initial-repo rev-parse HEAD) 0 modules/sub" cache
'
# When working with a sparse index, some commands will need to expand the
GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
git -C sparse-index reset -- folder1/a &&
test_region index convert_to_sparse trace2.txt &&
+ test_region index ensure_full_index trace2.txt &&
+
+ # ls-files expands on read, but does not write.
+ rm trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+ git -C sparse-index ls-files &&
test_region index ensure_full_index trace2.txt
'
init_repos &&
ensure_not_expanded status &&
+ ensure_not_expanded ls-files --sparse &&
ensure_not_expanded commit --allow-empty -m empty &&
echo >>sparse-index/a &&
ensure_not_expanded commit -a -m a &&
done
'
+test_expect_success 'sparse index is not expanded: fetch/pull' '
+ init_repos &&
+
+ git -C sparse-index remote add full "file://$(pwd)/full-checkout" &&
+ ensure_not_expanded fetch full &&
+ git -C full-checkout commit --allow-empty -m "for pull merge" &&
+ git -C sparse-index commit --allow-empty -m "for pull merge" &&
+ ensure_not_expanded pull full base
+'
+
+test_expect_success 'ls-files' '
+ init_repos &&
+
+ # Use a smaller sparse-checkout for reduced output
+ test_sparse_match git sparse-checkout set &&
+
+ # Behavior agrees by default. Sparse index is expanded.
+ test_all_match git ls-files &&
+
+ # With --sparse, the sparse index data changes behavior.
+ git -C sparse-index ls-files --sparse >actual &&
+
+ cat >expect <<-\EOF &&
+ a
+ deep/
+ e
+ folder1-
+ folder1.x
+ folder1/
+ folder10
+ folder2/
+ g
+ x/
+ z
+ EOF
+
+ test_cmp expect actual &&
+
+ # With --sparse and no sparse index, nothing changes.
+ git -C sparse-checkout ls-files >dense &&
+ git -C sparse-checkout ls-files --sparse >sparse &&
+ test_cmp dense sparse &&
+
+ # Set up a strange condition of having a file edit
+ # outside of the sparse-checkout cone. This is just
+ # to verify that sparse-checkout and sparse-index
+ # behave the same in this case.
+ write_script edit-content <<-\EOF &&
+ mkdir folder1 &&
+ echo content >>folder1/a
+ EOF
+ run_on_sparse ../edit-content &&
+
+ # ls-files does not currently notice modified files whose
+ # cache entries are marked SKIP_WORKTREE. This may change
+ # in the future, but here we test that sparse index does
+ # not accidentally create a change of behavior.
+ test_sparse_match git ls-files --modified &&
+ test_must_be_empty sparse-checkout-out &&
+ test_must_be_empty sparse-index-out &&
+
+ git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
+ test_must_be_empty sparse-index-out &&
+
+ # Add folder1 to the sparse-checkout cone and
+ # check that ls-files shows the expanded files.
+ test_sparse_match git sparse-checkout add folder1 &&
+ test_sparse_match git ls-files --modified &&
+
+ test_all_match git ls-files &&
+ git -C sparse-index ls-files --sparse >actual &&
+
+ cat >expect <<-\EOF &&
+ a
+ deep/
+ e
+ folder1-
+ folder1.x
+ folder1/0/0/0
+ folder1/0/1
+ folder1/a
+ folder10
+ folder2/
+ g
+ x/
+ z
+ EOF
+
+ test_cmp expect actual &&
+
+ # Double-check index expansion is avoided
+ ensure_not_expanded ls-files --sparse
+'
+
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
# in this scenario, but it shouldn't.
test_expect_success 'reset mixed and checkout orphan' '
# the sparse checkouts skip "adding" the other side of
# the conflict.
test_sparse_match git reset --mixed HEAD~1 &&
- test_sparse_match test-tool read-cache --table --expand &&
+ test_sparse_match git ls-files --stage &&
test_sparse_match git status --porcelain=v2 &&
# At this point, sparse-checkouts behave differently
# from the full-checkout.
test_sparse_match git checkout --orphan new-branch &&
- test_sparse_match test-tool read-cache --table --expand &&
+ test_sparse_match git ls-files --stage &&
test_sparse_match git status --porcelain=v2
'
rm -f result &&
for i in 1 2 3 4
do
- git config --bool --get bool.true$i >>result
- git config --bool --get bool.false$i >>result
+ git config --bool --get bool.true$i >>result &&
+ git config --bool --get bool.false$i >>result || return 1
done &&
test_cmp expect result'
EOF
: "work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)" &&
{
- echo "$rel_out $(git config --expiry-date date.valid1)"
+ echo "$rel_out $(git config --expiry-date date.valid1)" &&
git config --expiry-date date.valid2 &&
git config --expiry-date date.valid3 &&
git config --expiry-date date.valid4 &&
(
for i in $(test_seq 33)
do
- echo "create refs/heads/$i HEAD"
+ echo "create refs/heads/$i HEAD" || exit 1
done >large_input &&
run_with_limited_open_files git update-ref --stdin <large_input &&
git rev-parse --verify -q refs/heads/33
(
for i in $(test_seq 33)
do
- echo "delete refs/heads/$i HEAD"
+ echo "delete refs/heads/$i HEAD" || exit 1
done >large_input &&
run_with_limited_open_files git update-ref --stdin <large_input &&
test_must_fail git rev-parse --verify -q refs/heads/33
test_expect_success 'show-ref -d' '
{
echo $(git rev-parse refs/tags/A) refs/tags/A &&
- echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+ echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}" &&
echo $(git rev-parse refs/tags/C) refs/tags/C
} >expect &&
git show-ref -d A C >actual &&
test_expect_success 'show-ref --heads, --tags, --head, pattern' '
for branch in B main side
do
- echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+ echo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1
done >expect.heads &&
git show-ref --heads >actual &&
test_cmp expect.heads actual &&
for tag in A B C
do
- echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+ echo $(git rev-parse refs/tags/$tag) refs/tags/$tag || return 1
done >expect.tags &&
git show-ref --tags >actual &&
test_cmp expect.tags actual &&
{
echo $(git rev-parse HEAD) HEAD &&
- echo $(git rev-parse refs/heads/B) refs/heads/B
+ echo $(git rev-parse refs/heads/B) refs/heads/B &&
echo $(git rev-parse refs/tags/B) refs/tags/B
} >expect &&
git show-ref --head B >actual &&
{
echo $(git rev-parse HEAD) HEAD &&
- echo $(git rev-parse refs/heads/B) refs/heads/B
- echo $(git rev-parse refs/tags/B) refs/tags/B
+ echo $(git rev-parse refs/heads/B) refs/heads/B &&
+ echo $(git rev-parse refs/tags/B) refs/tags/B &&
echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
} >expect &&
git show-ref --head -d B >actual &&
printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
if test $i = 75; then
for j in $(test_seq 1 89); do
- printf X
+ printf X || return 1
done
else
printf X
fi &&
- printf "\n"
+ printf "\n" || return 1
done >.git/logs/refs/heads/reflogskip &&
git rev-parse reflogskip@{73} >actual &&
echo ${zf}03 >expect &&
test_expect_success 'blob and tree' '
test_tick &&
(
- for i in 0 1 2 3 4 5 6 7 8 9
- do
- echo $i
- done &&
+ test_write_lines 0 1 2 3 4 5 6 7 8 9 &&
echo &&
echo b1rwzyc3
) >a0blgqsjc &&
git checkout v1.0.0^0 &&
git mv a0blgqsjc f5518nwu &&
- for i in h62xsjeu j08bekfvt kg7xflhm
- do
- echo $i
- done >>f5518nwu &&
+ test_write_lines h62xsjeu j08bekfvt kg7xflhm >>f5518nwu &&
git add f5518nwu &&
test_tick &&
do
grep $type objects >$type.objects &&
sort $type.objects >$type.objects.sorted &&
- test_cmp $type.objects.sorted $type.objects
+ test_cmp $type.objects.sorted $type.objects || return 1
done
'
# NEEDSWORK: Stop hard-coding checksums.
if test "$indexversion" = "4"
then
- own=$(test_oid own_v4)
+ own=$(test_oid own_v4) &&
base=$(test_oid base_v4)
else
- own=$(test_oid own_v3)
+ own=$(test_oid own_v3) &&
base=$(test_oid base_v3)
fi &&
test $(grep $f actual | cut "-d " -f2) = $f &&
p=$(grep $f actual | cut "-d " -f1) &&
test -f $p &&
- test $(cat $p) = tree1$f
+ test $(cat $p) = tree1$f || return 1
done
'
test $(grep $f actual | cut "-d " -f2) = $f &&
p=$(grep $f actual | cut "-d " -f1) &&
test -f $p &&
- test $(cat $p) = tree2$f
+ test $(cat $p) = tree2$f || return 1
done
'
test_expect_success 'more switches' '
for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
do
- git checkout -b branch$i
+ git checkout -b branch$i || return 1
done
'
more_switches () {
for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
do
- git checkout branch$i
+ git checkout branch$i || return 1
done
}
'the index entry must still be a symbolic link' '
case "$(git ls-files --stage --cached symlink)" in
120000" "*symlink) echo pass;;
-*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
+*) echo fail; git ls-files --stage --cached symlink; false;;
esac'
test_done
test_cmp expect actual &&
git update-index --add one two three &&
- for i in one three two; do echo $i; done >expect &&
+ test_write_lines one three two >expect &&
git ls-files >actual &&
test_cmp expect actual &&
{
for path in path1 path2
do
- echo "100644 $one 1 $path"
- echo "100644 $two 2 $path"
- echo "100644 $three 3 $path"
- done
- echo "100644 $one 1 path3"
- echo "100644 $one 1 path4"
- echo "100644 $one 3 path5"
+ echo "100644 $one 1 $path" &&
+ echo "100644 $two 2 $path" &&
+ echo "100644 $three 3 $path" || return 1
+ done &&
+ echo "100644 $one 1 path3" &&
+ echo "100644 $one 1 path4" &&
+ echo "100644 $one 3 path5" &&
echo "100644 $one 3 path6"
} |
git update-index --index-info &&
git add -u &&
git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
{
- echo "100644 $three 0 path1"
- echo "100644 $two 0 path3"
+ echo "100644 $three 0 path1" &&
+ echo "100644 $two 0 path3" &&
echo "100644 $two 0 path5"
} >expect &&
test_cmp expect actual
"
} >expect &&
{
- cat expect
- echo ":100644 160000 $_empty $ZERO_OID T yonk"
+ cat expect &&
+ echo ":100644 160000 $_empty $ZERO_OID T yonk" &&
echo ":100644 000000 $_empty $ZERO_OID D zifmia"
} >expect-files &&
{
- cat expect
+ cat expect &&
echo ":000000 160000 $ZERO_OID $ZERO_OID A yonk"
} >expect-index &&
{
- echo "100644 $_empty 0 nitfol"
- echo "160000 $yomin 0 yomin"
+ echo "100644 $_empty 0 nitfol" &&
+ echo "160000 $yomin 0 yomin" &&
echo "160000 $yonk 0 yonk"
} >expect-final
'
mkdir 2 &&
for f in 1 2/1 2/2 3
do
- echo "$f" >"$f"
+ echo "$f" >"$f" || return 1
done &&
git add 1 2/2 3 &&
git add -N 2/1 &&
--- /dev/null
+#!/bin/sh
+
+test_description='Test handling of the current working directory becoming empty'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit init &&
+
+ git branch fd_conflict &&
+
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+
+ git revert HEAD &&
+ git tag reverted &&
+
+ git checkout fd_conflict &&
+ mkdir dirORfile &&
+ test_commit dirORfile/foo &&
+
+ git rm -r dirORfile &&
+ echo not-a-directory >dirORfile &&
+ git add dirORfile &&
+ git commit -m dirORfile &&
+
+ git switch -c df_conflict HEAD~1 &&
+ test_commit random_file &&
+
+ git switch -c undo_fd_conflict fd_conflict &&
+ git revert HEAD
+'
+
+test_incidental_dir_removal () {
+ test_when_finished "git reset --hard" &&
+
+ git checkout foo/bar/baz^{commit} &&
+ test_path_is_dir foo/bar &&
+
+ (
+ cd foo &&
+ "$@" &&
+
+ # Make sure foo still exists, and commands needing it work
+ test-tool getcwd &&
+ git status --porcelain
+ ) &&
+ test_path_is_missing foo/bar/baz &&
+ test_path_is_missing foo/bar &&
+
+ test_path_is_dir foo
+}
+
+test_required_dir_removal () {
+ git checkout df_conflict^{commit} &&
+ test_when_finished "git clean -fdx" &&
+
+ (
+ cd dirORfile &&
+
+ # Ensure command refuses to run
+ test_must_fail "$@" 2>../error &&
+ grep "Refusing to remove.*current working directory" ../error &&
+
+ # ...and that the index and working tree are left clean
+ git diff --exit-code HEAD &&
+
+ # Ensure that getcwd and git status do not error out (which
+ # they might if the current working directory had been removed)
+ test-tool getcwd &&
+ git status --porcelain
+ ) &&
+
+ test_path_is_dir dirORfile
+}
+
+test_expect_success 'checkout does not clean cwd incidentally' '
+ test_incidental_dir_removal git checkout init
+'
+
+test_expect_success 'checkout fails if cwd needs to be removed' '
+ test_required_dir_removal git checkout fd_conflict
+'
+
+test_expect_success 'reset --hard does not clean cwd incidentally' '
+ test_incidental_dir_removal git reset --hard init
+'
+
+test_expect_success 'reset --hard fails if cwd needs to be removed' '
+ test_required_dir_removal git reset --hard fd_conflict
+'
+
+test_expect_success 'merge does not clean cwd incidentally' '
+ test_incidental_dir_removal git merge reverted
+'
+
+# This file uses some simple merges where
+# Base: 'dirORfile/' exists
+# Side1: random other file changed
+# Side2: 'dirORfile/' removed, 'dirORfile' added
+# this should resolve cleanly, but merge-recursive throws merge conflicts
+# because it's dumb. Add a special test for checking merge-recursive (and
+# merge-ort), then after this just hard require ort for all remaining tests.
+#
+test_expect_success 'merge fails if cwd needs to be removed; recursive friendly' '
+ git checkout foo/bar/baz &&
+ test_when_finished "git clean -fdx" &&
+
+ mkdir dirORfile &&
+ (
+ cd dirORfile &&
+
+ test_must_fail git merge fd_conflict 2>../error
+ ) &&
+
+ test_path_is_dir dirORfile &&
+ grep "Refusing to remove the current working directory" error
+'
+
+GIT_TEST_MERGE_ALGORITHM=ort
+
+test_expect_success 'merge fails if cwd needs to be removed' '
+ test_required_dir_removal git merge fd_conflict
+'
+
+test_expect_success 'cherry-pick does not clean cwd incidentally' '
+ test_incidental_dir_removal git cherry-pick reverted
+'
+
+test_expect_success 'cherry-pick fails if cwd needs to be removed' '
+ test_required_dir_removal git cherry-pick fd_conflict
+'
+
+test_expect_success 'rebase does not clean cwd incidentally' '
+ test_incidental_dir_removal git rebase reverted
+'
+
+test_expect_success 'rebase fails if cwd needs to be removed' '
+ test_required_dir_removal git rebase fd_conflict
+'
+
+test_expect_success 'revert does not clean cwd incidentally' '
+ test_incidental_dir_removal git revert HEAD
+'
+
+test_expect_success 'revert fails if cwd needs to be removed' '
+ test_required_dir_removal git revert undo_fd_conflict
+'
+
+test_expect_success 'rm does not clean cwd incidentally' '
+ test_incidental_dir_removal git rm bar/baz.t
+'
+
+test_expect_success 'apply does not remove cwd incidentally' '
+ git diff HEAD HEAD~1 >patch &&
+ test_incidental_dir_removal git apply ../patch
+'
+
+test_incidental_untracked_dir_removal () {
+ test_when_finished "git reset --hard" &&
+
+ git checkout foo/bar/baz^{commit} &&
+ mkdir -p untracked &&
+ mkdir empty
+ >untracked/random &&
+
+ (
+ cd untracked &&
+ "$@" &&
+
+ # Make sure untracked still exists, and commands needing it work
+ test-tool getcwd &&
+ git status --porcelain
+ ) &&
+ test_path_is_missing empty &&
+ test_path_is_missing untracked/random &&
+
+ test_path_is_dir untracked
+}
+
+test_expect_success 'clean does not remove cwd incidentally' '
+ test_incidental_untracked_dir_removal \
+ git -C .. clean -fd -e warnings . >warnings &&
+ grep "Refusing to remove current working directory" warnings
+'
+
+test_expect_success 'stash does not remove cwd incidentally' '
+ test_incidental_untracked_dir_removal \
+ git stash --include-untracked
+'
+
+test_expect_success '`rm -rf dir` only removes a subset of dir' '
+ test_when_finished "rm -rf a/" &&
+
+ mkdir -p a/b/c &&
+ >a/b/c/untracked &&
+ >a/b/c/tracked &&
+ git add a/b/c/tracked &&
+
+ (
+ cd a/b &&
+ git rm -rf ../b
+ ) &&
+
+ test_path_is_dir a/b &&
+ test_path_is_missing a/b/c/tracked &&
+ test_path_is_file a/b/c/untracked
+'
+
+test_expect_success '`rm -rf dir` even with only tracked files will remove something else' '
+ test_when_finished "rm -rf a/" &&
+
+ mkdir -p a/b/c &&
+ >a/b/c/tracked &&
+ git add a/b/c/tracked &&
+
+ (
+ cd a/b &&
+ git rm -rf ../b
+ ) &&
+
+ test_path_is_missing a/b/c/tracked &&
+ test_path_is_missing a/b/c &&
+ test_path_is_dir a/b
+'
+
+test_expect_success 'git version continues working from a deleted dir' '
+ mkdir tmp &&
+ (
+ cd tmp &&
+ rm -rf ../tmp &&
+ git version
+ )
+'
+
+test_submodule_removal () {
+ path_status=$1 &&
+ shift &&
+
+ test_status=
+ test "$path_status" = dir && test_status=test_must_fail
+
+ test_when_finished "git reset --hard HEAD~1" &&
+ test_when_finished "rm -rf .git/modules/my_submodule" &&
+
+ git checkout foo/bar/baz &&
+
+ git init my_submodule &&
+ touch my_submodule/file &&
+ git -C my_submodule add file &&
+ git -C my_submodule commit -m "initial commit" &&
+ git submodule add ./my_submodule &&
+ git commit -m "Add the submodule" &&
+
+ (
+ cd my_submodule &&
+ $test_status "$@"
+ ) &&
+
+ test_path_is_${path_status} my_submodule
+}
+
+test_expect_success 'rm -r with -C leaves submodule if cwd inside' '
+ test_submodule_removal dir git -C .. rm -r my_submodule/
+'
+
+test_expect_success 'rm -r leaves submodule if cwd inside' '
+ test_submodule_removal dir \
+ git --git-dir=../.git --work-tree=.. rm -r ../my_submodule/
+'
+
+test_expect_success 'rm -rf removes submodule even if cwd inside' '
+ test_submodule_removal missing \
+ git --git-dir=../.git --work-tree=.. rm -rf ../my_submodule/
+'
+
+test_done
test_expect_success 'ls-files -c' '
(
cd top/sub &&
- for f in ../y*
- do
- echo "error: pathspec $SQ$f$SQ did not match any file(s) known to git"
- done >expect.err &&
+ printf "error: pathspec $SQ%s$SQ did not match any file(s) known to git\n" ../y* >expect.err &&
echo "Did you forget to ${SQ}git add${SQ}?" >>expect.err &&
ls ../x* >expect.out &&
test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
test_expect_success 'ls-files -o' '
(
cd top/sub &&
- for f in ../x*
- do
- echo "error: pathspec $SQ$f$SQ did not match any file(s) known to git"
- done >expect.err &&
+ printf "error: pathspec $SQ%s$SQ did not match any file(s) known to git\n" ../x* >expect.err &&
echo "Did you forget to ${SQ}git add${SQ}?" >>expect.err &&
ls ../y* >expect.out &&
test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
file=$(cat .git/expected_test_file) &&
if should_create_test_file "$file"
then
- dirs=${file%/*}
+ dirs=${file%/*} &&
if test "$file" != "$dirs"
then
mkdir -p -- "$dirs" &&
for i in $(test_seq 1 10)
do
git checkout -b branch$i initial &&
- test_commit --no-tag branch$i
+ test_commit --no-tag branch$i || return 1
done &&
git for-each-ref \
--sort=version:refname \
test_expect_success 'show-branch with showbranch.default' '
for branch in $(cat branches.sorted)
do
- test_config showbranch.default $branch --add
+ test_config showbranch.default $branch --add || return 1
done &&
git show-branch >actual &&
test_cmp expect actual
do
git rev-parse $branch >expect &&
git show-branch --merge-base $branch >actual &&
- test_cmp expect actual
+ test_cmp expect actual || return 1
done
'
do
git rev-parse initial >expect &&
git show-branch --merge-base initial $branch >actual &&
- test_cmp expect actual
+ test_cmp expect actual || return 1
done
'
while [ $i -gt 0 ]; do
echo " commit #$i" &&
echo " note for commit #$i" &&
- i=$(($i-1));
+ i=$(($i-1)) || return 1
done > expect &&
test_cmp expect output
}
while [ $nr -lt $number_of_commits ]; do
nr=$(($nr+1)) &&
test_tick &&
- cat <<INPUT_END
+ cat <<INPUT_END || return 1
commit refs/heads/main
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
data <<COMMIT
echo " first note for commit #$i" &&
echo " " &&
echo " second note for commit #$i" &&
- i=$(($i-1));
+ i=$(($i-1)) || return 1
done > expect &&
test_cmp expect output
}
do
echo " commit #$i" &&
echo " note #$i" &&
- i=$(($i - 1));
+ i=$(($i - 1)) || return 1
done > expect &&
test_cmp expect output
'
do
echo " commit #$i" &&
echo " note #$i" &&
- i=$(($i - 1));
+ i=$(($i - 1)) || return 1
done > expect &&
test_cmp expect output
'
if test -s difference
then
cat difference
- (exit 1)
+ false
else
echo happy
fi
if test -s difference
then
cat difference
- (exit 1)
+ false
else
echo happy
fi
echo happy
else
git show-branch
- (exit 1)
+ false
fi &&
f=$(git diff-tree --name-only HEAD^ HEAD) &&
g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
*)
echo "$f"
echo "$g"
- (exit 1)
+ false
esac
'
do
test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
git diff HEAD~$p original-no-ff-branch~$p > out &&
- test_must_be_empty out
+ test_must_be_empty out || return 1
done &&
test_cmp_rev HEAD~3 original-no-ff-branch~3 &&
git diff HEAD~3 original-no-ff-branch~3 > out &&
test_seq 5 | sed "s/$double/&&/" >seq &&
git add seq &&
test_tick &&
- git commit -m seq-$double
+ git commit -m seq-$double || return 1
done &&
git tag seq-onto &&
git reset --hard HEAD~2 &&
git config core.whitespace "blank-at-eol" &&
cp beginning file &&
git commit -m beginning file &&
- for i in 1 2 3 4 5; do
- echo $i
- done >> file &&
+ test_write_lines 1 2 3 4 5 >>file &&
git commit -m more file &&
git rebase --whitespace=fix HEAD^^ &&
test_cmp expect-beginning file
for l in a b c d e f g h i j k l m n o
do
- echo $l$l$l$l$l$l$l$l$l
+ echo $l$l$l$l$l$l$l$l$l || return 1
done >oops &&
test_tick &&
git add file1 &&
test_tick &&
git commit -m "$val" &&
- git tag $val
+ git tag $val || return 1
done
'
test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
- OUT=$( ((trap "" PIPE; git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+ OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'
git reset -q --hard &&
test_when_finished "rm -f .git/index.lock msg && git reset -q --hard" &&
blob=$(echo blob | git hash-object -w --stdin) &&
- for stage in 1 2 3
- do
- echo "100644 $blob $stage blob"
- done | git update-index --index-info &&
+ printf "100644 $blob %d\tblob\n" 1 2 3 | git update-index --index-info &&
git rm blob >msg 2>&1 &&
test_i18ngrep ! "needs merge" msg &&
test_must_fail git ls-files -s --error-unmatch blob
test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries' '
for s in 1 2 3
do
- echo $s > stage$s
- echo "100755 $(git hash-object -w stage$s) $s file"
- echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s symlink"
+ echo $s > stage$s &&
+ echo "100755 $(git hash-object -w stage$s) $s file" &&
+ echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s symlink" || return 1
done | git update-index --index-info &&
git config core.filemode 0 &&
git config core.symlinks 0 &&
git read-tree HEAD &&
case "$(git diff-index HEAD -- foo)" in
:100644" "*"M foo") echo pass;;
- *) echo fail; (exit 1);;
+ *) echo fail; false;;
esac &&
git add --refresh -- foo &&
test -z "$(git diff-index HEAD -- foo)"
# Avoid munging CRLFs to avoid an error message
git -c core.autocrlf=input add --sparse sparse_entry 2>stderr &&
test_must_be_empty stderr &&
- test-tool read-cache --table >actual &&
- grep "^100644 blob.*sparse_entry\$" actual &&
+ git ls-files --stage >actual &&
+ grep "^100644 .*sparse_entry\$" actual &&
git add --sparse --chmod=+x sparse_entry 2>stderr &&
test_must_be_empty stderr &&
- test-tool read-cache --table >actual &&
- grep "^100755 blob.*sparse_entry\$" actual &&
+ git ls-files --stage >actual &&
+ grep "^100755 .*sparse_entry\$" actual &&
git reset &&
. ./test-lib.sh
+test_expect_success 'usage on cmd and subcommand invalid option' '
+ test_expect_code 129 git stash --invalid-option 2>usage &&
+ grep "or: git stash" usage &&
+
+ test_expect_code 129 git stash push --invalid-option 2>usage &&
+ ! grep "or: git stash" usage
+'
+
+test_expect_success 'usage on main command -h emits a summary of subcommands' '
+ test_expect_code 129 git stash -h >usage &&
+ grep -F "usage: git stash list" usage &&
+ grep -F "or: git stash show" usage
+'
+
+test_expect_failure 'usage for subcommands should emit subcommand usage' '
+ test_expect_code 129 git stash push -h >usage &&
+ grep -F "usage: git stash [push" usage
+'
+
diff_cmp () {
for i in "$1" "$2"
do
for ref in ${LIB_CRLF_BRANCHES}
do
cat .crlf-${file}-\"\${ref}\".txt >>expect &&
- printf \"\n\" >>expect
+ printf \"\n\" >>expect || return 1
done &&
git $command_and_args --format=\"%${atom}\" >actual &&
test_cmp expect actual
do
printf " " >>expect &&
cat .crlf-subject-${branch}.txt >>expect &&
- printf "\n" >>expect
+ printf "\n" >>expect || return 1
done &&
git branch -v >tmp &&
# Remove first two columns, and the line for the currently checked out branch
do
for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" >"path$i$j"
+ echo "$i$j" >"path$i$j" || return 1
done
done &&
git add "path??" &&
i=0 &&
while test $i -lt 10000; do
echo $i &&
- i=$(($i + 1))
+ i=$(($i + 1)) || return 1
done >textfile &&
git add textfile &&
git diff --cached --stat binfile textfile >output &&
mkdir dir &&
mkdir dir2 &&
- for i in 1 2 3; do echo $i; done >file0 &&
- for i in A B; do echo $i; done >dir/sub &&
+ test_write_lines 1 2 3 >file0 &&
+ test_write_lines A B >dir/sub &&
cat file0 >file2 &&
git add file0 file2 dir/sub &&
git commit -m Initial &&
GIT_COMMITTER_DATE="2006-06-26 00:01:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- for i in 4 5 6; do echo $i; done >>file0 &&
- for i in C D; do echo $i; done >>dir/sub &&
+ test_write_lines 4 5 6 >>file0 &&
+ test_write_lines C D >>dir/sub &&
rm -f file2 &&
git update-index --remove file0 file2 dir/sub &&
git commit -m "Second${LF}${LF}This is the second commit." &&
GIT_COMMITTER_DATE="2006-06-26 00:02:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- for i in A B C; do echo $i; done >file1 &&
+ test_write_lines A B C >file1 &&
git add file1 &&
- for i in E F; do echo $i; done >>dir/sub &&
+ test_write_lines E F >>dir/sub &&
git update-index dir/sub &&
git commit -m Third &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
git checkout side &&
- for i in A B C; do echo $i; done >>file0 &&
- for i in 1 2; do echo $i; done >>dir/sub &&
+ test_write_lines A B C >>file0 &&
+ test_write_lines 1 2 >>dir/sub &&
cat dir/sub >file3 &&
git add file3 &&
git update-index file0 dir/sub &&
GIT_COMMITTER_DATE="2006-06-26 00:05:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
- for i in A B C; do echo $i; done >>file0 &&
- for i in 1 2; do echo $i; done >>dir/sub &&
+ test_write_lines A B C >>file0 &&
+ test_write_lines 1 2 >>dir/sub &&
git update-index file0 dir/sub &&
mkdir dir3 &&
GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
git checkout -b rearrange initial &&
- for i in B A; do echo $i; done >dir/sub &&
+ test_write_lines B A >dir/sub &&
git add dir/sub &&
git commit -m "Rearranged lines in dir/sub" &&
git checkout master &&
. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&
cat file >elif &&
git add file elif &&
test_tick &&
git commit -m Initial &&
git checkout -b side &&
- for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
+ test_write_lines 1 2 5 6 A B C 7 8 9 10 >file &&
test_chmod +x elif &&
test_tick &&
git commit -m "Side changes #1" &&
- for i in D E F; do echo "$i"; done >>file &&
+ test_write_lines D E F >>file &&
git update-index file &&
test_tick &&
git commit -m "Side changes #2" &&
git tag C2 &&
- for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
+ test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file &&
git update-index file &&
test_tick &&
git commit -m "Side changes #3 with \\n backslash-n in it." &&
git checkout side &&
git checkout -b patchid &&
- for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file2 &&
- for i in 1 2 3 A 4 B C 7 8 9 10 D E F 5 6; do echo "$i"; done >file3 &&
- for i in 8 9 10; do echo "$i"; done >file &&
+ test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file2 &&
+ test_write_lines 1 2 3 A 4 B C 7 8 9 10 D E F 5 6 >file3 &&
+ test_write_lines 8 9 10 >file &&
git add file file2 file3 &&
test_tick &&
git commit -m "patchid 1" &&
- for i in 4 A B 7 8 9 10; do echo "$i"; done >file2 &&
- for i in 8 9 10 5 6; do echo "$i"; done >file3 &&
+ test_write_lines 4 A B 7 8 9 10 >file2 &&
+ test_write_lines 8 9 10 5 6 >file3 &&
git add file2 file3 &&
test_tick &&
git commit -m "patchid 2" &&
- for i in 10 5 6; do echo "$i"; done >file &&
+ test_write_lines 10 5 6 >file &&
git add file &&
test_tick &&
git commit -m "patchid 3" &&
max=$(
for patch in 000[1-9]-*.patch
do
- echo "$patch" | wc -c
+ echo "$patch" | wc -c || exit 1
done |
sort -nr |
head -n 1
max=$(
for patch in 000[1-9]-*.patch
do
- echo "$patch" | wc -c
+ echo "$patch" | wc -c || exit 1
done |
sort -nr |
head -n 1
max=$(
for patch in patches/000[1-9]-*.patch
do
- echo "${patch#patches/}" | wc -c
+ echo "${patch#patches/}" | wc -c || exit 1
done |
sort -nr |
head -n 1
git checkout side &&
before=$(git hash-object file) &&
before=$(git rev-parse --short $before) &&
- for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
+ test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >>file &&
after=$(git hash-object file) &&
after=$(git rev-parse --short $after) &&
git update-index file &&
test_expect_success 'format-patch handles multi-line subjects' '
rm -rf patches/ &&
echo content >>file &&
- for i in one two three; do echo $i; done >msg &&
+ test_write_lines one two three >msg &&
git add file &&
git commit -F msg &&
git format-patch -o patches -1 &&
test_expect_success 'format-patch handles multi-line encoded subjects' '
rm -rf patches/ &&
echo content >>file &&
- for i in en två tre; do echo $i; done >msg &&
+ test_write_lines en två tre >msg &&
git add file &&
git commit -F msg &&
git format-patch -o patches -1 &&
test_expect_success 'whitespace-only changes reported across renames (diffstat)' '
git reset --hard &&
- for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i" || return 1; done >x &&
git add x &&
git commit -m "base" &&
sed -e "5s/^/ /" x >z &&
test_expect_success 'whitespace-only changes reported across renames' '
git reset --hard HEAD~1 &&
- for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i" || return 1; done >x &&
git add x &&
hash_x=$(git hash-object x) &&
before=$(git rev-parse --short "$hash_x") &&
test_cmp expected actual
'
+test_expect_success 'zebra alternate color is only used when necessary' '
+ cat >old.txt <<-\EOF &&
+ line 1A should be marked as oldMoved newMovedAlternate
+ line 1B should be marked as oldMoved newMovedAlternate
+ unchanged
+ line 2A should be marked as oldMoved newMovedAlternate
+ line 2B should be marked as oldMoved newMovedAlternate
+ line 3A should be marked as oldMovedAlternate newMoved
+ line 3B should be marked as oldMovedAlternate newMoved
+ unchanged
+ line 4A should be marked as oldMoved newMovedAlternate
+ line 4B should be marked as oldMoved newMovedAlternate
+ line 5A should be marked as oldMovedAlternate newMoved
+ line 5B should be marked as oldMovedAlternate newMoved
+ line 6A should be marked as oldMoved newMoved
+ line 6B should be marked as oldMoved newMoved
+ EOF
+ cat >new.txt <<-\EOF &&
+ line 1A should be marked as oldMoved newMovedAlternate
+ line 1B should be marked as oldMoved newMovedAlternate
+ unchanged
+ line 3A should be marked as oldMovedAlternate newMoved
+ line 3B should be marked as oldMovedAlternate newMoved
+ line 2A should be marked as oldMoved newMovedAlternate
+ line 2B should be marked as oldMoved newMovedAlternate
+ unchanged
+ line 6A should be marked as oldMoved newMoved
+ line 6B should be marked as oldMoved newMoved
+ line 4A should be marked as oldMoved newMovedAlternate
+ line 4B should be marked as oldMoved newMovedAlternate
+ line 5A should be marked as oldMovedAlternate newMoved
+ line 5B should be marked as oldMovedAlternate newMoved
+ EOF
+ test_expect_code 1 git diff --no-index --color --color-moved=zebra \
+ --color-moved-ws=allow-indentation-change \
+ old.txt new.txt >output &&
+ grep -v index output | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/old.txt b/new.txt<RESET>
+ <BOLD>--- a/old.txt<RESET>
+ <BOLD>+++ b/new.txt<RESET>
+ <CYAN>@@ -1,14 +1,14 @@<RESET>
+ <BOLD;MAGENTA>-line 1A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;MAGENTA>-line 1B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 1A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 1B should be marked as oldMoved newMovedAlternate<RESET>
+ unchanged<RESET>
+ <BOLD;MAGENTA>-line 2A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;MAGENTA>-line 2B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;BLUE>-line 3A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;BLUE>-line 3B should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 3A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 3B should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 2A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 2B should be marked as oldMoved newMovedAlternate<RESET>
+ unchanged<RESET>
+ <BOLD;MAGENTA>-line 4A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;MAGENTA>-line 4B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;BLUE>-line 5A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;BLUE>-line 5B should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;MAGENTA>-line 6A should be marked as oldMoved newMoved<RESET>
+ <BOLD;MAGENTA>-line 6B should be marked as oldMoved newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 6A should be marked as oldMoved newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 6B should be marked as oldMoved newMoved<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 4A should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW> line 4B should be marked as oldMoved newMovedAlternate<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 5A should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN> line 5B should be marked as oldMovedAlternate newMoved<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'short lines of opposite sign do not get marked as moved' '
+ cat >old.txt <<-\EOF &&
+ this line should be marked as moved
+ unchanged
+ unchanged
+ unchanged
+ unchanged
+ too short
+ this line should be marked as oldMoved newMoved
+ this line should be marked as oldMovedAlternate newMoved
+ unchanged 1
+ unchanged 2
+ unchanged 3
+ unchanged 4
+ this line should be marked as oldMoved newMoved/newMovedAlternate
+ EOF
+ cat >new.txt <<-\EOF &&
+ too short
+ unchanged
+ unchanged
+ this line should be marked as moved
+ too short
+ unchanged
+ unchanged
+ this line should be marked as oldMoved newMoved/newMovedAlternate
+ unchanged 1
+ unchanged 2
+ this line should be marked as oldMovedAlternate newMoved
+ this line should be marked as oldMoved newMoved/newMovedAlternate
+ unchanged 3
+ this line should be marked as oldMoved newMoved
+ unchanged 4
+ EOF
+ test_expect_code 1 git diff --no-index --color --color-moved=zebra \
+ old.txt new.txt >output && cat output &&
+ grep -v index output | test_decode_color >actual &&
+ cat >expect <<-\EOF &&
+ <BOLD>diff --git a/old.txt b/new.txt<RESET>
+ <BOLD>--- a/old.txt<RESET>
+ <BOLD>+++ b/new.txt<RESET>
+ <CYAN>@@ -1,13 +1,15 @@<RESET>
+ <BOLD;MAGENTA>-this line should be marked as moved<RESET>
+ <GREEN>+<RESET><GREEN>too short<RESET>
+ unchanged<RESET>
+ unchanged<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as moved<RESET>
+ <GREEN>+<RESET><GREEN>too short<RESET>
+ unchanged<RESET>
+ unchanged<RESET>
+ <RED>-too short<RESET>
+ <BOLD;MAGENTA>-this line should be marked as oldMoved newMoved<RESET>
+ <BOLD;BLUE>-this line should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+ unchanged 1<RESET>
+ unchanged 2<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMovedAlternate newMoved<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+ unchanged 3<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved<RESET>
+ unchanged 4<RESET>
+ <BOLD;MAGENTA>-this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'cmd option assumes configured colored-moved' '
test_config color.diff.oldMoved "magenta" &&
test_config color.diff.newMoved "cyan" &&
test_cmp expected actual
'
+test_expect_success '--color-moved rewinds for MIN_ALNUM_COUNT' '
+ git reset --hard &&
+ test_write_lines >file \
+ A B C one two three four five six seven D E F G H I J &&
+ git add file &&
+ test_write_lines >file \
+ one two A B C D E F G H I J two three four five six seven &&
+ git diff --color-moved=zebra -- file &&
+
+ git diff --color-moved=zebra --color -- file >actual.raw &&
+ grep -v "index" actual.raw | test_decode_color >actual &&
+ cat >expected <<-\EOF &&
+ <BOLD>diff --git a/file b/file<RESET>
+ <BOLD>--- a/file<RESET>
+ <BOLD>+++ b/file<RESET>
+ <CYAN>@@ -1,13 +1,8 @@<RESET>
+ <GREEN>+<RESET><GREEN>one<RESET>
+ <GREEN>+<RESET><GREEN>two<RESET>
+ A<RESET>
+ B<RESET>
+ C<RESET>
+ <RED>-one<RESET>
+ <BOLD;MAGENTA>-two<RESET>
+ <BOLD;MAGENTA>-three<RESET>
+ <BOLD;MAGENTA>-four<RESET>
+ <BOLD;MAGENTA>-five<RESET>
+ <BOLD;MAGENTA>-six<RESET>
+ <BOLD;MAGENTA>-seven<RESET>
+ D<RESET>
+ E<RESET>
+ F<RESET>
+ <CYAN>@@ -15,3 +10,9 @@<RESET> <RESET>G<RESET>
+ H<RESET>
+ I<RESET>
+ J<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>two<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>three<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>four<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>five<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>six<RESET>
+ <BOLD;CYAN>+<RESET><BOLD;CYAN>seven<RESET>
+ EOF
+
+ test_cmp expected actual
+'
+
test_expect_success 'move detection with submodules' '
test_create_repo bananas &&
echo ripe >bananas/recipe &&
test_expect_success 'compare mixed whitespace delta across moved blocks' '
git reset --hard &&
- tr Q_ "\t " <<-EOF >text.txt &&
- ${EMPTY}
- ____too short without
- ${EMPTY}
+ tr "^|Q_" "\f\v\t " <<-EOF >text.txt &&
+ ^__
+ |____too short without
+ ^
___being grouped across blank line
${EMPTY}
context
git add text.txt &&
git commit -m "add text.txt" &&
- tr Q_ "\t " <<-EOF >text.txt &&
+ tr "^|Q_" "\f\v\t " <<-EOF >text.txt &&
context
lines
to
${EMPTY}
QQtoo short without
${EMPTY}
- Q_______being grouped across blank line
+ ^Q_______being grouped across blank line
${EMPTY}
Q_QThese two lines have had their
indentation reduced by four spaces
-c core.whitespace=space-before-tab \
diff --color --color-moved --ws-error-highlight=all \
--color-moved-ws=allow-indentation-change >actual.raw &&
- grep -v "index" actual.raw | test_decode_color >actual &&
+ grep -v "index" actual.raw | tr "\f\v" "^|" | test_decode_color >actual &&
cat <<-\EOF >expected &&
<BOLD>diff --git a/text.txt b/text.txt<RESET>
<BOLD>--- a/text.txt<RESET>
<BOLD>+++ b/text.txt<RESET>
<CYAN>@@ -1,16 +1,16 @@<RESET>
- <BOLD;MAGENTA>-<RESET>
- <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> too short without<RESET>
- <BOLD;MAGENTA>-<RESET>
+ <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>^<RESET><BRED> <RESET>
+ <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>| too short without<RESET>
+ <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>^<RESET>
<BOLD;MAGENTA>-<RESET><BOLD;MAGENTA> being grouped across blank line<RESET>
<BOLD;MAGENTA>-<RESET>
<RESET>context<RESET>
<BOLD;YELLOW>+<RESET>
<BOLD;YELLOW>+<RESET> <BOLD;YELLOW>too short without<RESET>
<BOLD;YELLOW>+<RESET>
- <BOLD;YELLOW>+<RESET> <BOLD;YELLOW> being grouped across blank line<RESET>
+ <BOLD;YELLOW>+<RESET><BOLD;YELLOW>^ being grouped across blank line<RESET>
<BOLD;YELLOW>+<RESET>
<BOLD;CYAN>+<RESET> <BRED> <RESET> <BOLD;CYAN>These two lines have had their<RESET>
<BOLD;CYAN>+<RESET><BOLD;CYAN>indentation reduced by four spaces<RESET>
test_expect_success 'setup hunk header tests' '
for i in $diffpatterns
do
- echo "$i-* diff=$i"
+ echo "$i-* diff=$i" || return 1
done > .gitattributes &&
# add all test files to the index
'
test_expect_success 'color new trailing blank lines' '
- { echo a; echo b; echo; echo; } >x &&
+ test_write_lines a b "" "" >x &&
git add x &&
- { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+ test_write_lines a "" "" "" c "" "" "" "" >x &&
git diff --color x >output &&
cnt=$($grep_a "${blue_grep}" output | wc -l) &&
test $cnt = 2
git diff-tree five six -r --name-status -B -M | sort >actual &&
{
- echo "R100 foo bar"
+ echo "R100 foo bar" &&
echo "R100 bar foo"
} | sort >expect &&
test_cmp expect actual
git diff-tree one two -r --name-status -B -M | sort >actual &&
{
- echo "R100 foo bar"
+ echo "R100 foo bar" &&
echo "R100 bar foo"
} | sort >expect &&
test_cmp expect actual
git diff-tree three four -r --name-status -B -M | sort >actual &&
{
# see -B -M (#6) in t4008
- echo "C100 foo bar"
+ echo "C100 foo bar" &&
echo "T100 foo"
} | sort >expect &&
test_cmp expect actual
for n in $sample
do
- git diff -U0 file-?$n
+ git diff -U0 file-?$n || return 1
done | zc >actual &&
test_cmp expect actual
(
echo "A $NS" &&
- for c in B C D E F G H I J K
- do
- echo " $c"
- done &&
+ printf " %s\n" B C D E F G H I J K &&
echo "L $NS" &&
- for c in M N O P Q R S T U V
- do
- echo " $c"
- done
+ printf " %s\n" M N O P Q R S T U V
) >file &&
git add file &&
for i in $(test_seq 1 40)
do
blob=$(echo file$i | git hash-object --stdin -w) &&
- trees="$trees$(echo "100644 blob $blob file" | git mktree)$LF"
+ trees="$trees$(echo "100644 blob $blob file" | git mktree)$LF" || return 1
done
'
for t in o x
do
path="$b$o$t" &&
- case "$path" in ooo) continue ;; esac
+ case "$path" in ooo) continue ;; esac &&
paths="$paths$path " &&
p=" $path" &&
case "$b" in x) echo "$m1$p" ;; esac &&
for path in $paths
do
>"$path" &&
- echo ":000000 100644 $ZERO_OID $ZERO_OID U $path"
+ echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" || return 1
done >diff-files-0.expect &&
git diff-files -0 >diff-files-0.actual &&
test_cmp diff-files-0.expect diff-files-0.actual
echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" &&
case "$path" in
x??) echo ":100644 100644 $blob1 $ZERO_OID M $path"
- esac
+ esac || return 1
done >diff-files-1.expect &&
git diff-files -1 >diff-files-1.actual &&
test_cmp diff-files-1.expect diff-files-1.actual
echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" &&
case "$path" in
?x?) echo ":100644 100644 $blob2 $ZERO_OID M $path"
- esac
+ esac || return 1
done >diff-files-2.expect &&
git diff-files -2 >diff-files-2.actual &&
test_cmp diff-files-2.expect diff-files-2.actual &&
echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" &&
case "$path" in
??x) echo ":100644 100644 $blob3 $ZERO_OID M $path"
- esac
+ esac || return 1
done >diff-files-3.expect &&
git diff-files -3 >diff-files-3.actual &&
test_cmp diff-files-3.expect diff-files-3.actual
git rm -f d &&
for stage in 1 2 3
do
- sed -e "s/ 0 a/ $stage d/" x
+ sed -e "s/ 0 a/ $stage d/" x || return 1
done |
git update-index --index-info &&
echo d >d &&
i=0 &&
while test $i -lt 1000
do
- echo $i && i=$(($i + 1))
+ echo $i && i=$(($i + 1)) || return 1
done >abcd &&
git commit -m message abcd
'
for i in $(test_seq 1 9)
do
echo $i >$i.txt &&
- git add $i.txt
+ git add $i.txt || return 1
done &&
git commit -m "init" &&
git checkout -b side &&
for i in $(test_seq 2 9)
do
- echo $i/2 >>$i.txt
+ echo $i/2 >>$i.txt || return 1
done &&
git commit -a -m "side 2-9" &&
git checkout main &&
git checkout side &&
for i in $(test_seq 2 9)
do
- echo $i/3 >>$i.txt
+ echo $i/3 >>$i.txt || return 1
done &&
echo "4side" >>4.txt &&
git commit -a -m "side 2-9 +4" &&
git checkout main &&
for i in $(test_seq 1 9)
do
- echo $i/3 >>$i.txt
+ echo $i/3 >>$i.txt || return 1
done &&
echo "4main" >>4.txt &&
git commit -a -m "main 1-9 +4" &&
git checkout side &&
for i in $(test_seq 5 9)
do
- echo $i/4 >>$i.txt
+ echo $i/4 >>$i.txt || return 1
done &&
git commit -a -m "side 5-9" &&
git checkout main &&
for i in $(test_seq 1 3)
do
- echo $i/4 >>$i.txt
+ echo $i/4 >>$i.txt || return 1
done &&
git commit -a -m "main 1-3 +4hello" &&
git merge side &&
git checkout side &&
for i in $(test_seq 5 9)
do
- echo $i/5 >>$i.txt
+ echo $i/5 >>$i.txt || return 1
done &&
git commit -a -m "side 5-9" &&
git checkout main &&
for i in $(test_seq 1 3)
do
- echo $i/4 >>$i.txt
+ echo $i/4 >>$i.txt || return 1
done &&
git commit -a -m "main 1-3" &&
git merge side &&
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9 10 11 12
- do
- echo $i
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 >file &&
git update-index --add file &&
- for i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12
- do
- echo $i
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12 >file &&
cat file >expect &&
git diff >O0.diff &&
'
test_expect_success 'git apply --numstat - < patch patch' '
- for i in 1 2; do echo "1 1 text"; done >expect &&
+ cat >expect <<-\EOF &&
+ 1 1 text
+ 1 1 text
+ EOF
git apply --numstat - < patch patch >actual &&
test_cmp expect actual
'
test_expect_success setup '
- for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
+ test_write_lines a b c d e f g h i j k l m n >file1 &&
perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
git add file1 file2 &&
git commit -m initial &&
git tag initial &&
- for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
+ test_write_lines a b c g h i J K L m o n p q >file1 &&
perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
git commit -a -m second &&
. ./test-lib.sh
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
- do
- echo $i
- done >file1 &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >file1 &&
cat file1 >saved.file1 &&
git update-index --add file1 &&
git commit -m initial &&
- for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
- do
- echo $i
- done >file1 &&
+ test_write_lines 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21 >file1 &&
git diff >patch.1 &&
cat file1 >clean &&
- for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
- do
- echo $i
- done >expected &&
+ test_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21 >expected &&
mv file1 file2 &&
git update-index --add --remove file1 file2 &&
mv saved.file1 file1 &&
git update-index --add --remove file1 file2 &&
- for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
- do
- echo $i
- done >file1 &&
+ test_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21 >file1 &&
cat file1 >saved.file1
'
. ./test-lib.sh
test_expect_success setup '
- {
- echo; echo;
- echo A; echo B; echo C;
- echo;
- } >file1 &&
+ test_write_lines "" "" A B C "" >file1 &&
cat file1 >file1.orig &&
{
cat file1 &&
echo Oops, should not have succeeded
false
else
- status=$?
- echo "Status was $status"
+ status=$? &&
+ echo "Status was $status" &&
if test -f .git/index.lock
then
echo Oops, should not have crashed
test_might_fail git config --unset core.whitespace &&
rm -f .gitattributes &&
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
- { echo a; echo b; echo c; } >expect &&
- { cat expect; echo; } >one &&
+ test_write_lines a b c >expect &&
+ { cat expect && echo; } >one &&
git diff -- one >patch &&
git checkout one &&
'
test_expect_success 'blank at EOF with --whitespace=fix (2)' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
- { echo a; echo c; } >expect &&
- { cat expect; echo; echo; } >one &&
+ test_write_lines a b >expect &&
+ { cat expect && test_write_lines "" ""; } >one &&
git diff -- one >patch &&
git checkout one &&
'
test_expect_success 'blank at EOF with --whitespace=fix (3)' '
- { echo a; echo b; echo; } >one &&
+ test_write_lines a b "" >one &&
git add one &&
- { echo a; echo c; echo; } >expect &&
- { cat expect; echo; echo; } >one &&
+ test_write_lines a c "" >expect &&
+ { cat expect && test_write_lines "" ""; } >one &&
git diff -- one >patch &&
git checkout one &&
'
test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
- { echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+ test_write_lines a b "" "" "" "" "" d >one &&
git add one &&
- { echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+ test_write_lines a b "" "" "" "" "" "" d >expect &&
cp expect one &&
git diff -- one >patch &&
'
test_expect_success 'blank at EOF with --whitespace=warn' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
echo >>one &&
cat one >expect &&
'
test_expect_success 'blank at EOF with --whitespace=error' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
cat one >expect &&
echo >>one &&
'
test_expect_success 'blank but not empty at EOF' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
echo " " >>one &&
cat one >expect &&
'
test_expect_success 'applying beyond EOF requires one non-blank context line' '
- { echo; echo; echo; echo; } >one &&
+ test_write_lines "" "" "" "" >one &&
git add one &&
- { echo b; } >>one &&
+ echo b >>one &&
git diff -- one >patch &&
git checkout one &&
- { echo a; echo; } >one &&
+ test_write_lines a "" >one &&
cp one expect &&
test_must_fail git apply --whitespace=fix patch &&
test_cmp expect one &&
test_expect_success 'tons of blanks at EOF should not apply' '
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
- echo; echo; echo; echo;
+ test_write_lines "" "" "" "" || return 1
done >one &&
git add one &&
echo a >>one &&
'
test_expect_success 'two missing blank lines at end with --whitespace=fix' '
- { echo a; echo; echo b; echo c; } >one &&
+ test_write_lines a "" b c >one &&
cp one no-blank-lines &&
- { echo; echo; } >>one &&
+ test_write_lines "" "" >>one &&
git add one &&
echo d >>one &&
cp one expect &&
'
test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
- { echo a; echo; } >one &&
+ test_write_lines a "" >one &&
git add one &&
- { echo b; echo a; echo; } >one &&
+ test_write_lines b a "" >one &&
cp one expect &&
git diff -- one >patch &&
echo a >one &&
'
test_expect_success 'shrink file with tons of missing blanks at end of file' '
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
cp one no-blank-lines &&
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
- echo; echo; echo; echo;
+ test_write_lines "" "" "" "" || return 1
done >>one &&
git add one &&
echo a >one &&
'
test_expect_success 'missing blanks at EOF must only match blank lines' '
- { echo a; echo b; } >one &&
+ test_write_lines a b >one &&
git add one &&
- { echo c; echo d; } >>one &&
+ test_write_lines c d >>one &&
git diff -- one >patch &&
echo a >one &&
git add one &&
echo d >>one &&
git diff -- one >patch &&
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
cp one expect &&
- { echo; echo d; } >>expect &&
+ test_write_lines "" d >>expect &&
git add one &&
git apply --whitespace=fix patch &&
echo d >>one &&
cp one expect &&
git diff -- one >patch &&
- { echo a; echo b; echo c; } >one &&
+ test_write_lines a b c >one &&
git add one &&
git checkout-index -f one &&
git add file &&
# file-0 is full of whitespace breakages
- for l in a bb c d eeee f ggg h
- do
- echo "$l "
- done >file-0 &&
+ printf "%s \n" a bb c d eeee f ggg h >file-0 &&
# patch-0 creates a whitespace broken file
cat file-0 >file &&
git add empty &&
test_tick &&
git commit -m initial &&
- for i in a b c d e
- do
- echo $i
- done >empty &&
+ git commit --allow-empty -m "empty commit" &&
+ git format-patch --always HEAD~ >empty.patch &&
+ test_write_lines a b c d e >empty &&
cat empty >expect &&
git diff |
sed -e "/^diff --git/d" \
'
test_expect_success 'apply empty' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply patch0 &&
test_cmp expect empty
'
+test_expect_success 'apply empty patch fails' '
+ test_when_finished "git reset --hard" &&
+ test_must_fail git apply empty.patch &&
+ test_must_fail git apply - </dev/null
+'
+
+test_expect_success 'apply with --allow-empty succeeds' '
+ test_when_finished "git reset --hard" &&
+ git apply --allow-empty empty.patch &&
+ git apply --allow-empty - </dev/null
+'
+
test_expect_success 'apply --index empty' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply --index patch0 &&
test_cmp expect empty &&
git diff --exit-code
'
test_expect_success 'apply create' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply patch1 &&
test_cmp expect missing
'
test_expect_success 'apply --index create' '
- git reset --hard &&
rm -f missing &&
+ test_when_finished "git reset --hard" &&
git apply --index patch1 &&
test_cmp expect missing &&
git diff --exit-code
}
test_expect_success setup '
- for i in a b c d e f g h i j k l m
- do
- echo $i
- done >same_fn &&
+ test_write_lines a b c d e f g h i j k l m >same_fn &&
cp same_fn other_fn &&
git add same_fn other_fn &&
git commit -m initial
x=1 &&
while test $x -lt $n
do
- printf "%63s%d\n" "" $x >>after
- x=$(( $x + 1 ))
+ printf "%63s%d\n" "" $x >>after &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "\t%s\n" d e f >>after &&
test_expect_code 1 git diff --no-index before after >patch2.patch.raw &&
x=1 &&
while test $x -lt $n
do
- printf "%63s%d\n" "" $x >>expect-2
- x=$(( $x + 1 ))
+ printf "%63s%d\n" "" $x >>expect-2 &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "%64s\n" d e f >>expect-2 &&
x=0 &&
while test $x -lt $n
do
- printf "%63s%02d\n" "" $x >>after
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>after &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "\t%s\n" d e f >>after &&
test_expect_code 1 git diff --no-index before after >patch3.patch.raw &&
x=0 &&
while test $x -lt $n
do
- printf "%63s%02d\n" "" $x >>expect-3
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>expect-3 &&
+ x=$(( $x + 1 )) || return 1
done &&
printf "%64s\n" d e f >>expect-3 &&
x=0 &&
while test $x -lt 50
do
- printf "\t%02d\n" $x >>before
- x=$(( $x + 1 ))
+ printf "\t%02d\n" $x >>before &&
+ x=$(( $x + 1 )) || return 1
done &&
cat before >after &&
printf "%64s\n" a b c >>after &&
while test $x -lt 100
do
- printf "\t%02d\n" $x >>before
- printf "\t%02d\n" $x >>after
- x=$(( $x + 1 ))
+ printf "\t%02d\n" $x >>before &&
+ printf "\t%02d\n" $x >>after &&
+ x=$(( $x + 1 )) || return 1
done &&
test_expect_code 1 git diff --no-index before after >patch4.patch.raw &&
sed -e "s/before/test-4/" -e "s/after/test-4/" patch4.patch.raw >patch4.patch &&
x=0 &&
while test $x -lt 50
do
- printf "%63s%02d\n" "" $x >>test-4
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>test-4 &&
+ x=$(( $x + 1 )) || return 1
done &&
cat test-4 >expect-4 &&
printf "%64s\n" a b c >>expect-4 &&
while test $x -lt 100
do
- printf "%63s%02d\n" "" $x >>test-4
- printf "%63s%02d\n" "" $x >>expect-4
- x=$(( $x + 1 ))
+ printf "%63s%02d\n" "" $x >>test-4 &&
+ printf "%63s%02d\n" "" $x >>expect-4 &&
+ x=$(( $x + 1 )) || return 1
done &&
git config core.whitespace tab-in-indent,tabwidth=63 &&
git format-patch --stdout first | sed -e "1d"
} | append_cr >patch1-crlf.eml &&
{
- printf "%255s\\n" ""
+ printf "%255s\\n" "" &&
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Two" &&
echo "X-Fake-Field: Line Three" &&
git format-patch -M --stdout lorem^ >rename-add.patch &&
+ git checkout -b empty-commit &&
+ git commit -m "empty commit" --allow-empty &&
+
+ : >empty.patch &&
+ git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
# reset time
sane_unset test_tick &&
test_tick
git -C client am ../patch
'
+test_expect_success 'an empty input file is error regardless of --empty option' '
+ test_when_finished "git am --abort || :" &&
+ test_must_fail git am --empty=drop empty.patch 2>actual &&
+ echo "Patch format detection failed." >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+ test_when_finished "git am --abort || :" &&
+ git checkout empty-commit^ &&
+ test_must_fail git am --empty empty-commit.patch 2>err &&
+ echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+ test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+ test_when_finished "git am --abort || :" &&
+ test_must_fail git am empty-commit.patch >err &&
+ grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+ test_when_finished "git am --abort || :" &&
+ test_must_fail git am --empty=stop empty-commit.patch >err &&
+ grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+ git am --empty=drop empty-commit.patch >output &&
+ git rev-parse empty-commit^ >expected &&
+ git rev-parse HEAD >actual &&
+ test_cmp expected actual &&
+ grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+ git am --empty=keep empty-commit.patch >output &&
+ test_path_is_missing .git/rebase-apply &&
+ git show empty-commit --format="%B" >expected &&
+ git show HEAD --format="%B" >actual &&
+ grep -f actual expected &&
+ grep "Creating an empty commit: empty commit" output
+'
+
+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 &&
+ 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 &&
+ git rev-parse empty-commit^ >expected &&
+ git rev-parse HEAD >actual &&
+ test_cmp expected actual
+'
+
+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 &&
+ 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 &&
+ test_path_is_missing .git/rebase-apply &&
+ git show empty-commit --format="%B" >expected &&
+ git show HEAD --format="%B" >actual &&
+ grep -f actual expected
+'
+
+test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' '
+ git checkout empty-commit^ &&
+ test_must_fail git am empty-commit.patch >err &&
+ : >empty-file &&
+ git add empty-file &&
+ git am --allow-empty &&
+ git show empty-commit --format="%B" >expected &&
+ git show HEAD --format="%B" >actual &&
+ grep -f actual expected &&
+ git diff HEAD^..HEAD --name-only
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+ test_when_finished "git am --abort || :" &&
+ git rev-parse HEAD >expected &&
+ test_must_fail git am seq.patch &&
+ test_must_fail git am --allow-empty >err &&
+ ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+ git rev-parse HEAD >actual &&
+ test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+ test_when_finished "git am --abort || :" &&
+ git rev-parse HEAD >expected &&
+ test_must_fail git am -3 seq.patch &&
+ test_must_fail git am --allow-empty >err &&
+ ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+ git rev-parse HEAD >actual &&
+ test_cmp actual expected
+'
+
test_done
. ./test-lib.sh
test_expect_success setup '
- for i in a b c d e f g
- do
- echo $i
- done >file-1 &&
+ test_write_lines a b c d e f g >file-1 &&
cp file-1 file-2 &&
test_tick &&
git add file-1 file-2 &&
test_must_fail git am$with3 000[1245]-*.patch &&
git log --pretty=tformat:%s >actual &&
- for i in 3 2 initial
- do
- echo $i
- done >expect &&
+ test_write_lines 3 2 initial >expect &&
test_cmp expect actual
'
test_expect_success 'diff-filter=M' '
- actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
- expect=$(echo second) &&
- verbose test "$actual" = "$expect"
+ git log --pretty="format:%s" --diff-filter=M HEAD >actual &&
+ printf "second" >expect &&
+ test_cmp expect actual
'
test_expect_success 'diff-filter=D' '
- actual=$(git log --no-renames --pretty="format:%s" --diff-filter=D HEAD) &&
- expect=$(echo sixth ; echo third) &&
- verbose test "$actual" = "$expect"
+ git log --no-renames --pretty="format:%s" --diff-filter=D HEAD >actual &&
+ printf "sixth\nthird" >expect &&
+ test_cmp expect actual
'
test_expect_success 'diff-filter=R' '
- actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
- expect=$(echo third) &&
- verbose test "$actual" = "$expect"
+ git log -M --pretty="format:%s" --diff-filter=R HEAD >actual &&
+ printf "third" >expect &&
+ test_cmp expect actual
'
test_expect_success 'diff-filter=C' '
- actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
- expect=$(echo fourth) &&
- verbose test "$actual" = "$expect"
+ git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual &&
+ printf "fourth" >expect &&
+ test_cmp expect actual
'
test_expect_success 'git log --follow' '
- actual=$(git log --follow --pretty="format:%s" ichi) &&
- expect=$(echo third ; echo second ; echo initial) &&
- verbose test "$actual" = "$expect"
+ git log --follow --pretty="format:%s" ichi >actual &&
+ printf "third\nsecond\ninitial" >expect &&
+ test_cmp expect actual
'
test_expect_success 'git config log.follow works like --follow' '
test_config log.follow true &&
- actual=$(git log --pretty="format:%s" ichi) &&
- expect=$(echo third ; echo second ; echo initial) &&
- verbose test "$actual" = "$expect"
+ git log --pretty="format:%s" ichi >actual &&
+ printf "third\nsecond\ninitial" >expect &&
+ test_cmp expect actual
'
test_expect_success 'git config log.follow does not die with multiple paths' '
test_expect_success 'git config log.follow is overridden by --no-follow' '
test_config log.follow true &&
- actual=$(git log --no-follow --pretty="format:%s" ichi) &&
- expect="third" &&
- verbose test "$actual" = "$expect"
+ git log --no-follow --pretty="format:%s" ichi >actual &&
+ printf "third" >expect &&
+ test_cmp expect actual
'
# Note that these commits are intentionally listed out of order.
test_cmp expect actual &&
# POSIX extended
- git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
+ git -c grep.patternType=extended log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
test_cmp expect actual &&
# PCRE
test_cmp expect actual
'
+test_expect_success 'set up commits with different authors' '
+ git checkout --orphan authors &&
+ test_commit --author "Jim <jim@example.com>" jim_1 &&
+ test_commit --author "Val <val@example.com>" val_1 &&
+ test_commit --author "Val <val@example.com>" val_2 &&
+ test_commit --author "Jim <jim@example.com>" jim_2 &&
+ test_commit --author "Val <val@example.com>" val_3 &&
+ test_commit --author "Jim <jim@example.com>" jim_3
+'
+
+test_expect_success 'log --invert-grep --grep --author' '
+ cat >expect <<-\EOF &&
+ val_3
+ val_1
+ EOF
+ git log --format=%s --author=Val --grep 2 --invert-grep >actual &&
+ test_cmp expect actual
+'
+
test_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_expect_success 'patch-id output is well-formed' '
- git log -p -1 | git patch-id >output &&
+ git log -p -1 >log.output &&
+ git patch-id <log.output >output &&
grep "^$OID_REGEX $(git rev-parse HEAD)$" output
'
calc_patch_id () {
patch_name="$1"
shift
- git patch-id "$@" |
- sed "s/ .*//" >patch-id_"$patch_name" &&
+ git patch-id "$@" >patch-id.output &&
+ sed "s/ .*//" patch-id.output >patch-id_"$patch_name" &&
test_line_count -gt 0 patch-id_"$patch_name"
}
}
get_patch_id () {
- get_top_diff "$1" | calc_patch_id "$@"
+ get_top_diff "$1" >top-diff.output &&
+ calc_patch_id <top-diff.output "$@"
}
test_expect_success 'patch-id detects equality' '
test_expect_success 'patch-id supports git-format-patch output' '
get_patch_id main &&
git checkout same &&
- git format-patch -1 --stdout | calc_patch_id same &&
+ git format-patch -1 --stdout >format-patch.output &&
+ calc_patch_id same <format-patch.output &&
test_cmp patch-id_main patch-id_same &&
- set $(git format-patch -1 --stdout | git patch-id) &&
+ set $(git patch-id <format-patch.output) &&
test "$2" = $(git rev-parse HEAD)
'
test_expect_success 'whitespace is irrelevant in footer' '
get_patch_id main &&
git checkout same &&
- git format-patch -1 --stdout | sed "s/ \$//" | calc_patch_id same &&
+ git format-patch -1 --stdout >format-patch.output &&
+ sed "s/ \$//" format-patch.output | calc_patch_id same &&
test_cmp patch-id_main patch-id_same
'
shift
name="order-${1}-$relevant"
shift
- get_top_diff "main" | calc_patch_id "$name" "$@" &&
+ get_top_diff "main" >top-diff.output &&
+ calc_patch_id <top-diff.output "$name" "$@" &&
git checkout same &&
- git format-patch -1 --stdout -O foo-then-bar |
- calc_patch_id "ordered-$name" "$@" &&
+ git format-patch -1 --stdout -O foo-then-bar >format-patch.output &&
+ calc_patch_id <format-patch.output "ordered-$name" "$@" &&
cmp_patch_id $relevant "$name" "ordered-$name"
}
test_expect_success 'patch-id supports git-format-patch MIME output' '
get_patch_id main &&
git checkout same &&
- git format-patch -1 --attach --stdout | calc_patch_id same &&
+ git format-patch -1 --attach --stdout >format-patch.output &&
+ calc_patch_id <format-patch.output same &&
test_cmp patch-id_main patch-id_same
'
else
: >expect-contains-bad
fi &&
- echo "$hash $desc"
+ echo "$hash $desc" || return 1
done >expect &&
test_path_exists expect-contains-good &&
test_path_exists expect-contains-bad &&
test_seq 1000 > c.c &&
git add c.c &&
git commit -m "modify many lines" &&
- git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
+ git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c || return 1; done)
'
test_expect_success '-s shows only line-log commits' '
test_expect_success 'git log with broken author email' '
{
- echo commit $(cat broken_email.hash)
- echo "Author: A U Thor <author@example.com>"
- echo "Date: Thu Apr 7 15:13:13 2005 -0700"
- echo
+ echo commit $(cat broken_email.hash) &&
+ echo "Author: A U Thor <author@example.com>" &&
+ echo "Date: Thu Apr 7 15:13:13 2005 -0700" &&
+ echo &&
echo " foo"
} >expect.out &&
cd empty &&
for i in $(test_seq 1 6)
do
- git commit --allow-empty -m "$i"
+ git commit --allow-empty -m "$i" || return 1
done &&
# Generate Bloom filters for empty commits 1-6, two at a time.
test_filter_computed 2 trace.event &&
test_filter_not_computed 4 trace.event &&
test_filter_trunc_empty 2 trace.event &&
- test_filter_trunc_large 0 trace.event
+ test_filter_trunc_large 0 trace.event || return 1
done &&
# Finally, make sure that once all commits have filters, that
path=$(get_pax_header $header path) &&
if test -n "$path"
then
- mv "$data" "$path"
+ mv "$data" "$path" || exit 1
fi
fi
done
for depth in 1 2 3 4 5
do
mkdir $p &&
- cd $p
+ cd $p || exit 1
done &&
echo text >file_with_long_path
) &&
printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
printf "A not substituted O" >a/substfile2 &&
(p=long_path_to_a_file && cd a &&
- for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
+ for depth in 1 2 3 4 5; do mkdir $p && cd $p || exit 1; done &&
echo text >file_with_long_path)
'
do
for b in 0 1 2 3 4 5 6 7 8 9 a b c d e f
do
- : >00/$a$b
+ : >00/$a$b || return 1
done
done &&
git add 00 &&
do
for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f
do
- echo "040000 tree $subtree $c$d"
+ echo "040000 tree $subtree $c$d" || return 1
done
done >tree &&
tree=$(git mktree <tree) &&
# create tree containing 65500 entries of that blob
for i in $(test_seq 1 65500)
do
- echo "100644 blob $blob $i"
+ echo "100644 blob $blob $i" || return 1
done >tree &&
tree=$(git mktree <tree) &&
do
git mailinfo mboxrd/msg mboxrd/patch \
<mboxrd/$i >mboxrd/out &&
- test_cmp "$DATA/${i}mboxrd" mboxrd/msg
+ test_cmp "$DATA/${i}mboxrd" mboxrd/msg || return 1
done &&
sp=" " &&
echo "From " >expect &&
for i in 0 1 2 3 4 5 6 7 8 9
do
o=$(echo $j$i | git hash-object -w --stdin) &&
- echo "100644 $o 0 $j$i"
+ echo "100644 $o 0 $j$i" || return 1
done
done >LIST &&
rm -f .git/index &&
ST=$(git write-tree) &&
git rev-list --objects "$LIST" "$LI" "$ST" >actual &&
PACK5=$( git pack-objects test-5 <actual ) &&
- PACK6=$( (
- echo "$LIST"
- echo "$LI"
- echo "$ST"
- ) | git pack-objects test-6 ) &&
+ PACK6=$( test_write_lines "$LIST" "$LI" "$ST" | git pack-objects test-6 ) &&
test_create_repo test-5 &&
(
cd test-5 &&
for i in 0 1 2 3 4 5 6 7 8 9
do
o=$(echo $j$i | git hash-object -w --stdin) &&
- echo "100644 $o 0 $j$i"
+ echo "100644 $o 0 $j$i" || return 1
done
done >LIST &&
rm -f .git/index &&
ST=$(git write-tree) &&
git rev-list --objects "$LIST" "$LI" "$ST" >actual &&
PACK5=$( git pack-objects test-5 <actual ) &&
- PACK6=$( (
- echo "$LIST"
- echo "$LI"
- echo "$ST"
- ) | git pack-objects test-6 ) &&
+ PACK6=$( test_write_lines "$LIST" "$LI" "$ST" | git pack-objects test-6 ) &&
test_create_repo test-7 &&
(
cd test-7 &&
for id in A B C
do
git pack-objects .git/objects/pack/pack-$id \
- --incremental --revs <<-EOF
+ --incremental --revs <<-EOF || exit 1
refs/tags/$id
EOF
done &&
i=1 &&
while test $i -le 100
do
- iii=$(printf "%03i" $i)
+ iii=$(printf "%03i" $i) &&
test-tool genrandom "bar" 200 > wide_delta_$iii &&
test-tool genrandom "baz $iii" 50 >> wide_delta_$iii &&
test-tool genrandom "foo"$i 100 > deep_delta_$iii &&
#
test_expect_success \
'setup base' \
- 'for a in a b c d e f g h i; do echo $a >>text; done &&
+ 'test_write_lines a b c d e f g h i >text &&
echo side >side &&
git update-index --add text side &&
A=$(echo A | git commit-tree $(git write-tree)) &&
git add "file$i" &&
test_tick &&
git commit -m "$i" &&
- git tag "tag$i"
+ git tag "tag$i" || return 1
done &&
obj=$(git rev-parse --verify tag3) &&
fanout=$(expr "$obj" : "\(..\)") &&
test_when_finished "rm -f .git/objects/pack/*.keep" &&
for i in .git/objects/pack/*.pack
do
- >${i%.pack}.keep
+ >${i%.pack}.keep || return 1
done &&
reusable_pack --honor-pack-keep >empty.pack &&
git index-pack empty.pack &&
git commit -m $i &&
cur=$(git rev-parse HEAD^{tree}) &&
{
- test -n "$prev" && echo "-$prev"
- echo $cur
+ if test -n "$prev"
+ then
+ echo "-$prev"
+ fi &&
+ echo $cur &&
echo "$(git rev-parse :file) file"
} | git pack-objects --stdout >tmp &&
git index-pack --stdin --fix-thin <tmp || return 1
git init r1 &&
for n in 1 2 3 4 5
do
- echo "This is file: $n" > r1/file.$n
- git -C r1 add file.$n
- git -C r1 commit -m "$n"
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
done
'
git init r2 &&
for n in 1000 10000
do
- printf "%"$n"s" X > r2/large.$n
- git -C r2 add large.$n
- git -C r2 commit -m "$n"
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n" || return 1
done
'
mkdir r3/dir1 &&
for n in sparse1 sparse2
do
- echo "This is file: $n" > r3/$n
- git -C r3 add $n
- echo "This is file: dir1/$n" > r3/dir1/$n
- git -C r3 add dir1/$n
+ echo "This is file: $n" > r3/$n &&
+ git -C r3 add $n &&
+ echo "This is file: dir1/$n" > r3/dir1/$n &&
+ git -C r3 add dir1/$n || return 1
done &&
git -C r3 commit -m "sparse" &&
echo dir1/ >pattern1 &&
mkdir r4/dir1 &&
for n in sparse1 sparse2
do
- echo "This is file: $n" > r4/$n
- git -C r4 add $n
- echo "This is file: dir1/$n" > r4/dir1/$n
- git -C r4 add dir1/$n
+ echo "This is file: $n" > r4/$n &&
+ git -C r4 add $n &&
+ echo "This is file: dir1/$n" > r4/dir1/$n &&
+ git -C r4 add dir1/$n || return 1
done &&
echo dir1/ >r4/pattern &&
git -C r4 add pattern &&
for id in `cat expected | sed "s|..|&/|"`
do
- rm r1/.git/objects/$id
+ rm r1/.git/objects/$id || return 1
done
'
for i in $(test_seq 3)
do
test_commit $i &&
- git branch commits/$i
+ git branch commits/$i || return 1
done &&
git repack
'
for i in $(test_seq 4 5)
do
test_commit $i &&
- git branch commits/$i
+ git branch commits/$i || return 1
done &&
git reset --hard commits/2 &&
for i in $(test_seq 6 7)
do
test_commit $i &&
- git branch commits/$i
+ git branch commits/$i || return 1
done &&
git reset --hard commits/2 &&
git merge commits/4 &&
test_commit initial &&
for i in $(test_seq 1 5)
do
- generate_objects $i
+ generate_objects $i || return 1
done &&
commit_and_list_objects
'
test_expect_success 'add more objects' '
for i in $(test_seq 6 10)
do
- generate_objects $i
+ generate_objects $i || return 1
done &&
commit_and_list_objects
'
do
generate_objects $j &&
commit_and_list_objects &&
- git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list
+ git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list || return 1
done
'
mkdir objects64/pack &&
for i in $(test_seq 1 11)
do
- generate_objects 11
+ generate_objects 11 || return 1
done &&
commit_and_list_objects &&
pack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&
git update-index --add large_file.txt &&
for i in $(test_seq 1 20)
do
- test_commit $i
+ test_commit $i || exit 1
done &&
git branch A HEAD &&
git branch B HEAD~8 &&
for j in $(test_seq 1 3)
do
mkdir f$i/f$j &&
- echo $j >f$i/f$j/data.txt
+ echo $j >f$i/f$j/data.txt || return 1
done
done &&
git add . &&
do
git checkout -b topic$i main &&
echo change-$i >f$i/f$i/data.txt &&
- git commit -a -m "Changed f$i/f$i/data.txt"
+ git commit -a -m "Changed f$i/f$i/data.txt" || return 1
done &&
cat >packinput.txt <<-EOF &&
topic1
test_path_exists $rev &&
test_index_pack "$conf" --no-rev-index &&
- test_path_is_missing $rev
+ test_path_is_missing $rev || return 1
done
'
while [ $cur -le 10 ]; do
add A$cur $(eval echo \$A$prev) &&
prev=$cur &&
- cur=$(($cur+1))
+ cur=$(($cur+1)) || return 1
done &&
add B1 $A1 &&
git update-ref refs/heads/A "$ATIP" &&
while [ $cur -le 65 ]; do
add B$cur $(eval echo \$B$prev) &&
prev=$cur &&
- cur=$(($cur+1))
+ cur=$(($cur+1)) || return 1
done
'
test_expect_success 'setup tests for the --stdin parameter' '
for head in C D E F
do
- add $head
+ add $head || return 1
done &&
for head in A B C D E F
do
- git tag $head $head
+ git tag $head $head || return 1
done &&
cat >input <<-\EOF &&
refs/heads/C
for i in 0 1 2 3 4 5 6 7 8 9; do
for j in 0 1 2 3 4 5 6 7 8 9; do
for k in 0 1 2 3 4 5 6 7 8 9; do
- echo "$branchprefix$i$j$k" >> .git/packed-refs
+ echo "$branchprefix$i$j$k" >> .git/packed-refs || return 1
done
done
done &&
(
cd test &&
git tag -a -m "Some tag" some-tag main &&
- exit_with=true &&
for type in commit tag tree blob
do
if test "$type" = "blob"
push origin $oid:dst 2>err &&
test_i18ngrep "error: The destination you" err &&
test_i18ngrep ! "hint: Did you mean" err ||
- exit_with=false
- done &&
- $exit_with
+ exit 1
+ done
)
'
git config branch.main.remote two &&
git config branch.main.merge refs/heads/one &&
mkdir -p .git/remotes &&
- {
- echo "URL: ../two/.git/"
- echo "Pull: refs/heads/main:refs/heads/two"
- echo "Pull: refs/heads/one:refs/heads/one"
- } >.git/remotes/two
+ cat >.git/remotes/two <<-\EOF
+ URL: ../two/.git/
+ Pull: refs/heads/main:refs/heads/two
+ Pull: refs/heads/one:refs/heads/one
+ EOF
) &&
git clone . bundle &&
git clone . seven
main_in_two=$(cd ../two && git rev-parse main) &&
one_in_two=$(cd ../two && git rev-parse one) &&
{
- echo "$one_in_two "
+ echo "$one_in_two " &&
echo "$main_in_two not-for-merge"
} >expected &&
cut -f -2 .git/FETCH_HEAD >actual &&
git bundle list-heads bundle5 >actual &&
for h in HEAD refs/heads/main
do
- echo "$(git rev-parse --verify $h) $h"
+ echo "$(git rev-parse --verify $h) $h" || return 1
done >expect &&
test_cmp expect actual
remotes="$remotes config-glob" &&
mkdir -p .git/remotes &&
- {
- echo "URL: ../.git/"
- echo "Pull: refs/heads/main:remotes/rem/main"
- echo "Pull: refs/heads/one:remotes/rem/one"
- echo "Pull: two:remotes/rem/two"
- echo "Pull: refs/heads/three:remotes/rem/three"
- } >.git/remotes/remote-explicit &&
+ cat >.git/remotes/remote-explicit <<-\EOF &&
+ URL: ../.git/
+ Pull: refs/heads/main:remotes/rem/main
+ Pull: refs/heads/one:remotes/rem/one
+ Pull: two:remotes/rem/two
+ Pull: refs/heads/three:remotes/rem/three
+ EOF
remotes="$remotes remote-explicit" &&
- {
- echo "URL: ../.git/"
- echo "Pull: refs/heads/*:refs/remotes/rem/*"
- } >.git/remotes/remote-glob &&
+ cat >.git/remotes/remote-glob <<-\EOF &&
+ URL: ../.git/
+ Pull: refs/heads/*:refs/remotes/rem/*
+ EOF
remotes="$remotes remote-glob" &&
mkdir -p .git/branches &&
git config branch.br-$remote-merge.merge refs/heads/three &&
git config branch.br-$remote-octopus.remote $remote &&
git config branch.br-$remote-octopus.merge refs/heads/one &&
- git config --add branch.br-$remote-octopus.merge two
+ git config --add branch.br-$remote-octopus.merge two || return 1
done &&
build_script sed_script
'
cp "$expect_r" expect_r &&
convert_expected expect_r sed_script &&
{
- echo "# $cmd"
- set x $cmd; shift
- git symbolic-ref HEAD refs/heads/$1 ; shift
- rm -f .git/FETCH_HEAD
+ echo "# $cmd" &&
+ set x $cmd && shift &&
+ git symbolic-ref HEAD refs/heads/$1 && shift &&
+ rm -f .git/FETCH_HEAD &&
git for-each-ref \
refs/heads refs/remotes/rem refs/tags |
while read val type refname
do
- git update-ref -d "$refname" "$val"
- done
- git fetch "$@" >/dev/null
+ git update-ref -d "$refname" "$val" || return 1
+ done &&
+ git fetch "$@" >/dev/null &&
cat .git/FETCH_HEAD
} >"$actual_f" &&
git show-ref >"$actual_r" &&
git pull ../testrepo main &&
git tag -m "annotated" tag &&
git for-each-ref >tmp1 &&
- (
- cat tmp1
- sed -n "s|refs/heads/main$|refs/remotes/origin/main|p" tmp1
- ) |
+ sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
sort -k 3 >../expect
) &&
git init dst &&
test_done
fi
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping test; dumb HTTP protocol not supported with reftable.'
+ test_done
+fi
+
LIB_HTTPD_DAV=t
. "$TEST_DIRECTORY"/lib-httpd.sh
ROOT_PATH="$PWD"
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping test; dumb HTTP protocol not supported with reftable.'
+ test_done
+fi
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
git init client &&
for i in $(test_seq 7)
do
- test_commit -C client c$i
+ test_commit -C client c$i || return 1
done &&
# We send: "c7" (skip 1) "c5" (skip 2) "c2" (skip 4). After that, since
git init client &&
for i in $(test_seq 11)
do
- test_commit -C client c$i
+ test_commit -C client c$i || return 1
done &&
git -C client checkout c5 &&
test_commit -C client c5side &&
for i in $(test_seq 8)
do
git -C client checkout --orphan b$i &&
- test_commit -C client b$i.c0
+ test_commit -C client b$i.c0 || return 1
done &&
for j in $(test_seq 19)
do
for i in $(test_seq 8)
do
git -C client checkout b$i &&
- test_commit -C client b$i.c$j
+ test_commit -C client b$i.c$j || return 1
done
done &&
# should still send the others (in this test, just check b2).
for i in $(test_seq 0 8)
do
- have_not_sent b1.c$i
+ have_not_sent b1.c$i || return 1
done &&
have_sent b2.c1 b2.c0
'
check_config_missing other2
'
+test_expect_success 'fetch --set-upstream with a detached HEAD' '
+ git checkout HEAD^0 &&
+ test_when_finished "git checkout -" &&
+ cat >expect <<-\EOF &&
+ warning: could not set upstream of HEAD to '"'"'main'"'"' from '"'"'upstream'"'"' when it does not point to any branch.
+ EOF
+ git fetch --set-upstream upstream main 2>actual.raw &&
+ grep ^warning: actual.raw >actual &&
+ test_cmp expect actual
+'
+
# tests for pull --set-upstream
test_expect_success 'setup bare parent pull' '
check_config_missing other2
'
+test_expect_success 'pull --set-upstream with a detached HEAD' '
+ git checkout HEAD^0 &&
+ test_when_finished "git checkout -" &&
+ cat >expect <<-\EOF &&
+ warning: could not set upstream of HEAD to '"'"'main'"'"' from '"'"'upstream'"'"' when it does not point to any branch.
+ EOF
+ git pull --no-rebase --set-upstream upstream main 2>actual.raw &&
+ grep ^warning: actual.raw >actual &&
+ test_cmp expect actual
+'
+
test_done
hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
{
printf "%s %s refs/heads/newbranch\\0report-status object-format=%s\\n" \
- "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize_raw
+ "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize_raw &&
printf 0000 &&
echo "$hash_next" | git pack-objects --stdout
} >push_body &&
test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
{
- printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw
+ printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw &&
printf "0000"
} >input &&
fake_nc "$GIT_DAEMON_HOST_PORT" <input >output &&
test_expect_success 'set up many-ref tests' '
{
- nr=1000
+ nr=1000 &&
while test $nr -lt 2000
do
- nr=$(( $nr + 1 ))
- echo "create refs/heads/b/$nr $COMMIT3"
+ nr=$(( $nr + 1 )) &&
+ echo "create refs/heads/b/$nr $COMMIT3" || return 1
done
} | git update-ref --stdin
'
test_expect_success 'clone -c can set multi-keys' '
rm -rf child &&
git clone -c core.foo=bar -c core.foo=baz . child &&
- { echo bar; echo baz; } >expect &&
+ test_write_lines bar baz >expect &&
git --git-dir=child/.git config --get-all core.foo >actual &&
test_cmp expect actual
'
git init src &&
for n in 1 2 3 4
do
- echo "This is file: $n" > src/file.$n.txt
- git -C src add file.$n.txt
- git -C src commit -m "file $n"
- git -C src ls-files -s file.$n.txt >>temp
+ echo "This is file: $n" > src/file.$n.txt &&
+ git -C src add file.$n.txt &&
+ git -C src commit -m "file $n" &&
+ git -C src ls-files -s file.$n.txt >>temp || return 1
done &&
awk -f print_2.awk <temp | sort >expect_1.oids &&
test_line_count = 4 expect_1.oids
git -C src remote add srv "file://$(pwd)/srv.bare" &&
for x in a b c d e
do
- echo "Mod file.1.txt $x" >>src/file.1.txt
- git -C src add file.1.txt
- git -C src commit -m "mod $x"
+ echo "Mod file.1.txt $x" >>src/file.1.txt &&
+ git -C src add file.1.txt &&
+ git -C src commit -m "mod $x" || return 1
done &&
git -C src blame main -- file.1.txt >expect.blame &&
git -C src push -u srv main
test_expect_success 'push new commits to server for file.2.txt' '
for x in a b c d e f
do
- echo "Mod file.2.txt $x" >>src/file.2.txt
- git -C src add file.2.txt
- git -C src commit -m "mod $x"
+ echo "Mod file.2.txt $x" >>src/file.2.txt &&
+ git -C src add file.2.txt &&
+ git -C src commit -m "mod $x" || return 1
done &&
git -C src push -u srv main
'
test_expect_success 'push new commits to server for file.3.txt' '
for x in a b c d e f
do
- echo "Mod file.3.txt $x" >>src/file.3.txt
- git -C src add file.3.txt
- git -C src commit -m "mod $x"
+ echo "Mod file.3.txt $x" >>src/file.3.txt &&
+ git -C src add file.3.txt &&
+ git -C src commit -m "mod $x" || return 1
done &&
git -C src push -u srv main
'
for i in $(test_seq 1 100)
do
echo "make the tree big" >server/file$i &&
- git -C server add file$i
+ git -C server add file$i || return 1
done &&
git -C server commit -m "initial" &&
git clone --bare --filter=tree:0 "file://$(pwd)/server" client &&
for i in $(test_seq 10)
do
echo "this is a line" >>"$SERVER/foo.txt" &&
- echo "this is another line" >>"$SERVER/have.txt"
+ echo "this is another line" >>"$SERVER/have.txt" || return 1
done &&
git -C "$SERVER" add foo.txt have.txt &&
git -C "$SERVER" commit -m bar &&
echo "data 0" &&
echo "M 644 inline bla.txt" &&
echo "data 4" &&
- echo "bla"
+ echo "bla" || return 1
done | git -C "$HTTPD_DOCUMENT_ROOT_PATH/big" fast-import &&
GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git \
then
>h2found
fi
- fi
+ fi || return 1
done &&
test -f hfound &&
test -f h2found &&
. ./test-lib.sh
test_expect_success 'setup' '
- for n in 1 2 3 4 5 ; do \
- echo $n > a ; \
- git add a ; \
- git commit -m "$n" ; \
+ for n in 1 2 3 4 5 ; do
+ echo $n > a &&
+ git add a &&
+ git commit -m "$n" || return 1
done
'
git checkout -b root$i five &&
test_commit $i &&
roots="$roots root$i" ||
- return
+ return 1
done &&
git checkout main &&
test_tick &&
test_tick_keep=$test_tick &&
for i in 1 2 3 4 5 6 7 8; do
- test_tick=$test_tick_keep
- test_commit t$i
+ test_tick=$test_tick_keep &&
+ test_commit t$i || return 1
done &&
git rev-list t1^! --not t$i >result &&
test_must_be_empty result
'
test_expect_success 'rev-list D..M' '
- for c in E F G H I J K L M; do echo $c; done >expect &&
+ test_write_lines E F G H I J K L M >expect &&
git rev-list --format=%s D..M |
sed -e "/^commit /d" |
sort >actual &&
'
test_expect_success 'rev-list --ancestry-path D..M' '
- for c in E F H I J L M; do echo $c; done >expect &&
+ test_write_lines E F H I J L M >expect &&
git rev-list --ancestry-path --format=%s D..M |
sed -e "/^commit /d" |
sort >actual &&
'
test_expect_success 'rev-list F...I' '
- for c in F G H I; do echo $c; done >expect &&
+ test_write_lines F G H I >expect &&
git rev-list --format=%s F...I |
sed -e "/^commit /d" |
sort >actual &&
'
test_expect_success 'rev-list --ancestry-path F...I' '
- for c in F H I; do echo $c; done >expect &&
+ test_write_lines F H I >expect &&
git rev-list --ancestry-path --format=%s F...I |
sed -e "/^commit /d" |
sort >actual &&
'
test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
- for c in G L; do echo $c; done >expect &&
+ test_write_lines G L >expect &&
git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
sed -e "/^commit /d" |
sort >actual &&
. ./test-lib.sh
test_expect_success 'setup diverging branches' '
- for i in 1 2 3 4 5 6 7 8 9 10; do
- echo $i
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&
git add file &&
git commit -m base &&
git tag base &&
test_tick &&
git commit --allow-empty -m "$i" &&
commit=$(git rev-parse --verify HEAD) &&
- printf "$commit " >>.git/info/grafts
+ printf "$commit " >>.git/info/grafts || return 1
done
'
git init r1 &&
for n in 1 2 3 4 5
do
- echo "This is file: $n" > r1/file.$n
- git -C r1 add file.$n
- git -C r1 commit -m "$n"
+ echo "This is file: $n" > r1/file.$n &&
+ git -C r1 add file.$n &&
+ git -C r1 commit -m "$n" || return 1
done
'
git init r2 &&
for n in 1000 10000
do
- printf "%"$n"s" X > r2/large.$n
- git -C r2 add large.$n
- git -C r2 commit -m "$n"
+ printf "%"$n"s" X > r2/large.$n &&
+ git -C r2 add large.$n &&
+ git -C r2 commit -m "$n" || return 1
done
'
mkdir r3/dir1 &&
for n in sparse1 sparse2
do
- echo "This is file: $n" > r3/$n
- git -C r3 add $n
- echo "This is file: dir1/$n" > r3/dir1/$n
- git -C r3 add dir1/$n
+ echo "This is file: $n" > r3/$n &&
+ git -C r3 add $n &&
+ echo "This is file: dir1/$n" > r3/dir1/$n &&
+ git -C r3 add dir1/$n || return 1
done &&
git -C r3 commit -m "sparse" &&
echo dir1/ >pattern1 &&
for id in `cat expected | sed "s|..|&/|"`
do
- rm r1/.git/objects/$id
+ rm r1/.git/objects/$id || return 1
done &&
git -C r1 rev-list --quiet --missing=print --objects HEAD >revs &&
>expect.unsorted &&
for rev in $(git rev-list --all)
do
- git name-rev $rev >>expect.unsorted
+ git name-rev $rev >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
git name-rev --all >actual.unsorted &&
for rev in $(git rev-list --all)
do
name=$(git name-rev --name-only $rev) &&
- echo "$rev ($name)" >>expect.unsorted
+ echo "$rev ($name)" >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
git rev-list --all | git name-rev --stdin >actual.unsorted &&
committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
data <<EOF
commit #$i
-EOF"
- test $i = 1 && echo "from refs/heads/main^0"
- i=$(($i + 1))
+EOF" &&
+ if test $i = 1
+ then
+ echo "from refs/heads/main^0"
+ fi &&
+ i=$(($i + 1)) || return 1
done | git fast-import &&
git checkout main &&
git tag far-far-away HEAD^ &&
fi &&
: >$p &&
git add $p &&
- git commit -m $p
+ git commit -m $p || return 1
done &&
git log --oneline --format=%s >actual &&
cat <<EOF >expect &&
while test $i -gt 9
do
echo " $i" &&
- i=$(($i-1))
+ i=$(($i-1)) || return 1
done &&
echo " ..."
} >expected &&
test_cmp expected .git/MERGE_MSG
'
+test_expect_success 'merge --into-name=<name>' '
+ test_when_finished "git checkout main" &&
+ git checkout -B side main &&
+ git commit --allow-empty -m "One step ahead" &&
+
+ git checkout --detach main &&
+ git merge --no-ff side &&
+ git show -s --format="%s" >full.0 &&
+ head -n1 full.0 >actual &&
+ # expect that HEAD is shown as-is
+ grep -e "Merge branch .side. into HEAD$" actual &&
+
+ git reset --hard main &&
+ git merge --no-ff --into-name=main side &&
+ git show -s --format="%s" >full.1 &&
+ head -n1 full.1 >actual &&
+ # expect that we pretend to be merging to main, that is suppressed
+ grep -e "Merge branch .side.$" actual &&
+
+ git checkout -b throwaway main &&
+ git merge --no-ff --into-name=main side &&
+ git show -s --format="%s" >full.2 &&
+ head -n1 full.2 >actual &&
+ # expect that we pretend to be merging to main, that is suppressed
+ grep -e "Merge branch .side.$" actual
+'
+
test_expect_success 'merge.suppressDest configuration' '
+ test_when_finished "git checkout main" &&
git checkout -B side main &&
git commit --allow-empty -m "One step ahead" &&
git checkout main &&
git -c merge.suppressDest="ma?*[rn]" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
head -n1 full.3 >actual &&
grep -e "Merge branch .side." actual &&
- ! grep -e " into main$" actual
+ ! grep -e " into main$" actual &&
+
+ git checkout --detach HEAD &&
+ git -c merge.suppressDest="main" fmt-merge-msg <.git/FETCH_HEAD >full.4 &&
+ head -n1 full.4 >actual &&
+ grep -e "Merge branch .side. into HEAD$" actual &&
+
+ git -c merge.suppressDest="main" fmt-merge-msg \
+ --into-name=main <.git/FETCH_HEAD >full.5 &&
+ head -n1 full.5 >actual &&
+ grep -e "Merge branch .side." actual &&
+ ! grep -e " into main$" actual &&
+ ! grep -e " into HEAD$" actual
'
test_done
'
test_expect_success '%(raw:size) with --shell' '
- git for-each-ref --format="%(raw:size)" | while read line
- do
- echo "'\''$line'\''" >>expect
- done &&
+ git for-each-ref --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect &&
git for-each-ref --format="%(raw:size)" --shell >actual &&
test_cmp expect actual
'
echo "${pair#*=}" >expect &&
git for-each-ref --format="${pair%=*}" \
refs/heads/main >actual &&
- test_cmp expect actual
+ test_cmp expect actual || exit 1
done &&
git branch push-simple &&
git config branch.push-simple.pushRemote from &&
test_expect_success merge '
- {
- echo "binary -merge"
- echo "union merge=union"
- } >.gitattributes &&
+ cat >.gitattributes <<-\EOF &&
+ binary -merge
+ union merge=union
+ EOF
if git merge main
then
echo Oops, should not have succeeded
false
else
- git ls-files -s >current
+ git ls-files -s >current &&
test_cmp expect current
fi
'
echo Oops, should not have succeeded
false
else
- git ls-files -s >current
+ git ls-files -s >current &&
test_cmp expect current
fi
'
test_expect_success setup '
s="1 2 3 4 5 6 7 8" &&
- for i in $s; do echo $i; done >hello &&
+ test_write_lines $s >hello &&
git add hello &&
git commit -m initial &&
git checkout -b side &&
git add hello &&
git commit -m second &&
git checkout main &&
- for i in mundo $s; do echo $i; done >hello &&
+ test_write_lines mundo $s >hello &&
git add hello &&
git commit -m main
test_expect_success 'subtree available and works like recursive' '
git merge -s subtree side &&
- for i in mundo $s world; do echo $i; done >expect &&
+ test_write_lines mundo $s world >expect &&
test_cmp expect hello
'
: >file2 &&
git add file2 &&
git commit -m b2 &&
- {
- echo "100755 $H 2 file2"
- echo "100644 $H 3 file2"
- } >expect
+ cat >expect <<-EOF
+ 100755 $H 2 file2
+ 100644 $H 3 file2
+ EOF
'
do_both_modes () {
test_might_fail git branch -D test$n &&
git reset --hard initial &&
for i in $(count $n); do
- make_text $i initial initial >$i
+ make_text $i initial initial >$i || return 1
done &&
git add . &&
git commit -m add=$n &&
for i in $(count $n); do
- make_text $i changed initial >$i
+ make_text $i changed initial >$i || return 1
done &&
git commit -a -m change=$n &&
git checkout -b test$n HEAD^ &&
for i in $(count $n); do
- git rm $i
- make_text $i initial changed >$i.moved
+ git rm $i &&
+ make_text $i initial changed >$i.moved || return 1
done &&
git add . &&
git commit -m change+rename=$n &&
git reset --hard initial &&
for i in $(count 200); do
- make_text foo bar baz >$i
+ make_text foo bar baz >$i || return 1
done &&
git add . &&
git commit -m create-files &&
cd basic-rename &&
ten="0 1 2 3 4 5 6 7 8 9" &&
- for i in $ten
- do
- echo line $i in a sample file
- done >one &&
- for i in $ten
- do
- echo line $i in another sample file
- done >two &&
+ printf "line %d in a sample file\n" $ten >one &&
+ printf "line %d in another sample file\n" $ten >two &&
git add one two &&
test_tick && git commit -m initial &&
cd rename-modify &&
ten="0 1 2 3 4 5 6 7 8 9" &&
- for i in $ten
- do
- echo line $i in a sample file
- done >one &&
- for i in $ten
- do
- echo line $i in another sample file
- done >two &&
+ printf "line %d in a sample file\n" $ten >one &&
+ printf "line %d in another sample file\n" $ten >two &&
git add one two &&
test_tick && git commit -m initial &&
cd nested_conflicts &&
# Create some related files now
- for i in $(test_seq 1 10)
- do
- echo Random base content line $i
- done >initial &&
+ printf "Random base content line %d\n" $(test_seq 1 10) >initial &&
cp initial b_L1 &&
cp initial b_R1 &&
cd virtual_merge_base_has_nested_conflicts &&
# Create some related files now
- for i in $(test_seq 1 10)
- do
- echo Random base content line $i
- done >content &&
+ printf "Random base content line %d\n" $(test_seq 1 10) >content &&
# Setup original commit
git add content &&
. ./test-lib.sh
test_expect_success setup '
- for i in 1 2 3 4 5 6 7 8 9
- do
- echo "$i"
- done >file &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >file &&
git add file &&
cp file elif &&
git commit -m initial &&
# more trees than static slots used by oid_to_hex()
for commit in $c0 $c2 $c4 $c5 $c6 $c7
do
- git rev-parse "$commit^{tree}"
+ git rev-parse "$commit^{tree}" || return 1
done >trees &&
# ignore the return code; it only fails because the input is weird...
do
test_commit "1-$i" &&
git branch -f commit-1-$i &&
- git tag -a -m "1-$i" tag-1-$i commit-1-$i
+ git tag -a -m "1-$i" tag-1-$i commit-1-$i || return 1
done &&
for j in $(test_seq 1 9)
do
do
git merge commit-$j-$i -m "$x-$i" &&
git branch -f commit-$x-$i &&
- git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i
+ git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i || return 1
done
done &&
git commit-graph write --reachable &&
git log -1 \
--format="format:tag: tagging %h (%s, %cd)%n" \
--date=format:%Y-%m-%d >expected &&
- test_when_finished "git tag -d tag_with_reflog" &&
- git tag --create-reflog tag_with_reflog &&
- git reflog exists refs/tags/tag_with_reflog &&
- sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual &&
+ test_when_finished "git tag -d tag_with_reflog1" &&
+ git tag --create-reflog tag_with_reflog1 &&
+ git reflog exists refs/tags/tag_with_reflog1 &&
+ test-tool ref-store main for-each-reflog-ent refs/tags/tag_with_reflog1 | sed -e "s/^.* //" >actual &&
test_cmp expected actual
'
git log -1 \
--format="format:tag: tagging %h (%s, %cd)%n" \
--date=format:%Y-%m-%d >expected &&
- test_when_finished "git tag -d tag_with_reflog" &&
- git tag -m "annotated tag" --create-reflog tag_with_reflog &&
- git reflog exists refs/tags/tag_with_reflog &&
- sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual &&
+ test_when_finished "git tag -d tag_with_reflog2" &&
+ git tag -m "annotated tag" --create-reflog tag_with_reflog2 &&
+ git reflog exists refs/tags/tag_with_reflog2 &&
+ test-tool ref-store main for-each-reflog-ent refs/tags/tag_with_reflog2 | sed -e "s/^.* //" >actual &&
test_cmp expected actual
'
'
test_expect_success 'option core.logAllRefUpdates=always creates reflog' '
- test_when_finished "git tag -d tag_with_reflog" &&
+ test_when_finished "git tag -d tag_with_reflog3" &&
test_config core.logAllRefUpdates always &&
- git tag tag_with_reflog &&
- git reflog exists refs/tags/tag_with_reflog
+ git tag tag_with_reflog3 &&
+ git reflog exists refs/tags/tag_with_reflog3
'
test_expect_success 'listing all tags if one exists should succeed' '
committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
data <<EOF
commit #$i
-EOF"
- test $i = 1 && echo "from refs/heads/main^0"
- i=$(($i + 1))
+EOF" &&
+ if test $i = 1
+ then
+ echo "from refs/heads/main^0"
+ fi &&
+ i=$(($i + 1)) || return 1
done | git fast-import &&
git checkout main &&
git tag far-far-away HEAD^ &&
test_expect_success 'add a directory outside the work tree' '(
cd tester &&
- d1="$(cd .. ; pwd)" &&
+ d1="$(cd .. && pwd)" &&
test_must_fail git add "$d1"
)'
. ./test-lib.sh
test_expect_success setup '
- for i in 1 2 3; do echo line $i; done >file1 &&
+ printf "line %d\n" 1 2 3 >file1 &&
cat file1 >file2 &&
git add file1 file2 &&
test_tick &&
test_expect_success 'commit a file whose name is a dash' '
git reset --hard &&
- for i in 1 2 3 4 5
- do
- echo $i
- done >./- &&
+ test_write_lines 1 2 3 4 5 >./- &&
git add ./- &&
test_tick &&
git commit -m "add dash" >output </dev/null &&
test_commit rebase-b b bb &&
for i in $(test_seq 1 13)
do
- test_commit rebase-$i c $i
+ test_commit rebase-$i c $i || return 1
done &&
git checkout main &&
You are in the middle of an am session.
The current patch is empty.
(use "git am --skip" to skip this patch)
+ (use "git am --allow-empty" to record this patch as an empty commit)
(use "git am --abort" to restore the original branch)
nothing to commit (use -u to show untracked files)
Acked-by: Johan
Reviewed-by: Peff
EOF
- { echo; echo "Acked-by: Johan"; } |
+ { echo && echo "Acked-by: Johan"; } |
git -c "trailer.Acked-by.ifexists=addifdifferent" interpret-trailers \
--trailer "Reviewed-by: Peff" --trailer "Acked-by: Johan" >actual &&
test_cmp expected actual
git config core.preloadIndex $preload_val &&
if test $preload_val = true
then
- GIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX
+ GIT_TEST_PRELOAD_INDEX=$preload_val && export GIT_TEST_PRELOAD_INDEX
else
sane_unset GIT_TEST_PRELOAD_INDEX
fi
# 256 near-identical stanzas...
for i in $(test_seq 1 256); do
for j in 1 2 3 4 5; do
- echo $i-$j
+ echo $i-$j || return 1
done
done >file &&
git add file &&
refs="" &&
while test $i -le 30
do
- refs="$refs c$i"
- i=$(expr $i + 1)
+ refs="$refs c$i" &&
+ i=$(expr $i + 1) || return 1
done &&
git merge $refs &&
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
echo $i > $i.c &&
git add $i.c &&
git commit -m $i &&
- git tag $i
+ git tag $i || return 1
done &&
git reset --hard A &&
for i in F G H I
echo $i > $i.c &&
git add $i.c &&
git commit -m $i &&
- git tag $i
+ git tag $i || return 1
done
'
. ./test-lib.sh
. "${TEST_DIRECTORY}/lib-bitmap.sh"
. "${TEST_DIRECTORY}/lib-midx.sh"
+. "${TEST_DIRECTORY}/lib-terminal.sh"
commit_and_pack () {
test_commit "$@" 1>&2 &&
rm alt_objects/pack/$base_name.keep
else
touch alt_objects/pack/$base_name.keep
- fi
+ fi || return 1
done &&
git repack -a -d &&
test_no_missing_in_packs
)
'
+test_expect_success '--write-midx -b packs non-kept objects' '
+ GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+ git repack --write-midx -a -b &&
+ test_subcommand_inexact git pack-objects --honor-pack-keep <trace.txt
+'
+
+test_expect_success TTY '--quiet disables progress' '
+ test_terminal env GIT_PROGRESS_DELAY=0 \
+ git -C midx repack -ad --quiet --write-midx 2>stderr &&
+ test_must_be_empty stderr
+'
+
test_done
EOF
test_expect_success setup '
- {
- echo foo mmap bar
- echo foo_mmap bar
- echo foo_mmap bar mmap
- echo foo mmap bar_mmap
- echo foo_mmap bar mmap baz
- } >file &&
- {
- echo Hello world
- echo HeLLo world
- echo Hello_world
- echo HeLLo_world
- } >hello_world &&
- {
- echo "a+b*c"
- echo "a+bc"
- echo "abc"
- } >ab &&
- {
- echo d &&
- echo 0
- } >d0 &&
+ cat >file <<-\EOF &&
+ foo mmap bar
+ foo_mmap bar
+ foo_mmap bar mmap
+ foo mmap bar_mmap
+ foo_mmap bar mmap baz
+ EOF
+ cat >hello_world <<-\EOF &&
+ Hello world
+ HeLLo world
+ Hello_world
+ HeLLo_world
+ EOF
+ cat >ab <<-\EOF &&
+ a+b*c
+ a+bc
+ abc
+ EOF
+ cat >d0 <<-\EOF &&
+ d
+ 0
+ EOF
echo vvv >v &&
echo ww w >w &&
echo x x xx x >x &&
echo vvv >t/v &&
mkdir t/a &&
echo vvv >t/a/v &&
- {
- echo "line without leading space1"
- echo " line with leading space1"
- echo " line with leading space2"
- echo " line with leading space3"
- echo "line without leading space2"
- } >space &&
+ qz_to_tab_space >space <<-\EOF &&
+ line without leading space1
+ Zline with leading space1
+ Zline with leading space2
+ Zline with leading space3
+ line without leading space2
+ EOF
cat >hello.ps1 <<-\EOF &&
# No-op.
function dummy() {}
esac
test_expect_success "grep -w $L" '
- {
- echo ${HC}file:1:foo mmap bar
- echo ${HC}file:3:foo_mmap bar mmap
- echo ${HC}file:4:foo mmap bar_mmap
- echo ${HC}file:5:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo mmap bar
+ ${HC}file:3:foo_mmap bar mmap
+ ${HC}file:4:foo mmap bar_mmap
+ ${HC}file:5:foo_mmap bar mmap baz
+ EOF
git -c grep.linenumber=false grep -n -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column)" '
- {
- echo ${HC}file:5:foo mmap bar
- echo ${HC}file:14:foo_mmap bar mmap
- echo ${HC}file:5:foo mmap bar_mmap
- echo ${HC}file:14:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:5:foo mmap bar
+ ${HC}file:14:foo_mmap bar mmap
+ ${HC}file:5:foo mmap bar_mmap
+ ${HC}file:14:foo_mmap bar mmap baz
+ EOF
git grep --column -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column, extended OR)" '
- {
- echo ${HC}file:14:foo_mmap bar mmap
- echo ${HC}file:19:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:14:foo_mmap bar mmap
+ ${HC}file:19:foo_mmap bar mmap baz
+ EOF
git grep --column -w -e mmap$ --or -e baz $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column, --invert-match)" '
- {
- echo ${HC}file:1:foo mmap bar
- echo ${HC}file:1:foo_mmap bar
- echo ${HC}file:1:foo_mmap bar mmap
- echo ${HC}file:1:foo mmap bar_mmap
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo mmap bar
+ ${HC}file:1:foo_mmap bar
+ ${HC}file:1:foo_mmap bar mmap
+ ${HC}file:1:foo mmap bar_mmap
+ EOF
git grep --column --invert-match -w -e baz $H -- file >actual &&
test_cmp expected actual
'
test_expect_success "grep $L (with --column, --invert-match, extended OR)" '
- {
- echo ${HC}hello_world:6:HeLLo_world
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}hello_world:6:HeLLo_world
+ EOF
git grep --column --invert-match -e ll --or --not -e _ $H -- hello_world \
>actual &&
test_cmp expected actual
'
test_expect_success "grep $L (with --column, --invert-match, extended AND)" '
- {
- echo ${HC}hello_world:3:Hello world
- echo ${HC}hello_world:3:Hello_world
- echo ${HC}hello_world:6:HeLLo_world
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}hello_world:3:Hello world
+ ${HC}hello_world:3:Hello_world
+ ${HC}hello_world:6:HeLLo_world
+ EOF
git grep --column --invert-match --not -e _ --and --not -e ll $H -- hello_world \
>actual &&
test_cmp expected actual
'
test_expect_success "grep $L (with --column, double-negation)" '
- {
- echo ${HC}file:1:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo_mmap bar mmap baz
+ EOF
git grep --column --not \( --not -e foo --or --not -e baz \) $H -- file \
>actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --column, -C)" '
- {
- echo ${HC}file:5:foo mmap bar
- echo ${HC}file-foo_mmap bar
- echo ${HC}file:14:foo_mmap bar mmap
- echo ${HC}file:5:foo mmap bar_mmap
- echo ${HC}file:14:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:5:foo mmap bar
+ ${HC}file-foo_mmap bar
+ ${HC}file:14:foo_mmap bar mmap
+ ${HC}file:5:foo mmap bar_mmap
+ ${HC}file:14:foo_mmap bar mmap baz
+ EOF
git grep --column -w -C1 -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with --line-number, --column)" '
- {
- echo ${HC}file:1:5:foo mmap bar
- echo ${HC}file:3:14:foo_mmap bar mmap
- echo ${HC}file:4:5:foo mmap bar_mmap
- echo ${HC}file:5:14:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:5:foo mmap bar
+ ${HC}file:3:14:foo_mmap bar mmap
+ ${HC}file:4:5:foo mmap bar_mmap
+ ${HC}file:5:14:foo_mmap bar mmap baz
+ EOF
git grep -n --column -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (with non-extended patterns, --column)" '
- {
- echo ${HC}file:5:foo mmap bar
- echo ${HC}file:10:foo_mmap bar
- echo ${HC}file:10:foo_mmap bar mmap
- echo ${HC}file:5:foo mmap bar_mmap
- echo ${HC}file:10:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:5:foo mmap bar
+ ${HC}file:10:foo_mmap bar
+ ${HC}file:10:foo_mmap bar mmap
+ ${HC}file:5:foo mmap bar_mmap
+ ${HC}file:10:foo_mmap bar mmap baz
+ EOF
git grep --column -w -e bar -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L" '
- {
- echo ${HC}file:1:foo mmap bar
- echo ${HC}file:3:foo_mmap bar mmap
- echo ${HC}file:4:foo mmap bar_mmap
- echo ${HC}file:5:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:foo mmap bar
+ ${HC}file:3:foo_mmap bar mmap
+ ${HC}file:4:foo mmap bar_mmap
+ ${HC}file:5:foo_mmap bar mmap baz
+ EOF
git -c grep.linenumber=true grep -w -e mmap $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L" '
- {
- echo ${HC}file:foo mmap bar
- echo ${HC}file:foo_mmap bar mmap
- echo ${HC}file:foo mmap bar_mmap
- echo ${HC}file:foo_mmap bar mmap baz
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:foo mmap bar
+ ${HC}file:foo_mmap bar mmap
+ ${HC}file:foo mmap bar_mmap
+ ${HC}file:foo_mmap bar mmap baz
+ EOF
git -c grep.linenumber=true grep --no-line-number -w -e mmap $H >actual &&
test_cmp expected actual
'
'
test_expect_success "grep -w $L (x)" '
- {
- echo ${HC}x:1:x x xx x
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}x:1:x x xx x
+ EOF
git grep -n -w -e "x xx* x" $H >actual &&
test_cmp expected actual
'
test_expect_success "grep -w $L (y-1)" '
- {
- echo ${HC}y:1:y yy
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}y:1:y yy
+ EOF
git grep -n -w -e "^y" $H >actual &&
test_cmp expected actual
'
'
test_expect_success "grep $L (with --column, --only-matching)" '
- {
- echo ${HC}file:1:5:mmap
- echo ${HC}file:2:5:mmap
- echo ${HC}file:3:5:mmap
- echo ${HC}file:3:13:mmap
- echo ${HC}file:4:5:mmap
- echo ${HC}file:4:13:mmap
- echo ${HC}file:5:5:mmap
- echo ${HC}file:5:13:mmap
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}file:1:5:mmap
+ ${HC}file:2:5:mmap
+ ${HC}file:3:5:mmap
+ ${HC}file:3:13:mmap
+ ${HC}file:4:5:mmap
+ ${HC}file:4:13:mmap
+ ${HC}file:5:5:mmap
+ ${HC}file:5:13:mmap
+ EOF
git grep --column -n -o -e mmap $H >actual &&
test_cmp expected actual
'
'
test_expect_success "grep --max-depth -1 $L" '
- {
- echo ${HC}t/a/v:1:vvv
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/a/v:1:vvv
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth -1 -n -e vvv $H >actual &&
test_cmp expected actual &&
git grep --recursive -n -e vvv $H >actual &&
'
test_expect_success "grep --max-depth 0 $L" '
- {
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H >actual &&
'
test_expect_success "grep --max-depth 0 -- '*' $L" '
- {
- echo ${HC}t/a/v:1:vvv
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/a/v:1:vvv
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- "*" >actual &&
'
test_expect_success "grep --max-depth 1 $L" '
- {
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 1 -n -e vvv $H >actual &&
test_cmp expected actual
'
test_expect_success "grep --max-depth 0 -- t $L" '
- {
- echo ${HC}t/v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- t >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- t >actual &&
'
test_expect_success "grep --max-depth 0 -- . t $L" '
- {
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- . t >actual &&
'
test_expect_success "grep --max-depth 0 -- t . $L" '
- {
- echo ${HC}t/v:1:vvv
- echo ${HC}v:1:vvv
- } >expected &&
+ cat >expected <<-EOF &&
+ ${HC}t/v:1:vvv
+ ${HC}v:1:vvv
+ EOF
git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
test_cmp expected actual &&
git grep --no-recursive -n -e vvv $H -- t . >actual &&
'
test_expect_success PCRE 'grep -P -v pattern' '
- {
- echo "ab:a+b*c"
- echo "ab:a+bc"
- } >expected &&
+ cat >expected <<-\EOF &&
+ ab:a+b*c
+ ab:a+bc
+ EOF
git grep -P -v "abc" ab >actual &&
test_cmp expected actual
'
'
test_expect_success PCRE 'grep -P -w pattern' '
- {
- echo "hello_world:Hello world"
- echo "hello_world:HeLLo world"
- } >expected &&
+ cat >expected <<-\EOF &&
+ hello_world:Hello world
+ hello_world:HeLLo world
+ EOF
git grep -P -w "He((?i)ll)o" hello_world >actual &&
test_cmp expected actual
'
'
test_expect_success 'grep -G pattern with grep.patternType=fixed' '
- {
- echo "ab:a+b*c"
- echo "ab:a+bc"
- } >expected &&
+ cat >expected <<-\EOF &&
+ ab:a+b*c
+ ab:a+bc
+ EOF
git \
-c grep.patterntype=fixed \
grep -G "a+b" ab >actual &&
'
test_expect_success 'grep -E pattern with grep.patternType=fixed' '
- {
- echo "ab:a+b*c"
- echo "ab:a+bc"
- echo "ab:abc"
- } >expected &&
+ cat >expected <<-\EOF &&
+ ab:a+b*c
+ ab:a+bc
+ ab:abc
+ EOF
git \
-c grep.patterntype=fixed \
grep -E "a+" ab >actual &&
test_cmp invalid-0xe5 actual
'
+test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep non-literal ASCII from UTF-8' '
+ git grep --perl-regexp -h -o -e ll. file >actual &&
+ echo "lló" >expected &&
+ test_cmp expected actual
+'
+
test_done
test_commit abbrev &&
sha1=$(git rev-parse --verify HEAD) &&
check_abbrev () {
- expect=$1; shift
+ expect=$1 && shift &&
echo $sha1 | cut -c 1-$expect >expect &&
git blame "$@" abbrev.t >actual &&
perl -lne "/[0-9a-f]+/ and print \$&" <actual >actual.sha &&
echo B B B B B >two &&
echo C C C C C >tres &&
echo ABC >mouse &&
- for i in 1 2 3 4 5 6 7 8 9
- do
- echo $i
- done >nine_lines &&
- for i in 1 2 3 4 5 6 7 8 9 a
- do
- echo $i
- done >ten_lines &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 >nine_lines &&
+ test_write_lines 1 2 3 4 5 6 7 8 9 a >ten_lines &&
git add one two tres mouse nine_lines ten_lines &&
test_tick &&
GIT_AUTHOR_NAME=Initial git commit -m Initial &&
echo "$line" >>"$i" &&
git add "$i" &&
test_tick &&
- GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count"
+ GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count" || return 1
done <"a$i"
done &&
do
# Overwrite the files with the final content.
cp b$i $i &&
- git add $i
+ git add $i || return 1
done &&
test_tick &&
mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
for i in a b c ; do \
echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&
- echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \
+ echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t || return 1
done &&
echo "bad delete test" > \
import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
svn mv t native/t &&
for i in a b c
do
- svn mv $i.pm native/$i.pm
+ svn mv $i.pm native/$i.pm || return 1
done &&
echo z >>native/t/c.t &&
poke native/t/c.t &&
rm -rf "$GIT_DIR"/svn &&
for i in $(cat fetch.out)
do
- path=$(expr $i : "\([^:]*\):.*$")
- ref=$(expr $i : "[^:]*:\(refs/remotes/.*\)$")
- if test -z "$ref"; then continue; fi
- if test -n "$path"; then path="/$path"; fi
+ path=${i%%:*} &&
+ ref=${i#*:} &&
+ if test "$ref" = "${ref#refs/remotes/}"; then continue; fi &&
+ if test -n "$path"; then path="/$path"; fi &&
mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
echo "$svnrepo"$path >"$GIT_DIR"/svn/$ref/info/url ||
return 1
test_expect_success 'setup svnrepo' '
for i in aa bb cc dd
do
- svn_cmd mkdir -m $i --username $i "$svnrepo"/$i
+ svn_cmd mkdir -m $i --username $i "$svnrepo"/$i || return 1
done
'
git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
for i in bb ee cc
do
- branch="aa/branches/$i"
- svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch"
+ branch="aa/branches/$i" &&
+ svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch" || return 1
done
'
test_expect_success 'clone an SVN repository with ignored www directory' '
git svn clone --ignore-paths="^www" "$svnrepo" g &&
echo test_qqq > expect &&
- for i in g/*/*.txt; do cat $i >> expect2; done &&
+ for i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
( cd c && git svn fetch --ignore-paths="^www" ) &&
rm expect2 &&
echo test_qqq > expect &&
- for i in c/*/*.txt; do cat $i >> expect2; done &&
+ for i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd c &&
git svn rebase --ignore-paths="^www" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd c &&
git svn rebase --ignore-paths="^www" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd g &&
git svn rebase &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd c &&
git svn rebase --ignore-paths="^www" &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
test_expect_success 'setup svnrepo' '
for i in aa bb cc-sub dd-sub ee-foo ff
do
- svn mkdir -m $i --username $i "$svnrepo"/$i
+ svn mkdir -m $i --username $i "$svnrepo"/$i || return 1
done
'
test_expect_success 'initialize repo' '
for i in a b c d d/e d/e/f "weird file name"
do
- svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
done
'
test_expect_success 'initialize trunk' '
for i in trunk trunk/a trunk/"weird file name"
do
- svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
done
'
test_expect_success 'clone an SVN repository with filter to include qqq directory' '
git svn clone --include-paths="qqq" "$svnrepo" g &&
echo test_qqq > expect &&
- for i in g/*/*.txt; do cat $i >> expect2; done &&
+ for i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
( cd c && git svn fetch --include-paths="qqq" ) &&
rm expect2 &&
echo test_qqq > expect &&
- for i in c/*/*.txt; do cat $i >> expect2; done &&
+ for i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&
test_cmp expect expect2
'
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd c &&
git svn rebase --include-paths="qqq" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd g &&
git svn rebase &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd c &&
git svn rebase --include-paths="qqq" &&
printf "test_qqq\nb\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd g &&
git svn rebase &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
cd c &&
git svn rebase --include-paths="qqq" &&
printf "test_qqq\nb\nygg\n" > expect &&
- for i in */*.txt; do cat $i >> expect2; done &&
+ for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
test_cmp expect2 expect &&
rm expect expect2
)
test_expect_success 'initialize repo' '
for i in a b c d d/e d/e/f "weird file name"
do
- svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
done
'
blob=$(git rev-parse HEAD:one.t) &&
for i in $(test_seq 1024 16384)
do
- echo ":$i $blob"
+ echo ":$i $blob" || return 1
done >>marks
'
)
'
+
+test_expect_success 'fast-export --first-parent outputs all revisions output by revision walk' '
+ git init first-parent &&
+ (
+ cd first-parent &&
+ test_commit A &&
+ git checkout -b topic1 &&
+ test_commit B &&
+ git checkout main &&
+ git merge --no-ff topic1 &&
+
+ git checkout -b topic2 &&
+ test_commit C &&
+ git checkout main &&
+ git merge --no-ff topic2 &&
+
+ test_commit D &&
+
+ git fast-export main -- --first-parent >first-parent-export &&
+ git fast-export main -- --first-parent --reverse >first-parent-reverse-export &&
+ test_cmp first-parent-export first-parent-reverse-export &&
+
+ git init import &&
+ git -C import fast-import <first-parent-export &&
+
+ git log --format="%ad %s" --first-parent main >expected &&
+ git -C import log --format="%ad %s" --all >actual &&
+ test_cmp expected actual &&
+ test_line_count = 4 actual
+ )
+'
+
test_done
'(for dir in A A/B A/B/C A/D E; do
mkdir $dir &&
echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")" &&
- git add $dir
+ git add $dir || exit 1
done) &&
git commit -q -m "deep sub directory structure" &&
git push gitcvs.git >/dev/null &&
test_cmp "$dir/$filename" "../$dir/$filename"; then
:
else
- echo >failure
+ exit 1
fi
- done) &&
- test ! -f failure'
+ done)'
cd "$WORKDIR"
test_expect_success 'cvs update (delete file)' \
for i in 1 2 3 4 5 6 7
do
echo Line $i >>merge &&
- echo Line $i >>expected
+ echo Line $i >>expected || return 1
done &&
echo Line 8 >>expected &&
git add merge &&
cd cvswork &&
GIT_CONFIG="$git_config" cvs annotate merge >../out &&
sed -e "s/ .*//" ../out >../actual &&
- for i in 3 1 1 1 1 1 1 1 2 4; do echo 1.$i; done >../expect &&
+ printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
test_cmp ../expect ../actual
'
cd "$git" &&
git ls-files >lines &&
test_line_count = 8 lines
- )
+ ) || return 1
done
'
. ./lib-git-p4.sh
+CP1252="\223\224"
+
test_expect_success 'start p4d' '
start_p4d
'
p4 submit -d "filek" &&
p4 add -t text+ko fileko &&
p4 submit -d "fileko" &&
+ printf "$CP1252" >fileko_cp1252 &&
+ p4 add -t text+ko fileko_cp1252 &&
+ p4 submit -d "fileko_cp1252" &&
p4 add -t text file_text &&
p4 submit -d "file_text"
)
)
'
+test_expect_success 'check cp1252 smart quote are preserved through RCS keyword processing' '
+ test_when_finished cleanup_git &&
+ git p4 clone --dest="$git" //depot &&
+ (
+ cd "$git" &&
+ printf "$CP1252" >expect &&
+ test_cmp_bin expect fileko_cp1252
+ )
+'
+
test_done
for i in $(test_seq 0 10)
do
p4_add_file "included/x$i" &&
- p4_add_file "excluded/x$i"
+ p4_add_file "excluded/x$i" || return 1
done &&
for i in $(test_seq 0 10)
do
- p4_add_file "excluded/y$i"
+ p4_add_file "excluded/y$i" || return 1
done
'
do
for i in $(test_seq 1 10)
do
- p4_add_file "$p/file$p$i"
+ p4_add_file "$p/file$p$i" || return 1
done
done
'
refs/remotes/remote/branch-in-remote
do
git update-ref $remote_ref main &&
- test_when_finished "git update-ref -d $remote_ref"
+ test_when_finished "git update-ref -d $remote_ref" || return 1
done &&
(
cur= &&
refs/remotes/remote/branch-in-remote
do
git update-ref $remote_ref main &&
- test_when_finished "git update-ref -d $remote_ref"
+ test_when_finished "git update-ref -d $remote_ref" || return 1
done &&
(
cur=mat &&
fi
}
+# Check that the given command was invoked as part of the
+# trace2-format trace on stdin, but without an exact set of
+# arguments.
+#
+# test_subcommand [!] <command> <args>... < <trace>
+#
+# For example, to look for an invocation of "git pack-objects"
+# with the "--honor-pack-keep" argument, use
+#
+# GIT_TRACE2_EVENT=event.log git repack ... &&
+# test_subcommand git pack-objects --honor-pack-keep <event.log
+#
+# If the first parameter passed is !, this instead checks that
+# the given command was not called.
+#
+test_subcommand_inexact () {
+ local negate=
+ if test "$1" = "!"
+ then
+ negate=t
+ shift
+ fi
+
+ local expr=$(printf '"%s".*' "$@")
+ expr="${expr%,}"
+
+ if test -n "$negate"
+ then
+ ! grep "\"event\":\"child_start\".*\[$expr\]"
+ else
+ grep "\"event\":\"child_start\".*\[$expr\]"
+ fi
+}
+
# Check that the given command was invoked as part of the
# trace2-format trace on stdin.
#
#include "cache.h"
#include "tmp-objdir.h"
+#include "chdir-notify.h"
#include "dir.h"
#include "sigchain.h"
#include "string-list.h"
struct tmp_objdir {
struct strbuf path;
struct strvec env;
+ struct object_directory *prev_odb;
+ int will_destroy;
};
/*
if (t == the_tmp_objdir)
the_tmp_objdir = NULL;
+ if (!on_signal && t->prev_odb)
+ restore_primary_odb(t->prev_odb, t->path.buf);
+
/*
* This may use malloc via strbuf_grow(), but we should
* have pre-grown t->path sufficiently so that this
*/
if (!on_signal)
tmp_objdir_free(t);
+
return err;
}
return ret;
}
-struct tmp_objdir *tmp_objdir_create(void)
+struct tmp_objdir *tmp_objdir_create(const char *prefix)
{
static int installed_handlers;
struct tmp_objdir *t;
if (the_tmp_objdir)
BUG("only one tmp_objdir can be used at a time");
- t = xmalloc(sizeof(*t));
+ t = xcalloc(1, sizeof(*t));
strbuf_init(&t->path, 0);
strvec_init(&t->env);
- strbuf_addf(&t->path, "%s/incoming-XXXXXX", get_object_directory());
+ /*
+ * Use a string starting with tmp_ so that the builtin/prune.c code
+ * can recognize any stale objdirs left behind by a crash and delete
+ * them.
+ */
+ strbuf_addf(&t->path, "%s/tmp_objdir-%s-XXXXXX", get_object_directory(), prefix);
/*
* Grow the strbuf beyond any filename we expect to be placed in it.
if (!t)
return 0;
+ if (t->prev_odb) {
+ if (the_repository->objects->odb->will_destroy)
+ BUG("migrating an ODB that was marked for destruction");
+ restore_primary_odb(t->prev_odb, t->path.buf);
+ t->prev_odb = NULL;
+ }
+
strbuf_addbuf(&src, &t->path);
strbuf_addstr(&dst, get_object_directory());
{
add_to_alternates_memory(t->path.buf);
}
+
+void tmp_objdir_replace_primary_odb(struct tmp_objdir *t, int will_destroy)
+{
+ if (t->prev_odb)
+ BUG("the primary object database is already replaced");
+ t->prev_odb = set_temporary_primary_odb(t->path.buf, will_destroy);
+ t->will_destroy = will_destroy;
+}
+
+struct tmp_objdir *tmp_objdir_unapply_primary_odb(void)
+{
+ if (!the_tmp_objdir || !the_tmp_objdir->prev_odb)
+ return NULL;
+
+ restore_primary_odb(the_tmp_objdir->prev_odb, the_tmp_objdir->path.buf);
+ the_tmp_objdir->prev_odb = NULL;
+ return the_tmp_objdir;
+}
+
+void tmp_objdir_reapply_primary_odb(struct tmp_objdir *t, const char *old_cwd,
+ const char *new_cwd)
+{
+ char *path;
+
+ path = reparent_relative_path(old_cwd, new_cwd, t->path.buf);
+ strbuf_reset(&t->path);
+ strbuf_addstr(&t->path, path);
+ free(path);
+ tmp_objdir_replace_primary_odb(t, t->will_destroy);
+}
*
* Example:
*
- * struct tmp_objdir *t = tmp_objdir_create();
+ * struct tmp_objdir *t = tmp_objdir_create("incoming");
* if (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&
* !tmp_objdir_migrate(t))
* printf("success!\n");
struct tmp_objdir;
/*
- * Create a new temporary object directory; returns NULL on failure.
+ * Create a new temporary object directory with the specified prefix;
+ * returns NULL on failure.
*/
-struct tmp_objdir *tmp_objdir_create(void);
+struct tmp_objdir *tmp_objdir_create(const char *prefix);
/*
* Return a list of environment strings, suitable for use with
*/
void tmp_objdir_add_as_alternate(const struct tmp_objdir *);
+/*
+ * Replaces the writable object store in the current process with the temporary
+ * object directory and makes the former main object store an alternate.
+ * If will_destroy is nonzero, the object directory may not be migrated.
+ */
+void tmp_objdir_replace_primary_odb(struct tmp_objdir *, int will_destroy);
+
+/*
+ * If the primary object database was replaced by a temporary object directory,
+ * restore it to its original value while keeping the directory contents around.
+ * Returns NULL if the primary object database was not replaced.
+ */
+struct tmp_objdir *tmp_objdir_unapply_primary_odb(void);
+
+/*
+ * Reapplies the former primary temporary object database, after potentially
+ * changing its relative path.
+ */
+void tmp_objdir_reapply_primary_odb(struct tmp_objdir *, const char *old_cwd,
+ const char *new_cwd);
+
+
#endif /* TMP_OBJDIR_H */
/* ERROR_NOT_UPTODATE_DIR */
"Updating '%s' would lose untracked files in it",
+ /* ERROR_CWD_IN_THE_WAY */
+ "Refusing to remove '%s' since it is the current working directory.",
+
/* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
"Untracked working tree file '%s' would be overwritten by merge.",
msgs[ERROR_NOT_UPTODATE_DIR] =
_("Updating the following directories would lose untracked files in them:\n%s");
+ msgs[ERROR_CWD_IN_THE_WAY] =
+ _("Refusing to remove the current working directory:\n%s");
+
if (!strcmp(cmd, "checkout"))
msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be removed by checkout:\n%%s"
cnt++;
}
- /*
- * Then we need to make sure that we do not lose a locally
- * present file that is not ignored.
- */
+ /* Do not lose a locally present file that is not ignored. */
pathbuf = xstrfmt("%.*s/", namelen, ce->name);
memset(&d, 0, sizeof(d));
free(pathbuf);
if (i)
return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
+
+ /* Do not lose startup_info->original_cwd */
+ if (startup_info->original_cwd &&
+ !strcmp(startup_info->original_cwd, ce->name))
+ return add_rejected_path(o, ERROR_CWD_IN_THE_WAY, ce->name);
+
return cnt;
}
int len;
struct stat st;
- if (o->index_only || !o->update ||
- o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED)
+ if (o->index_only || !o->update)
+ return 0;
+
+ if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED) {
+ /* Avoid nuking startup_info->original_cwd... */
+ if (startup_info->original_cwd &&
+ !strcmp(startup_info->original_cwd, ce->name))
+ return add_rejected_path(o, ERROR_CWD_IN_THE_WAY,
+ ce->name);
+ /* ...but nuke anything else. */
return 0;
+ }
len = check_leading_path(ce->name, ce_namelen(ce), 0);
if (!len)
ERROR_WOULD_OVERWRITE = 0,
ERROR_NOT_UPTODATE_FILE,
ERROR_NOT_UPTODATE_DIR,
+ ERROR_CWD_IN_THE_WAY,
ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
ERROR_BIND_OVERLAP,
}
struct output_state {
- char buffer[8193];
+ /*
+ * We do writes no bigger than LARGE_PACKET_DATA_MAX - 1, because with
+ * sideband-64k the band designator takes up 1 byte of space. Because
+ * relay_pack_data keeps the last byte to itself, we make the buffer 1
+ * byte bigger than the intended maximum write size.
+ */
+ char buffer[(LARGE_PACKET_DATA_MAX - 1) + 1];
int used;
unsigned packfile_uris_started : 1;
unsigned packfile_started : 1;
const struct string_list *uri_protocols)
{
struct child_process pack_objects = CHILD_PROCESS_INIT;
- struct output_state output_state = { { 0 } };
+ struct output_state *output_state = xcalloc(1, sizeof(struct output_state));
char progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
}
if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
int result = relay_pack_data(pack_objects.out,
- &output_state,
+ output_state,
pack_data->use_sideband,
!!uri_protocols);
}
/* flush the data */
- if (output_state.used > 0) {
- send_client_data(1, output_state.buffer, output_state.used,
+ if (output_state->used > 0) {
+ send_client_data(1, output_state->buffer, output_state->used,
pack_data->use_sideband);
fprintf(stderr, "flushed.\n");
}
+ free(output_state);
if (pack_data->use_sideband)
packet_flush(1);
return;
static void show_am_in_progress(struct wt_status *s,
const char *color)
{
+ int am_empty_patch;
+
status_printf_ln(s, color,
_("You are in the middle of an am session."));
if (s->state.am_empty_patch)
status_printf_ln(s, color,
_("The current patch is empty."));
if (s->hints) {
- if (!s->state.am_empty_patch)
+ am_empty_patch = s->state.am_empty_patch;
+ if (!am_empty_patch)
status_printf_ln(s, color,
_(" (fix conflicts and then run \"git am --continue\")"));
status_printf_ln(s, color,
_(" (use \"git am --skip\" to skip this patch)"));
+ if (am_empty_patch)
+ status_printf_ln(s, color,
+ _(" (use \"git am --allow-empty\" to record this patch as an empty commit)"));
status_printf_ln(s, color,
_(" (use \"git am --abort\" to restore the original branch)"));
}