diff options
77 files changed, 1561 insertions, 670 deletions
diff --git a/.gitignore b/.gitignore index bf66648e2c..c188d0b461 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ /git-commit-tree /git-config /git-count-objects +/git-credential /git-credential-cache /git-credential-cache--daemon /git-credential-store @@ -172,7 +173,6 @@ /gitweb/static/gitweb.js /gitweb/static/gitweb.min.* /test-chmtime -/test-credential /test-ctype /test-date /test-delta diff --git a/Documentation/RelNotes/1.7.12.txt b/Documentation/RelNotes/1.7.12.txt index f49eef51b7..4ecaa2f4cb 100644 --- a/Documentation/RelNotes/1.7.12.txt +++ b/Documentation/RelNotes/1.7.12.txt @@ -6,6 +6,16 @@ Updates since v1.7.11 UI, Workflows & Features + * Per-user $HOME/.gitconfig file can optionally be stored in + $HOME/.config/git/config instead, which is in line with XDG. + + * The value of core.attributesfile and core.excludesfile default to + $HOME/.config/attributes and $HOME/.config/ignore respectively when + these files exist. + + * Scripted Porcelain writers now have access to the credential API via + the "git credential" plumbing command. + * "git help" used to always default to "man" format even on platforms where "man" viewer is not widely available. @@ -16,10 +26,22 @@ UI, Workflows & Features turn this off, as a more explicit alternative over use of file:// URL. + * "git fetch" and friends used to say "remote side hung up + unexpectedly" when they failed to get response they expect from the + other side, but one common reason why they don't get expected + response is that the remote repository does not exist or cannot be + read. The error message in this case was updated to give better + hints to the user. + * git native protocol agents learned to show software version over the wire, so that the server log can be examined to see the vintage distribution of clients. + * "git help -w $cmd" can show HTML version of documentation for + "git-$cmd" by setting help.htmlpath to somewhere other than the + default location where the build procedure installs them locally; + the variable can even point at a http:// URL. + * "git rebase -i" learned "-x <cmd>" to insert "exec <cmd>" after each commit in the resulting history. @@ -64,6 +86,42 @@ Unless otherwise noted, all the fixes since v1.7.11 in the maintenance releases are contained in this release (see release notes to them for details). + * We did not have test to make sure "git rebase" without extra options + filters out an empty commit in the original history. + (merge 2b5ba7b mz/empty-rebase-test later to maint). + + * "git fast-export" produced an input stream for fast-import without + properly quoting pathnames when they contain SPs in them. + (merge ff59f6d js/fast-export-paths-with-spaces later to maint). + + * "git checkout --detach", when you are still on an unborn branch, + should be forbidden, but it wasn't. + (merge 8ced1aa cw/no-detaching-an-unborn later to maint). + + * Some implementations of Perl terminates "lines" with CRLF even when + the script is operating on just a sequence of bytes. Make sure to + use "$PERL_PATH", the version of Perl the user told Git to use, in + our tests to avoid unnecessary breakages in tests. + (merge ad78585 vr/use-our-perl-in-tests later to maint). + + * "git blame" did not try to make sure that the abbreviated commit + object names in its output are unique. + (merge b31272f jc/maint-blame-unique-abbrev later to maint). + + * On Cygwin, the platform pread(2) is not thread safe, just like our + own compat/ emulation, and cannot be used in the index-pack + program. Makefile variable NO_THREAD_SAFE_PREAD can be defined to + avoid use of this function in a threaded program. + (merge c0f8654 rj/platform-pread-may-be-thread-unsafe later to maint). + + * "git clone --single-branch" to clone a single branch did not limit + the cloning to the specified branch. + (merge 0ec4b16 nd/clone-single-fix later to maint). + + * "git diff --no-index" did not correctly handle relative paths and + did not correctly give exit codes when run under "--quiet" option. + (merge 304970d th/diff-no-index-fixes later to maint). + * When "git log" gets "--simplify-merges/by-decoration" together with "--first-parent", the combination of these options makes the simplification logic to use in-core commit objects that haven't diff --git a/Documentation/config.txt b/Documentation/config.txt index ccce4f6697..c6ff15e594 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -484,7 +484,9 @@ core.excludesfile:: '.git/info/exclude', git looks into this file for patterns of files which are not meant to be tracked. "`~/`" is expanded to the value of `$HOME` and "`~user/`" to the specified user's - home directory. See linkgit:gitignore[5]. + home directory. Its default value is $XDG_CONFIG_HOME/git/ignore. + If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore + is used instead. See linkgit:gitignore[5]. core.askpass:: Some commands (e.g. svn and http interfaces) that interactively @@ -499,7 +501,9 @@ core.attributesfile:: In addition to '.gitattributes' (per-directory) and '.git/info/attributes', git looks into this file for attributes (see linkgit:gitattributes[5]). Path expansions are made the same - way as for `core.excludesfile`. + way as for `core.excludesfile`. Its default value is + $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME is either not + set or empty, $HOME/.config/git/attributes is used instead. core.editor:: Commands such as `commit` and `tag` that lets you edit @@ -881,7 +885,7 @@ column.ui:: make equal size columns -- + - This option defaults to 'never'. +This option defaults to 'never'. column.branch:: Specify whether to output branch listing in `git branch` in columns. @@ -1721,6 +1725,7 @@ push.default:: no refspec is implied by any of the options given on the command line. Possible values are: + +-- * `nothing` - do not push anything. * `matching` - push all branches having the same name in both ends. This is for those who prepare all the branches into a publishable @@ -1740,12 +1745,13 @@ push.default:: option and is well-suited for beginners. It will become the default in Git 2.0. * `current` - push the current branch to a branch of the same name. - + - The `simple`, `current` and `upstream` modes are for those who want to - push out a single branch after finishing work, even when the other - branches are not yet ready to be pushed out. If you are working with - other people to push into the same shared repository, you would want - to use one of these. +-- ++ +The `simple`, `current` and `upstream` modes are for those who want to +push out a single branch after finishing work, even when the other +branches are not yet ready to be pushed out. If you are working with +other people to push into the same shared repository, you would want +to use one of these. rebase.stat:: Whether to show a diffstat of what changed upstream since the last diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index d9463cb387..2d6ef32a08 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -97,10 +97,11 @@ OPTIONS --global:: For writing options: write to global ~/.gitconfig file rather than - the repository .git/config. + the repository .git/config, write to $XDG_CONFIG_HOME/git/config file + if this file exists and the ~/.gitconfig file doesn't. + -For reading options: read only from global ~/.gitconfig rather than -from all available files. +For reading options: read only from global ~/.gitconfig and from +$XDG_CONFIG_HOME/git/config rather than from all available files. + See also <<FILES>>. @@ -194,7 +195,7 @@ See also <<FILES>>. FILES ----- -If not set explicitly with '--file', there are three files where +If not set explicitly with '--file', there are four files where 'git config' will search for configuration options: $GIT_DIR/config:: @@ -204,6 +205,14 @@ $GIT_DIR/config:: User-specific configuration file. Also called "global" configuration file. +$XDG_CONFIG_HOME/git/config:: + Second user-specific configuration file. If $XDG_CONFIG_HOME is not set + or empty, $HOME/.config/git/config will be used. Any single-valued + variable set in this file will be overwritten by whatever is in + ~/.gitconfig. It is a good idea not to create this file if + you sometimes use older versions of Git, as support for this + file was added fairly recently. + $(prefix)/etc/gitconfig:: System-wide configuration file. diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt new file mode 100644 index 0000000000..a81684e15f --- /dev/null +++ b/Documentation/git-credential.txt @@ -0,0 +1,144 @@ +git-credential(1) +================= + +NAME +---- +git-credential - retrieve and store user credentials + +SYNOPSIS +-------- +------------------ +git credential <fill|approve|reject> +------------------ + +DESCRIPTION +----------- + +Git has an internal interface for storing and retrieving credentials +from system-specific helpers, as well as prompting the user for +usernames and passwords. The git-credential command exposes this +interface to scripts which may want to retrieve, store, or prompt for +credentials in the same manner as git. The design of this scriptable +interface models the internal C API; see +link:technical/api-credentials.txt[the git credential API] for more +background on the concepts. + +git-credential takes an "action" option on the command-line (one of +`fill`, `approve`, or `reject`) and reads a credential description +on stdin (see <<IOFMT,INPUT/OUTPUT FORMAT>>). + +If the action is `fill`, git-credential will attempt to add "username" +and "password" attributes to the description by reading config files, +by contacting any configured credential helpers, or by prompting the +user. The username and password attributes of the credential +description are then printed to stdout together with the attributes +already provided. + +If the action is `approve`, git-credential will send the description +to any configured credential helpers, which may store the credential +for later use. + +If the action is `reject`, git-credential will send the description to +any configured credential helpers, which may erase any stored +credential matching the description. + +If the action is `approve` or `reject`, no output should be emitted. + +TYPICAL USE OF GIT CREDENTIAL +----------------------------- + +An application using git-credential will typically use `git +credential` following these steps: + + 1. Generate a credential description based on the context. ++ +For example, if we want a password for +`https://example.com/foo.git`, we might generate the following +credential description (don't forget the blank line at the end; it +tells `git credential` that the application finished feeding all the +infomation it has): + + protocol=https + host=example.com + path=foo.git + + 2. Ask git-credential to give us a username and password for this + description. This is done by running `git credential fill`, + feeding the description from step (1) to its standard input. The complete + credential description (including the credential per se, i.e. the + login and password) will be produced on standard output, like: + + protocol=https + host=example.com + username=bob + password=secr3t ++ +In most cases, this means the attributes given in the input will be +repeated in the output, but git may also modify the credential +description, for example by removing the `path` attribute when the +protocol is HTTP(s) and `credential.useHttpPath` is false. ++ +If the `git credential` knew about the password, this step may +not have involved the user actually typing this password (the +user may have typed a password to unlock the keychain instead, +or no user interaction was done if the keychain was already +unlocked) before it returned `password=secr3t`. + + 3. Use the credential (e.g., access the URL with the username and + password from step (2)), and see if it's accepted. + + 4. Report on the success or failure of the password. If the + credential allowed the operation to complete successfully, then + it can be marked with an "approve" action to tell `git + credential` to reuse it in its next invocation. If the credential + was rejected during the operation, use the "reject" action so + that `git credential` will ask for a new password in its next + invocation. In either case, `git credential` should be fed with + the credential description obtained from step (2) (which also + contain the ones provided in step (1)). + +[[IOFMT]] +INPUT/OUTPUT FORMAT +------------------- + +`git credential` reads and/or writes (depending on the action used) +credential information in its standard input/output. These information +can correspond either to keys for which `git credential` will obtain +the login/password information (e.g. host, protocol, path), or to the +actual credential data to be obtained (login/password). + +The credential is split into a set of named attributes. +Attributes are provided to the helper, one per line. Each attribute is +specified by a key-value pair, separated by an `=` (equals) sign, +followed by a newline. The key may contain any bytes except `=`, +newline, or NUL. The value may contain any bytes except newline or NUL. +In both cases, all bytes are treated as-is (i.e., there is no quoting, +and one cannot transmit a value with newline or NUL in it). The list of +attributes is terminated by a blank line or end-of-file. +Git will send the following attributes (but may not send all of +them for a given credential; for example, a `host` attribute makes no +sense when dealing with a non-network protocol): + +`protocol`:: + + The protocol over which the credential will be used (e.g., + `https`). + +`host`:: + + The remote hostname for a network credential. + +`path`:: + + The path with which the credential will be used. E.g., for + accessing a remote https repository, this will be the + repository's path on the server. + +`username`:: + + The credential's username, if we already have one (e.g., from a + URL, from the user, or from a previously run helper). + +`password`:: + + The credential's password, if we are asking it to be stored. diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 80120ea14f..e16f3e175b 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -75,6 +75,8 @@ repositories (i.e., attributes of interest to all users) should go into `.gitattributes` files. Attributes that should affect all repositories for a single user should be placed in a file specified by the `core.attributesfile` configuration option (see linkgit:git-config[1]). +Its default value is $XDG_CONFIG_HOME/git/attributes. If $XDG_CONFIG_HOME +is either not set or empty, $HOME/.config/git/attributes is used instead. Attributes for all users on a system should be placed in the `$(prefix)/etc/gitattributes` file. diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 2e7328b830..c1f692a71e 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -50,7 +50,9 @@ the repository but are specific to one user's workflow) should go into the `$GIT_DIR/info/exclude` file. Patterns which a user wants git to ignore in all situations (e.g., backup or temporary files generated by the user's editor of choice) generally go into a file specified by -`core.excludesfile` in the user's `~/.gitconfig`. +`core.excludesfile` in the user's `~/.gitconfig`. Its default value is +$XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty, +$HOME/.config/git/ignore is used instead. The underlying git plumbing tools, such as 'git ls-files' and 'git read-tree', read diff --git a/Documentation/technical/api-credentials.txt b/Documentation/technical/api-credentials.txt index adb6f0c896..5977b58e57 100644 --- a/Documentation/technical/api-credentials.txt +++ b/Documentation/technical/api-credentials.txt @@ -241,42 +241,9 @@ appended to its command line, which is one of: Remove a matching credential, if any, from the helper's storage. The details of the credential will be provided on the helper's stdin -stream. The credential is split into a set of named attributes. -Attributes are provided to the helper, one per line. Each attribute is -specified by a key-value pair, separated by an `=` (equals) sign, -followed by a newline. The key may contain any bytes except `=`, -newline, or NUL. The value may contain any bytes except newline or NUL. -In both cases, all bytes are treated as-is (i.e., there is no quoting, -and one cannot transmit a value with newline or NUL in it). The list of -attributes is terminated by a blank line or end-of-file. - -Git will send the following attributes (but may not send all of -them for a given credential; for example, a `host` attribute makes no -sense when dealing with a non-network protocol): - -`protocol`:: - - The protocol over which the credential will be used (e.g., - `https`). - -`host`:: - - The remote hostname for a network credential. - -`path`:: - - The path with which the credential will be used. E.g., for - accessing a remote https repository, this will be the - repository's path on the server. - -`username`:: - - The credential's username, if we already have one (e.g., from a - URL, from the user, or from a previously run helper). - -`password`:: - - The credential's password, if we are asking it to be stored. +stream. The exact format is the same as the input/output format of the +`git credential` plumbing command (see the section `INPUT/OUTPUT +FORMAT` in linkgit:git-credential[7] for a detailed specification). For a `get` operation, the helper should produce a list of attributes on stdout in the same format. A helper is free to produce a subset, or @@ -158,6 +158,9 @@ all:: # Define NO_PREAD if you have a problem with pread() system call (e.g. # cygwin1.dll before v1.5.22). # +# Define NO_THREAD_SAFE_PREAD if your pread() implementation is not +# thread-safe. (e.g. compat/pread.c or cygwin) +# # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is # generally faster on your platform than accessing the working directory. # @@ -485,7 +488,6 @@ X = PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS)) TEST_PROGRAMS_NEED_X += test-chmtime -TEST_PROGRAMS_NEED_X += test-credential TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta @@ -833,6 +835,7 @@ BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit.o BUILTIN_OBJS += builtin/config.o BUILTIN_OBJS += builtin/count-objects.o +BUILTIN_OBJS += builtin/credential.o BUILTIN_OBJS += builtin/describe.o BUILTIN_OBJS += builtin/diff-files.o BUILTIN_OBJS += builtin/diff-index.o @@ -1059,6 +1062,7 @@ ifeq ($(uname_O),Cygwin) NO_IPV6 = YesPlease OLD_ICONV = UnfortunatelyYes endif + NO_THREAD_SAFE_PREAD = YesPlease NEEDS_LIBICONV = YesPlease NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes NO_TRUSTABLE_FILEMODE = UnfortunatelyYes @@ -1668,6 +1672,10 @@ endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD COMPAT_OBJS += compat/pread.o + NO_THREAD_SAFE_PREAD = YesPlease +endif +ifdef NO_THREAD_SAFE_PREAD + BASIC_CFLAGS += -DNO_THREAD_SAFE_PREAD endif ifdef NO_FAST_WORKING_DIRECTORY BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY @@ -497,6 +497,7 @@ static int git_attr_system(void) static void bootstrap_attr_stack(void) { struct attr_stack *elem; + char *xdg_attributes_file; if (attr_stack) return; @@ -515,13 +516,15 @@ static void bootstrap_attr_stack(void) } } - if (git_attributes_file) { - elem = read_attr_from_file(git_attributes_file, 1); - if (elem) { - elem->origin = NULL; - elem->prev = attr_stack; - attr_stack = elem; - } + if (!git_attributes_file) { + home_config_paths(NULL, &xdg_attributes_file, "attributes"); + git_attributes_file = xdg_attributes_file; + } + elem = read_attr_from_file(git_attributes_file, 1); + if (elem) { + elem->origin = NULL; + elem->prev = attr_stack; + attr_stack = elem; } if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { @@ -67,6 +67,7 @@ extern int cmd_commit(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); extern int cmd_config(int argc, const char **argv, const char *prefix); extern int cmd_count_objects(int argc, const char **argv, const char *prefix); +extern int cmd_credential(int argc, const char **argv, const char *prefix); extern int cmd_describe(int argc, const char **argv, const char *prefix); extern int cmd_diff_files(int argc, const char **argv, const char *prefix); extern int cmd_diff_index(int argc, const char **argv, const char *prefix); diff --git a/builtin/blame.c b/builtin/blame.c index 24d3dd5292..960c58d855 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1837,6 +1837,16 @@ static int read_ancestry(const char *graft_file) return 0; } +static int update_auto_abbrev(int auto_abbrev, struct origin *suspect) +{ + const char *uniq = find_unique_abbrev(suspect->commit->object.sha1, + auto_abbrev); + int len = strlen(uniq); + if (auto_abbrev < len) + return len; + return auto_abbrev; +} + /* * How many columns do we need to show line numbers, authors, * and filenames? @@ -1847,12 +1857,16 @@ static void find_alignment(struct scoreboard *sb, int *option) int longest_dst_lines = 0; unsigned largest_score = 0; struct blame_entry *e; + int compute_auto_abbrev = (abbrev < 0); + int auto_abbrev = default_abbrev; for (e = sb->ent; e; e = e->next) { struct origin *suspect = e->suspect; struct commit_info ci; int num; + if (compute_auto_abbrev) + auto_abbrev = update_auto_abbrev(auto_abbrev, suspect); if (strcmp(suspect->path, sb->path)) *option |= OUTPUT_SHOW_NAME; num = strlen(suspect->path); @@ -1880,6 +1894,10 @@ static void find_alignment(struct scoreboard *sb, int *option) max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); max_score_digits = decimal_width(largest_score); + + if (compute_auto_abbrev) + /* one more abbrev length is needed for the boundary commit */ + abbrev = auto_abbrev + 1; } /* @@ -2353,10 +2371,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_done: argc = parse_options_end(&ctx); - if (abbrev == -1) - abbrev = default_abbrev; - /* one more abbrev length is needed for the boundary commit */ - abbrev++; + if (0 < abbrev) + /* one more abbrev length is needed for the boundary commit */ + abbrev++; if (revs_file && read_ancestry(revs_file)) die_errno("reading graft file '%s' failed", revs_file); diff --git a/builtin/checkout.c b/builtin/checkout.c index e8c1b1f189..3980d5d06e 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -915,6 +915,8 @@ static int switch_unborn_to_new_branch(struct checkout_opts *opts) int status; struct strbuf branch_ref = STRBUF_INIT; + if (!opts->new_branch) + die(_("You are on a branch yet to be born")); strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch); status = create_symref("HEAD", branch_ref.buf, "checkout -b"); strbuf_release(&branch_ref); diff --git a/builtin/clone.c b/builtin/clone.c index 7f3b9823ce..d3b7fdccec 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -433,8 +433,11 @@ static struct ref *wanted_peer_refs(const struct ref *refs, if (!option_branch) remote_head = guess_remote_head(head, refs, 0); - else - remote_head = find_remote_branch(refs, option_branch); + else { + local_refs = NULL; + tail = &local_refs; + remote_head = copy_ref(find_remote_branch(refs, option_branch)); + } if (!remote_head && option_branch) warning(_("Could not find remote branch %s to clone."), diff --git a/builtin/config.c b/builtin/config.c index 33c8820af6..e8e1c0a456 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -161,7 +161,7 @@ static int show_config(const char *key_, const char *value_, void *cb) static int get_value(const char *key_, const char *regex_) { int ret = -1; - char *global = NULL, *repo_config = NULL; + char *global = NULL, *xdg = NULL, *repo_config = NULL; const char *system_wide = NULL, *local; struct config_include_data inc = CONFIG_INCLUDE_INIT; config_fn_t fn; @@ -169,12 +169,10 @@ static int get_value(const char *key_, const char *regex_) local = given_config_file; if (!local) { - const char *home = getenv("HOME"); local = repo_config = git_pathdup("config"); - if (home) - global = xstrdup(mkpath("%s/.gitconfig", home)); if (git_config_system()) system_wide = git_etc_gitconfig(); + home_config_paths(&global, &xdg, "config"); } if (use_key_regexp) { @@ -229,6 +227,8 @@ static int get_value(const char *key_, const char *regex_) if (do_all && system_wide) git_config_from_file(fn, system_wide, data); + if (do_all && xdg) + git_config_from_file(fn, xdg, data); if (do_all && global) git_config_from_file(fn, global, data); if (do_all) @@ -238,6 +238,8 @@ static int get_value(const char *key_, const char *regex_) git_config_from_file(fn, local, data); if (!do_all && !seen && global) git_config_from_file(fn, global, data); + if (!do_all && !seen && xdg) + git_config_from_file(fn, xdg, data); if (!do_all && !seen && system_wide) git_config_from_file(fn, system_wide, data); @@ -255,6 +257,7 @@ static int get_value(const char *key_, const char *regex_) free_strings: free(repo_config); free(global); + free(xdg); return ret; } @@ -379,13 +382,17 @@ int cmd_config(int argc, const char **argv, const char *prefix) } if (use_global_config) { - char *home = getenv("HOME"); - if (home) { - char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); + char *user_config = NULL; + char *xdg_config = NULL; + + home_config_paths(&user_config, &xdg_config, "config"); + + if (access(user_config, R_OK) && !access(xdg_config, R_OK)) + given_config_file = xdg_config; + else if (user_config) given_config_file = user_config; - } else { + else die("$HOME not set"); - } } else if (use_system_config) given_config_file = git_etc_gitconfig(); diff --git a/builtin/credential.c b/builtin/credential.c new file mode 100644 index 0000000000..0412fa00f0 --- /dev/null +++ b/builtin/credential.c @@ -0,0 +1,31 @@ +#include "git-compat-util.h" +#include "credential.h" +#include "builtin.h" + +static const char usage_msg[] = + "git credential [fill|approve|reject]"; + +int cmd_credential(int argc, const char **argv, const char *prefix) +{ + const char *op; + struct credential c = CREDENTIAL_INIT; + + op = argv[1]; + if (!op) + usage(usage_msg); + + if (credential_read(&c, stdin) < 0) + die("unable to read credential from stdin"); + + if (!strcmp(op, "fill")) { + credential_fill(&c); + credential_write(&c, stdout); + } else if (!strcmp(op, "approve")) { + credential_approve(&c); + } else if (!strcmp(op, "reject")) { + credential_reject(&c); + } else { + usage(usage_msg); + } + return 0; +} diff --git a/builtin/fast-export.c b/builtin/fast-export.c index ef7c012094..9ab6db3fb0 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -185,6 +185,8 @@ static void print_path(const char *path) int need_quote = quote_c_style(path, NULL, NULL, 0); if (need_quote) quote_c_style(path, NULL, stdout, 0); + else if (strchr(path, ' ')) + printf("\"%s\"", path); else printf("%s", path); } diff --git a/builtin/help.c b/builtin/help.c index 8f9cd60548..efea4f55e1 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -34,6 +34,8 @@ enum help_format { HELP_FORMAT_WEB }; +static const char *html_path; + static int show_all = 0; static unsigned int colopts; static enum help_format help_format = HELP_FORMAT_NONE; @@ -265,6 +267,12 @@ static int git_help_config(const char *var, const char *value, void *cb) help_format = parse_help_format(value); return 0; } + if (!strcmp(var, "help.htmlpath")) { + if (!value) + return config_error_nonbool(var); + html_path = xstrdup(value); + return 0; + } if (!strcmp(var, "man.viewer")) { if (!value) return config_error_nonbool(var); @@ -387,12 +395,15 @@ static void show_info_page(const char *git_cmd) static void get_html_page_path(struct strbuf *page_path, const char *page) { struct stat st; - const char *html_path = system_path(GIT_HTML_PATH); + if (!html_path) + html_path = system_path(GIT_HTML_PATH); /* Check that we have a git documentation directory. */ - if (stat(mkpath("%s/git.html", html_path), &st) - || !S_ISREG(st.st_mode)) - die(_("'%s': not a documentation directory."), html_path); + if (!strstr(html_path, "://")) { + if (stat(mkpath("%s/git.html", html_path), &st) + || !S_ISREG(st.st_mode)) + die("'%s': not a documentation directory.", html_path); + } strbuf_init(page_path, 0); strbuf_addf(page_path, "%s/%s.html", html_path, page); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 8b5c1eb33e..5a0372ab08 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -40,8 +40,8 @@ struct base_data { int ofs_first, ofs_last; }; -#if !defined(NO_PTHREADS) && defined(NO_PREAD) -/* NO_PREAD uses compat/pread.c, which is not thread-safe. Disable threading. */ +#if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD) +/* pread() emulation is not thread-safe. Disable threading. */ #define NO_PTHREADS #endif @@ -413,6 +413,7 @@ extern void verify_filename(const char *prefix, const char *name, int diagnose_misspelt_rev); extern void verify_non_filename(const char *prefix, const char *name); +extern int path_inside_repo(const char *prefix, const char *path); #define INIT_DB_QUIET 0x0001 @@ -621,6 +622,8 @@ extern char *git_snpath(char *buf, size_t n, const char *fmt, ...) __attribute__((format (printf, 3, 4))); extern char *git_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); +extern char *mkpathdup(const char *fmt, ...) + __attribute__((format (printf, 1, 2))); /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); @@ -710,6 +713,7 @@ int set_shared_perm(const char *path, int mode); int safe_create_leading_directories(char *path); int safe_create_leading_directories_const(const char *path); int mkdir_in_gitdir(const char *path); +extern void home_config_paths(char **global, char **xdg, char *file); extern char *expand_user_path(const char *path); const char *enter_repo(const char *path, int strict); static inline int is_absolute_path(const char *path) @@ -929,7 +929,10 @@ int git_config_system(void) int git_config_early(config_fn_t fn, void *data, const char *repo_config) { int ret = 0, found = 0; - const char *home = NULL; + char *xdg_config = NULL; + char *user_config = NULL; + + home_config_paths(&user_config, &xdg_config, "config"); if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), @@ -937,14 +940,14 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) found += 1; } - home = getenv("HOME"); - if (home) { - char buf[PATH_MAX]; - char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home); - if (!access(user_config, R_OK)) { - ret += git_config_from_file(fn, user_config, data); - found += 1; - } + if (!access(xdg_config, R_OK)) { + ret += git_config_from_file(fn, xdg_config, data); + found += 1; + } + + if (!access(user_config, R_OK)) { + ret += git_config_from_file(fn, user_config, data); + found += 1; } if (repo_config && !access(repo_config, R_OK)) { @@ -963,6 +966,8 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) break; } + free(xdg_config); + free(user_config); return ret == 0 ? found : ret; } @@ -49,6 +49,16 @@ static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1 extra->nr++; } +static void die_initial_contact(int got_at_least_one_head) +{ + if (got_at_least_one_head) + die("The remote end hung up upon initial contact"); + else + die("Could not read from remote repository.\n\n" + "Please make sure you have the correct access rights\n" + "and the repository exists."); +} + /* * Read all the refs from the other end */ @@ -56,6 +66,8 @@ struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *extra_have) { + int got_at_least_one_head = 0; + *list = NULL; for (;;) { struct ref *ref; @@ -64,7 +76,10 @@ struct ref **get_remote_heads(int in, struct ref **list, char *name; int len, name_len; - len = packet_read_line(in, buffer, sizeof(buffer)); + len = packet_read(in, buffer, sizeof(buffer)); + if (len < 0) + die_initial_contact(got_at_least_one_head); + if (!len) break; if (buffer[len-1] == '\n') @@ -95,6 +110,7 @@ struct ref **get_remote_heads(int in, struct ref **list, hashcpy(ref->old_sha1, old_sha1); *list = ref; list = &ref->next; + got_at_least_one_head = 1; } return list; } diff --git a/contrib/mw-to-git/git-remote-mediawiki b/contrib/mw-to-git/git-remote-mediawiki index c18bfa1f15..c07b4f0ee6 100755 --- a/contrib/mw-to-git/git-remote-mediawiki +++ b/contrib/mw-to-git/git-remote-mediawiki @@ -26,9 +26,6 @@ # - Git renames could be turned into MediaWiki renames (see TODO # below) # -# - login/password support requires the user to write the password -# cleartext in a file (see TODO below). -# # - No way to import "one page, and all pages included in it" # # - Multiple remote MediaWikis have not been very well tested. @@ -43,6 +40,8 @@ use encoding 'utf8'; binmode STDERR, ":utf8"; use URI::Escape; +use IPC::Open2; + use warnings; # Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced @@ -72,9 +71,7 @@ my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.". $rem chomp(@tracked_categories); my $wiki_login = run_git("config --get remote.". $remotename .".mwLogin"); -# TODO: ideally, this should be able to read from keyboard, but we're -# inside a remote helper, so our stdin is connect to git, not to a -# terminal. +# Note: mwPassword is discourraged. Use the credential system instead. my $wiki_passwd = run_git("config --get remote.". $remotename .".mwPassword"); my $wiki_domain = run_git("config --get remote.". $remotename .".mwDomain"); chomp($wiki_login); @@ -151,28 +148,108 @@ while (<STDIN>) { ########################## Functions ############################## +## credential API management (generic functions) + +sub credential_from_url { + my $url = shift; + my $parsed = URI->new($url); + my %credential; + + if ($parsed->scheme) { + $credential{protocol} = $parsed->scheme; + } + if ($parsed->host) { + $credential{host} = $parsed->host; + } + if ($parsed->path) { + $credential{path} = $parsed->path; + } + if ($parsed->userinfo) { + if ($parsed->userinfo =~ /([^:]*):(.*)/) { + $credential{username} = $1; + $credential{password} = $2; + } else { + $credential{username} = $parsed->userinfo; + } + } + + return %credential; +} + +sub credential_read { + my %credential; + my $reader = shift; + my $op = shift; + while (<$reader>) { + my ($key, $value) = /([^=]*)=(.*)/; + if (not defined $key) { + die "ERROR receiving response from git credential $op:\n$_\n"; + } + $credential{$key} = $value; + } + return %credential; +} + +sub credential_write { + my $credential = shift; + my $writer = shift; + while (my ($key, $value) = each(%$credential) ) { + if ($value) { + print $writer "$key=$value\n"; + } + } +} + +sub credential_run { + my $op = shift; + my $credential = shift; + my $pid = open2(my $reader, my $writer, "git credential $op"); + credential_write($credential, $writer); + print $writer "\n"; + close($writer); + + if ($op eq "fill") { + %$credential = credential_read($reader, $op); + } else { + if (<$reader>) { + die "ERROR while running git credential $op:\n$_"; + } + } + close($reader); + waitpid($pid, 0); + my $child_exit_status = $? >> 8; + if ($child_exit_status != 0) { + die "'git credential $op' failed with code $child_exit_status."; + } +} + # MediaWiki API instance, created lazily. my $mediawiki; sub mw_connect_maybe { if ($mediawiki) { - return; + return; } $mediawiki = MediaWiki::API->new; $mediawiki->{config}->{api_url} = "$url/api.php"; if ($wiki_login) { - if (!$mediawiki->login({ - lgname => $wiki_login, - lgpassword => $wiki_passwd, - lgdomain => $wiki_domain, - })) { - print STDERR "Failed to log in mediawiki user \"$wiki_login\" on $url\n"; - print STDERR "(error " . - $mediawiki->{error}->{code} . ': ' . - $mediawiki->{error}->{details} . ")\n"; - exit 1; + my %credential = credential_from_url($url); + $credential{username} = $wiki_login; + $credential{password} = $wiki_passwd; + credential_run("fill", \%credential); + my $request = {lgname => $credential{username}, + lgpassword => $credential{password}, + lgdomain => $wiki_domain}; + if ($mediawiki->login($request)) { + credential_run("approve", \%credential); + print STDERR "Logged in mediawiki user \"$credential{username}\".\n"; } else { - print STDERR "Logged in with user \"$wiki_login\".\n"; + print STDERR "Failed to log in mediawiki user \"$credential{username}\" on $url\n"; + print STDERR " (error " . + $mediawiki->{error}->{code} . ': ' . + $mediawiki->{error}->{details} . ")\n"; + credential_run("reject", \%credential); + exit 1; } } } diff --git a/credential.c b/credential.c index 62d1c56819..2c400073fa 100644 --- a/credential.c +++ b/credential.c @@ -191,7 +191,7 @@ static void credential_write_item(FILE *fp, const char *key, const char *value) fprintf(fp, "%s=%s\n", key, value); } -static void credential_write(const struct credential *c, FILE *fp) +void credential_write(const struct credential *c, FILE *fp) { credential_write_item(fp, "protocol", c->protocol); credential_write_item(fp, "host", c->host); diff --git a/credential.h b/credential.h index 96ea41bd1c..0c3e85e8e4 100644 --- a/credential.h +++ b/credential.h @@ -26,6 +26,7 @@ void credential_approve(struct credential *); void credential_reject(struct credential *); int credential_read(struct credential *, FILE *); +void credential_write(const struct credential *, FILE *); void credential_from_url(struct credential *, const char *url); int credential_match(const struct credential *have, const struct credential *want); diff --git a/diff-no-index.c b/diff-no-index.c index 77667b810d..beec49b5ac 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -151,23 +151,6 @@ static int queue_diff(struct diff_options *o, } } -static int path_outside_repo(const char *path) -{ - const char *work_tree; - size_t len; - - if (!is_absolute_path(path)) - return 0; - work_tree = get_git_work_tree(); - if (!work_tree) - return 1; - len = strlen(work_tree); - if (strncmp(path, work_tree, len) || - (path[len] != '\0' && path[len] != '/')) - return 1; - return 0; -} - void diff_no_index(struct rev_info *revs, int argc, const char **argv, int nongit, const char *prefix) @@ -197,8 +180,8 @@ void diff_no_index(struct rev_info *revs, * a colourful "diff" replacement. */ if ((argc != i + 2) || - (!path_outside_repo(argv[i]) && - !path_outside_repo(argv[i+1]))) + (path_inside_repo(prefix, argv[i]) && + path_inside_repo(prefix, argv[i+1]))) return; } if (argc != i + 2) @@ -268,5 +251,5 @@ void diff_no_index(struct rev_info *revs, * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ - exit(revs->diffopt.found_changes); + exit(diff_result_code(&revs->diffopt, 0)); } @@ -1303,12 +1303,17 @@ int remove_dir_recursively(struct strbuf *path, int flag) void setup_standard_excludes(struct dir_struct *dir) { const char *path; + char *xdg_path; dir->exclude_per_dir = ".gitignore"; path = git_path("info/exclude"); + if (!excludes_file) { + home_config_paths(NULL, &xdg_path, "ignore"); + excludes_file = xdg_path; + } if (!access(path, R_OK)) add_excludes_from_file(dir, path); - if (excludes_file && !access(excludes_file, R_OK)) + if (!access(excludes_file, R_OK)) add_excludes_from_file(dir, excludes_file); } @@ -351,6 +351,7 @@ static void handle_internal_command(int argc, const char **argv) { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "config", cmd_config, RUN_SETUP_GENTLY }, { "count-objects", cmd_count_objects, RUN_SETUP }, + { "credential", cmd_credential, RUN_SETUP_GENTLY }, { "describe", cmd_describe, RUN_SETUP }, { "diff", cmd_diff }, { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE }, @@ -87,6 +87,21 @@ char *git_pathdup(const char *fmt, ...) return xstrdup(path); } +char *mkpathdup(const char *fmt, ...) +{ + char *path; + struct strbuf sb = STRBUF_INIT; + va_list args; + + va_start(args, fmt); + strbuf_vaddf(&sb, fmt, args); + va_end(args); + path = xstrdup(cleanup_path(sb.buf)); + + strbuf_release(&sb); + return path; +} + char *mkpath(const char *fmt, ...) { va_list args; @@ -122,6 +137,32 @@ char *git_path(const char *fmt, ...) return cleanup_path(pathname); } +void home_config_paths(char **global, char **xdg, char *file) +{ + char *xdg_home = getenv("XDG_CONFIG_HOME"); + char *home = getenv("HOME"); + char *to_free = NULL; + + if (!home) { + if (global) + *global = NULL; + } else { + if (!xdg_home) { + to_free = mkpathdup("%s/.config", home); + xdg_home = to_free; + } + if (global) + *global = mkpathdup("%s/.gitconfig", home); + } + + if (!xdg_home) + *xdg = NULL; + else + *xdg = mkpathdup("%s/git/%s", xdg_home, file); + + free(to_free); +} + char *git_path_submodule(const char *path, const char *fmt, ...) { char *pathname = get_pathname(); diff --git a/perl/Makefile b/perl/Makefile index fe7a486464..6ca7d472eb 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -34,22 +34,34 @@ modules += Git/SVN/Ra $(makfile): ../GIT-CFLAGS Makefile echo all: private-Error.pm Git.pm Git/I18N.pm > $@ - echo ' mkdir -p blib/lib/Git/SVN/Memoize' >> $@ set -e; \ for i in $(modules); \ do \ + if test $$i = $${i%/*}; \ + then \ + subdir=; \ + else \ + subdir=/$${i%/*}; \ + fi; \ echo ' $(RM) blib/lib/'$$i'.pm' >> $@; \ + echo ' mkdir -p blib/lib'$$subdir >> $@; \ echo ' cp '$$i'.pm blib/lib/'$$i'.pm' >> $@; \ done echo ' $(RM) blib/lib/Error.pm' >> $@ '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \ echo ' cp private-Error.pm blib/lib/Error.pm' >> $@ echo install: >> $@ - echo ' mkdir -p "$$(DESTDIR)$(instdir_SQ)/Git/SVN/Memoize"' >> $@ set -e; \ for i in $(modules); \ do \ + if test $$i = $${i%/*}; \ + then \ + subdir=; \ + else \ + subdir=/$${i%/*}; \ + fi; \ echo ' $(RM) "$$(DESTDIR)$(instdir_SQ)/'$$i'.pm"' >> $@; \ + echo ' mkdir -p "$$(DESTDIR)$(instdir_SQ)'$$subdir'"' >> $@; \ echo ' cp '$$i'.pm "$$(DESTDIR)$(instdir_SQ)/'$$i'.pm"' >> $@; \ done echo ' $(RM) "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@ diff --git a/pkt-line.c b/pkt-line.c index 5a04984ea3..eaba15f124 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -135,13 +135,19 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...) strbuf_add(buf, buffer, n); } -static void safe_read(int fd, void *buffer, unsigned size) +static int safe_read(int fd, void *buffer, unsigned size, int return_line_fail) { ssize_t ret = read_in_full(fd, buffer, size); if (ret < 0) die_errno("read error"); - else if (ret < size) + else if (ret < size) { + if (return_line_fail) + return -1; + die("The remote end hung up unexpectedly"); + } + + return ret; } static int packet_length(const char *linelen) @@ -169,12 +175,14 @@ static int packet_length(const char *linelen) return len; } -int packet_read_line(int fd, char *buffer, unsigned size) +static int packet_read_internal(int fd, char *buffer, unsigned size, int return_line_fail) { - int len; + int len, ret; char linelen[4]; - safe_read(fd, linelen, 4); + ret = safe_read(fd, linelen, 4, return_line_fail); + if (return_line_fail && ret < 0) + return ret; len = packet_length(linelen); if (len < 0) die("protocol error: bad line length character: %.4s", linelen); @@ -185,12 +193,24 @@ int packet_read_line(int fd, char *buffer, unsigned size) len -= 4; if (len >= size) die("protocol error: bad line length %d", len); - safe_read(fd, buffer, len); + ret = safe_read(fd, buffer, len, return_line_fail); + if (return_line_fail && ret < 0) + return ret; buffer[len] = 0; packet_trace(buffer, len, 0); return len; } +int packet_read(int fd, char *buffer, unsigned size) +{ + return packet_read_internal(fd, buffer, size, 1); +} + +int packet_read_line(int fd, char *buffer, unsigned size) +{ + return packet_read_internal(fd, buffer, size, 0); +} + int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len) { diff --git a/pkt-line.h b/pkt-line.h index 1e5dcfe87c..8cfeb0c31c 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -13,6 +13,7 @@ void packet_buf_flush(struct strbuf *buf); void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3))); int packet_read_line(int fd, char *buffer, unsigned size); +int packet_read(int fd, char *buffer, unsigned size); int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len); ssize_t safe_write(int, const void *, ssize_t); @@ -4,7 +4,7 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; -char *prefix_path(const char *prefix, int len, const char *path) +static char *prefix_path_gently(const char *prefix, int len, const char *path) { const char *orig = path; char *sanitized; @@ -31,7 +31,8 @@ char *prefix_path(const char *prefix, int len, const char *path) if (strncmp(sanitized, work_tree, len) || (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) { error_out: - die("'%s' is outside repository", orig); + free(sanitized); + return NULL; } if (sanitized[len] == '/') len++; @@ -40,6 +41,25 @@ char *prefix_path(const char *prefix, int len, const char *path) return sanitized; } +char *prefix_path(const char *prefix, int len, const char *path) +{ + char *r = prefix_path_gently(prefix, len, path); + if (!r) + die("'%s' is outside repository", path); + return r; +} + +int path_inside_repo(const char *prefix, const char *path) +{ + int len = prefix ? strlen(prefix) : 0; + char *r = prefix_path_gently(prefix, len, path); + if (r) { + free(r); + return 1; + } + return 0; +} + int check_filename(const char *prefix, const char *arg) { const char *name; @@ -307,6 +307,25 @@ Don't: Use test_done instead if you need to stop the tests early (see "Skipping tests" below). + - use '! git cmd' when you want to make sure the git command exits + with failure in a controlled way by calling "die()". Instead, + use 'test_must_fail git cmd'. This will signal a failure if git + dies in an unexpected way (e.g. segfault). + + - use perl without spelling it as "$PERL_PATH". This is to help our + friends on Windows where the platform Perl often adds CR before + the end of line, and they bundle Git with a version of Perl that + does not do so, whose path is specified with $PERL_PATH. + + - use sh without spelling it as "$SHELL_PATH", when the script can + be misinterpreted by broken platform shell (e.g. Solaris). + + - chdir around in tests. It is not sufficient to chdir to + somewhere and then chdir back to the original location later in + the test, as any intermediate step can fail and abort the test, + causing the next test to start in an unexpected directory. Do so + inside a subshell if necessary. + - Break the TAP output The raw output from your test may be interpreted by a TAP harness. TAP @@ -342,9 +361,9 @@ If you need to skip tests you should do so by using the three-arg form of the test_* functions (see the "Test harness library" section below), e.g.: - test_expect_success PERL 'I need Perl' " - '$PERL_PATH' -e 'hlagh() if unf_unf()' - " + test_expect_success PERL 'I need Perl' ' + "$PERL_PATH" -e "hlagh() if unf_unf()" + ' The advantage of skipping tests like this is that platforms that don't have the PERL and other optional dependencies get an indication of how diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 4a37cd79e5..957ae936e8 100755 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -4,10 +4,20 @@ # stdout and stderr should be provided on stdin, # separated by "--". check() { + credential_opts= + credential_cmd=$1 + shift + for arg in "$@"; do + credential_opts="$credential_opts -c credential.helper='$arg'" + done read_chunk >stdin && read_chunk >expect-stdout && read_chunk >expect-stderr && - test-credential "$@" <stdin >stdout 2>stderr && + if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then + echo "git credential failed with code $?" && + cat stderr && + false + fi && test_cmp expect-stdout stdout && test_cmp expect-stderr stderr } @@ -41,7 +51,7 @@ reject() { echo protocol=$2 echo host=$3 echo username=$4 - ) | test-credential reject $1 + ) | git -c credential.helper=$1 credential reject } helper_test() { @@ -52,6 +62,8 @@ helper_test() { protocol=https host=example.com -- + protocol=https + host=example.com username=askpass-username password=askpass-password -- @@ -74,6 +86,8 @@ helper_test() { protocol=https host=example.com -- + protocol=https + host=example.com username=store-user password=store-pass -- @@ -85,6 +99,8 @@ helper_test() { protocol=http host=example.com -- + protocol=http + host=example.com username=askpass-username password=askpass-password -- @@ -98,6 +114,8 @@ helper_test() { protocol=https host=other.tld -- + protocol=https + host=other.tld username=askpass-username password=askpass-password -- @@ -112,6 +130,8 @@ helper_test() { host=example.com username=other -- + protocol=https + host=example.com username=other password=askpass-password -- @@ -133,6 +153,9 @@ helper_test() { host=path.tld path=bar.git -- + protocol=http + host=path.tld + path=bar.git username=askpass-username password=askpass-password -- @@ -150,6 +173,8 @@ helper_test() { protocol=https host=example.com -- + protocol=https + host=example.com username=askpass-username password=askpass-password -- @@ -176,6 +201,8 @@ helper_test() { host=example.com username=user1 -- + protocol=https + host=example.com username=user1 password=pass1 EOF @@ -184,6 +211,8 @@ helper_test() { host=example.com username=user2 -- + protocol=https + host=example.com username=user2 password=pass2 EOF @@ -200,6 +229,8 @@ helper_test() { host=example.com username=user1 -- + protocol=https + host=example.com username=user1 password=askpass-password -- @@ -213,6 +244,8 @@ helper_test() { host=example.com username=user2 -- + protocol=https + host=example.com username=user2 password=pass2 EOF @@ -234,6 +267,8 @@ helper_test_timeout() { protocol=https host=timeout.tld -- + protocol=https + host=timeout.tld username=askpass-username password=askpass-password -- diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh index 121e38002b..31d75ae043 100644 --- a/t/lib-git-p4.sh +++ b/t/lib-git-p4.sh @@ -2,6 +2,10 @@ # Library code for git p4 tests # +# p4 tests never use the top-level repo; always build/clone into +# a subdirectory called "$git" +TEST_NO_CREATE_REPO=NoThanks + . ./test-lib.sh if ! test_have_prereq PYTHON; then @@ -27,23 +31,48 @@ export P4CLIENT=client export P4EDITOR=: db="$TRASH_DIRECTORY/db" -cli="$TRASH_DIRECTORY/cli" +cli=$(test-path-utils real_path "$TRASH_DIRECTORY/cli") git="$TRASH_DIRECTORY/git" pidfile="$TRASH_DIRECTORY/p4d.pid" start_p4d() { mkdir -p "$db" "$cli" "$git" && + rm -f "$pidfile" && ( p4d -q -r "$db" -p $P4DPORT & echo $! >"$pidfile" ) && - for i in 1 2 3 4 5 ; do - p4 info >/dev/null 2>&1 && break || true && - echo waiting for p4d to start && + + # This gives p4d a long time to start up, as it can be + # quite slow depending on the machine. Set this environment + # variable to something smaller to fail faster in, say, + # an automated test setup. If the p4d process dies, that + # will be caught with the "kill -0" check below. + i=${P4D_START_PATIENCE:-300} + pid=$(cat "$pidfile") + ready= + while test $i -gt 0 + do + # succeed when p4 client commands start to work + if p4 info >/dev/null 2>&1 + then + ready=true + break + fi + # fail if p4d died + kill -0 $pid 2>/dev/null || break + echo waiting for p4d to start sleep 1 - done && - # complain if it never started - p4 info >/dev/null && + i=$(( $i - 1 )) + done + + if test -z "$ready" + then + # p4d failed to start + return 1 + fi + + # build a client ( cd "$cli" && p4 client -i <<-EOF @@ -53,6 +82,7 @@ start_p4d() { View: //depot/... //client/... EOF ) + return 0 } kill_p4d() { @@ -69,5 +99,6 @@ kill_p4d() { } cleanup_git() { - rm -rf "$git" + rm -rf "$git" && + mkdir "$git" } diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 20e28e34e7..538ea5fb1c 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -82,6 +82,9 @@ test_expect_success 'credential_fill passes along metadata' ' host=example.com path=foo.git -- + protocol=ftp + host=example.com + path=foo.git username=one password=two -- @@ -213,6 +216,8 @@ test_expect_success 'match configured credential' ' host=example.com path=repo.git -- + protocol=https + host=example.com username=foo password=bar -- @@ -225,6 +230,8 @@ test_expect_success 'do not match configured credential' ' protocol=https host=bar -- + protocol=https + host=bar username=askpass-username password=askpass-password -- @@ -239,6 +246,8 @@ test_expect_success 'pull username from config' ' protocol=https host=example.com -- + protocol=https + host=example.com username=foo password=askpass-password -- @@ -252,6 +261,8 @@ test_expect_success 'http paths can be part of context' ' host=example.com path=foo.git -- + protocol=https + host=example.com username=foo password=bar -- @@ -265,6 +276,9 @@ test_expect_success 'http paths can be part of context' ' host=example.com path=foo.git -- + protocol=https + host=example.com + path=foo.git username=foo password=bar -- diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index b946f87686..df573c4978 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -42,13 +42,13 @@ test_expect_success 'ls-tree piped to mktree (2)' ' ' test_expect_success 'ls-tree output in wrong order given to mktree (1)' ' - perl -e "print reverse <>" <top | + "$PERL_PATH" -e "print reverse <>" <top | git mktree >actual && test_cmp tree actual ' test_expect_success 'ls-tree output in wrong order given to mktree (2)' ' - perl -e "print reverse <>" <top.withsub | + "$PERL_PATH" -e "print reverse <>" <top.withsub | git mktree >actual && test_cmp tree.withsub actual ' diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh new file mode 100755 index 0000000000..3c75c3f2e7 --- /dev/null +++ b/t/t1306-xdg-files.sh @@ -0,0 +1,158 @@ +#!/bin/sh +# +# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas, +# Thomas Nguy, Khoi Nguyen +# Grenoble INP Ensimag +# + +test_description='Compatibility with $XDG_CONFIG_HOME/git/ files' + +. ./test-lib.sh + +test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' ' + mkdir -p .config/git && + echo "[alias]" >.config/git/config && + echo " myalias = !echo in_config" >>.config/git/config && + echo in_config >expected && + git myalias >actual && + test_cmp expected actual +' + + +test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' ' + >.gitconfig && + echo "[alias]" >.gitconfig && + echo " myalias = !echo in_gitconfig" >>.gitconfig && + echo in_gitconfig >expected && + git myalias >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\''t' ' + rm .gitconfig && + echo "[user]" >.config/git/config && + echo " name = read_config" >>.config/git/config && + echo read_config >expected && + git config --get user.name >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' ' + >.gitconfig && + echo "[user]" >.gitconfig && + echo " name = read_gitconfig" >>.gitconfig && + echo read_gitconfig >expected && + git config --get user.name >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --list: xdg file exists and ~/.gitconfig doesn'\''t' ' + rm .gitconfig && + echo user.name=read_config >expected && + git config --global --list >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists' ' + >.gitconfig && + echo "[user]" >.gitconfig && + echo " name = read_gitconfig" >>.gitconfig && + echo user.name=read_gitconfig >expected && + git config --global --list >actual && + test_cmp expected actual +' + + +test_expect_success 'Setup' ' + git init git && + cd git && + echo foo >to_be_excluded +' + + +test_expect_success 'Exclusion of a file in the XDG ignore file' ' + mkdir -p "$HOME"/.config/git/ && + echo to_be_excluded >"$HOME"/.config/git/ignore && + test_must_fail git add to_be_excluded +' + + +test_expect_success 'Exclusion in both XDG and local ignore files' ' + echo to_be_excluded >.gitignore && + test_must_fail git add to_be_excluded +' + + +test_expect_success 'Exclusion in a non-XDG global ignore file' ' + rm .gitignore && + echo >"$HOME"/.config/git/ignore && + echo to_be_excluded >"$HOME"/my_gitignore && + git config core.excludesfile "$HOME"/my_gitignore && + test_must_fail git add to_be_excluded +' + + +test_expect_success 'Checking attributes in the XDG attributes file' ' + echo foo >f && + git check-attr -a f >actual && + test_line_count -eq 0 actual && + echo "f attr_f" >"$HOME"/.config/git/attributes && + echo "f: attr_f: set" >expected && + git check-attr -a f >actual && + test_cmp expected actual +' + + +test_expect_success 'Checking attributes in both XDG and local attributes files' ' + echo "f -attr_f" >.gitattributes && + echo "f: attr_f: unset" >expected && + git check-attr -a f >actual && + test_cmp expected actual +' + + +test_expect_success 'Checking attributes in a non-XDG global attributes file' ' + test_might_fail rm .gitattributes && + echo "f attr_f=test" >"$HOME"/my_gitattributes && + git config core.attributesfile "$HOME"/my_gitattributes && + echo "f: attr_f: test" >expected && + git check-attr -a f >actual && + test_cmp expected actual +' + + +test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' ' + mkdir -p "$HOME"/.config/git && + >"$HOME"/.config/git/config && + test_might_fail rm "$HOME"/.gitconfig && + git config --global user.name "write_config" && + echo "[user]" >expected && + echo " name = write_config" >>expected && + test_cmp expected "$HOME"/.config/git/config +' + + +test_expect_success 'write: xdg file exists and ~/.gitconfig exists' ' + >"$HOME"/.gitconfig && + git config --global user.name "write_gitconfig" && + echo "[user]" >expected && + echo " name = write_gitconfig" >>expected && + test_cmp expected "$HOME"/.gitconfig +' + + +test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' ' + test_might_fail rm "$HOME"/.gitconfig && + test_might_fail rm "$HOME"/.config/git/config && + git config --global user.name "write_gitconfig" && + echo "[user]" >expected && + echo " name = write_gitconfig" >>expected && + test_cmp expected "$HOME"/.gitconfig +' + + +test_done diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh index 0e3b8582f2..655f278c5f 100755 --- a/t/t2017-checkout-orphan.sh +++ b/t/t2017-checkout-orphan.sh @@ -116,4 +116,10 @@ test_expect_success '--orphan refuses to switch if a merge is needed' ' git reset --hard ' +test_expect_success 'cannot --detach on an unborn branch' ' + git checkout master && + git checkout --orphan new && + test_must_fail git checkout --detach +' + test_done diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index c53c9f65eb..1f35e55ee3 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -71,7 +71,7 @@ test_expect_success 'ls-files -z does not quote funny filename' ' tabs ," (dq) and spaces EOF git ls-files -z >ls-files.z && - perl -pe "y/\000/\012/" <ls-files.z >current && + "$PERL_PATH" -pe "y/\000/\012/" <ls-files.z >current && test_cmp expected current ' @@ -108,7 +108,7 @@ test_expect_success 'diff-index -z does not quote funny filename' ' tabs ," (dq) and spaces EOF git diff-index -z --name-status $t0 >diff-index.z && - perl -pe "y/\000/\012/" <diff-index.z >current && + "$PERL_PATH" -pe "y/\000/\012/" <diff-index.z >current && test_cmp expected current ' @@ -118,7 +118,7 @@ test_expect_success 'diff-tree -z does not quote funny filename' ' tabs ," (dq) and spaces EOF git diff-tree -z --name-status $t0 $t1 >diff-tree.z && - perl -pe y/\\000/\\012/ <diff-tree.z >current && + "$PERL_PATH" -pe y/\\000/\\012/ <diff-tree.z >current && test_cmp expected current ' diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh index 7ba17974c5..7f8693b928 100755 --- a/t/t3401-rebase-partial.sh +++ b/t/t3401-rebase-partial.sh @@ -42,4 +42,12 @@ test_expect_success 'rebase --merge topic branch that was partially merged upstr test_path_is_missing .git/rebase-merge ' +test_expect_success 'rebase ignores empty commit' ' + git reset --hard A && + git commit --allow-empty -m empty && + test_commit D && + git rebase C && + test $(git log --format=%s C..) = "D" +' + test_done diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index b473b6d6eb..959aa26ef5 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -243,7 +243,7 @@ check_threading () { (git format-patch --stdout "$@"; echo $? > status.out) | # Prints everything between the Message-ID and In-Reply-To, # and replaces all Message-ID-lookalikes by a sequence number - perl -ne ' + "$PERL_PATH" -ne ' if (/^(message-id|references|in-reply-to)/i) { $printing = 1; } elsif (/^\S/) { diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 083f62d1d6..533afc1185 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -118,7 +118,7 @@ test_expect_success 'no diff with -diff' ' git diff | grep Binary ' -echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file +echo NULZbetweenZwords | "$PERL_PATH" -pe 'y/Z/\000/' > file test_expect_success 'force diff with "diff"' ' echo >.gitattributes "file diff" && diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh index 3ccc237a8d..36e2f075c9 100755 --- a/t/t4029-diff-trailing-space.sh +++ b/t/t4029-diff-trailing-space.sh @@ -27,7 +27,7 @@ test_expect_success \ git config --bool diff.suppressBlankEmpty true && git diff f > actual && test_cmp exp actual && - perl -i.bak -p -e "s/^\$/ /" exp && + "$PERL_PATH" -i.bak -p -e "s/^\$/ /" exp && git config --bool diff.suppressBlankEmpty false && git diff f > actual && test_cmp exp actual && diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index d4ab4f2ccf..eebb1eed8b 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -21,7 +21,7 @@ EOF cat >hexdump <<'EOF' #!/bin/sh -perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" +"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" EOF chmod +x hexdump diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh index c8296fa4fc..eacc6694f7 100755 --- a/t/t4031-diff-rewrite-binary.sh +++ b/t/t4031-diff-rewrite-binary.sh @@ -60,7 +60,7 @@ test_expect_success 'diff --stat counts binary rewrite as 0 lines' ' { echo "#!$SHELL_PATH" cat <<'EOF' -perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" +"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" EOF } >dump chmod +x dump diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh index cdb9202f57..231412d100 100755 --- a/t/t4035-diff-quiet.sh +++ b/t/t4035-diff-quiet.sh @@ -10,7 +10,22 @@ test_expect_success 'setup' ' git commit -m first && echo 2 >b && git add . && - git commit -a -m second + git commit -a -m second && + mkdir -p test-outside/repo && ( + cd test-outside/repo && + git init && + echo "1 1" >a && + git add . && + git commit -m 1 + ) && + mkdir -p test-outside/non/git && ( + cd test-outside/non/git && + echo "1 1" >a && + echo "1 1" >matching-file && + echo "1 1 " >trailing-space && + echo "1 1" >extra-space && + echo "2" >never-match + ) ' test_expect_success 'git diff-tree HEAD^ HEAD' ' @@ -77,4 +92,60 @@ test_expect_success 'git diff-index --cached HEAD' ' } ' +test_expect_success 'git diff, one file outside repo' ' + ( + cd test-outside/repo && + test_expect_code 0 git diff --quiet a ../non/git/matching-file && + test_expect_code 1 git diff --quiet a ../non/git/extra-space + ) +' + +test_expect_success 'git diff, both files outside repo' ' + ( + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" && + export GIT_CEILING_DIRECTORIES && + cd test-outside/non/git && + test_expect_code 0 git diff --quiet a matching-file && + test_expect_code 1 git diff --quiet a extra-space + ) +' + +test_expect_success 'git diff --ignore-space-at-eol, one file outside repo' ' + ( + cd test-outside/repo && + test_expect_code 0 git diff --quiet --ignore-space-at-eol a ../non/git/trailing-space && + test_expect_code 1 git diff --quiet --ignore-space-at-eol a ../non/git/extra-space + ) +' + +test_expect_success 'git diff --ignore-space-at-eol, both files outside repo' ' + ( + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" && + export GIT_CEILING_DIRECTORIES && + cd test-outside/non/git && + test_expect_code 0 git diff --quiet --ignore-space-at-eol a trailing-space && + test_expect_code 1 git diff --quiet --ignore-space-at-eol a extra-space + ) +' + +test_expect_success 'git diff --ignore-all-space, one file outside repo' ' + ( + cd test-outside/repo && + test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/trailing-space && + test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/extra-space && + test_expect_code 1 git diff --quiet --ignore-all-space a ../non/git/never-match + ) +' + +test_expect_success 'git diff --ignore-all-space, both files outside repo' ' + ( + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" && + export GIT_CEILING_DIRECTORIES && + cd test-outside/non/git && + test_expect_code 0 git diff --quiet --ignore-all-space a trailing-space && + test_expect_code 0 git diff --quiet --ignore-all-space a extra-space && + test_expect_code 1 git diff --quiet --ignore-all-space a never-match + ) +' + test_done diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 4dc8c67edc..979e98398b 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -8,7 +8,12 @@ test_expect_success 'setup' ' mkdir a && mkdir b && echo 1 >a/1 && - echo 2 >a/2 + echo 2 >a/2 && + git init repo && + echo 1 >repo/a && + mkdir -p non/git && + echo 1 >non/git/a && + echo 1 >non/git/b ' test_expect_success 'git diff --no-index directories' ' @@ -16,4 +21,12 @@ test_expect_success 'git diff --no-index directories' ' test $? = 1 && test_line_count = 14 cnt ' +test_expect_success 'git diff --no-index relative path outside repo' ' + ( + cd repo && + test_expect_code 0 git diff --no-index a ../non/git/a && + test_expect_code 0 git diff --no-index ../non/git/a ../non/git/b + ) +' + test_done diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index dbbf56cba9..99627bc6d6 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh @@ -25,10 +25,10 @@ test_expect_success 'setup' " git commit -m 'Initial Version' 2>/dev/null && git checkout -b binary && - perl -pe 'y/x/\000/' <file1 >file3 && + "$PERL_PATH" -pe 'y/x/\000/' <file1 >file3 && cat file3 >file4 && git add file2 && - perl -pe 'y/\000/v/' <file3 >file1 && + "$PERL_PATH" -pe 'y/\000/v/' <file3 >file1 && rm -f file2 && git update-index --add --remove file1 file2 file3 file4 && git commit -m 'Second Version' && diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index 2298ece801..fca815392e 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -12,14 +12,14 @@ test_description='git apply in reverse 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 && - perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 && + "$PERL_PATH" -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 && - perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 && + "$PERL_PATH" -pe "y/mon/\\000\\001\\002/" <file1 >file2 && git commit -a -m second && git tag second && diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 36255d608a..3ab670d36a 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -78,7 +78,7 @@ test_expect_success 'activate rerere, old style (conflicting merge)' ' test_might_fail git config --unset rerere.enabled && test_must_fail git merge first && - sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && + sha1=$("$PERL_PATH" -pe "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && grep "^=======\$" $rr/preimage && ! test -f $rr/postimage && @@ -91,7 +91,7 @@ test_expect_success 'rerere.enabled works, too' ' git reset --hard && test_must_fail git merge first && - sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && + sha1=$("$PERL_PATH" -pe "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && grep ^=======$ $rr/preimage ' @@ -101,7 +101,7 @@ test_expect_success 'set up rr-cache' ' git config rerere.enabled true && git reset --hard && test_must_fail git merge first && - sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && + sha1=$("$PERL_PATH" -pe "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 ' @@ -185,7 +185,7 @@ test_expect_success 'rerere updates postimage timestamp' ' test_expect_success 'rerere clear' ' rm $rr/postimage && - echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR && + echo "$sha1 a1" | "$PERL_PATH" -pe "y/\012/\000/" >.git/MERGE_RR && git rerere clear && ! test -d $rr ' diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 300ed910a5..2e52f8b838 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -13,9 +13,9 @@ TRASH=`pwd` test_expect_success \ 'setup' \ 'rm -f .git/index* && - perl -e "print \"a\" x 4096;" > a && - perl -e "print \"b\" x 4096;" > b && - perl -e "print \"c\" x 4096;" > c && + "$PERL_PATH" -e "print \"a\" x 4096;" > a && + "$PERL_PATH" -e "print \"b\" x 4096;" > b && + "$PERL_PATH" -e "print \"c\" x 4096;" > c && test-genrandom "seed a" 2097152 > a_big && test-genrandom "seed b" 2097152 > b_big && git update-index --add a a_big b b_big c && @@ -129,7 +129,7 @@ test_expect_success \ cd "$TRASH" test_expect_success 'compare delta flavors' ' - perl -e '\'' + "$PERL_PATH" -e '\'' defined($_ = -s $_) or die for @ARGV; exit 1 if $ARGV[0] <= $ARGV[1]; '\'' test-2-$packname_2.pack test-3-$packname_3.pack diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh index 5f6cd4f333..5b1250f0d2 100755 --- a/t/t5303-pack-corruption-resilience.sh +++ b/t/t5303-pack-corruption-resilience.sh @@ -98,7 +98,7 @@ test_expect_success \ 'create_new_pack && git prune-packed && chmod +w ${pack}.pack && - perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack && + "$PERL_PATH" -i.bak -pe "s/ base /abcdef/" ${pack}.pack && test_must_fail git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -155,7 +155,7 @@ test_expect_success \ 'create_new_pack && git prune-packed && chmod +w ${pack}.pack && - perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack && + "$PERL_PATH" -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 1d1ca98588..e80a2af348 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -125,6 +125,11 @@ test_expect_success 'single branch object count' ' test_cmp expected count.singlebranch ' +test_expect_success 'single given branch clone' ' + git clone --single-branch --branch A "file://$(pwd)/." branch-a && + test_must_fail git --git-dir=branch-a/.git rev-parse origin/B +' + test_expect_success 'clone shallow' ' git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow ' @@ -276,7 +281,7 @@ test_expect_success 'clone shallow with --branch' ' ' test_expect_success 'clone shallow object count' ' - echo "in-pack: 12" > count3.expected && + echo "in-pack: 6" > count3.expected && GIT_DIR=shallow3/.git git count-objects -v | grep "^in-pack" > count3.actual && test_cmp count3.expected count3.actual diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 6764d511ce..d16e5d384a 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -87,17 +87,15 @@ test_expect_success 'use branch.<name>.remote if possible' ' test_expect_success 'confuses pattern as remote when no remote specified' ' cat >exp <<-\EOF && fatal: '\''refs*master'\'' does not appear to be a git repository - fatal: The remote end hung up unexpectedly + fatal: Could not read from remote repository. + + Please make sure you have the correct access rights + and the repository exists. EOF # - # Do not expect "git ls-remote <pattern>" to work; ls-remote, correctly, - # confuses <pattern> for <remote>. Although ugly, this behaviour is akin - # to the confusion of refspecs for remotes by git-fetch and git-push, - # eg: - # - # $ git fetch branch - # - + # Do not expect "git ls-remote <pattern>" to work; ls-remote needs + # <remote> if you want to feed <pattern>, just like you cannot say + # fetch <branch>. # We could just as easily have used "master"; the "*" emphasizes its # role as a pattern. test_must_fail git ls-remote refs*master >actual 2>&1 && diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh index 62f2460047..5531bd1af4 100755 --- a/t/t5532-fetch-proxy.sh +++ b/t/t5532-fetch-proxy.sh @@ -15,7 +15,7 @@ test_expect_success 'setup remote repo' ' cat >proxy <<'EOF' #!/bin/sh echo >&2 "proxying for $*" -cmd=`perl -e ' +cmd=`"$PERL_PATH" -e ' read(STDIN, $buf, 4); my $n = hex($buf) - 4; read(STDIN, $buf, $n); diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh index be6094be77..fadf2f258e 100755 --- a/t/t5551-http-fetch.sh +++ b/t/t5551-http-fetch.sh @@ -130,7 +130,7 @@ test_expect_success EXPENSIVE 'create 50,000 tags in the repo' ' done | git fast-import --export-marks=marks && # now assign tags to all the dangling commits we created above - tag=$(perl -e "print \"bla\" x 30") && + tag=$("$PERL_PATH" -e "print \"bla\" x 30") && sed -e "s/^:\(.\+\) \(.\+\)$/\2 refs\/tags\/$tag-\1/" <marks >>packed-refs ) ' diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh index e51eb41f4b..bbb0581f88 100755 --- a/t/t6011-rev-list-with-bad-commit.sh +++ b/t/t6011-rev-list-with-bad-commit.sh @@ -37,7 +37,7 @@ test_expect_success 'verify number of revisions' \ test_expect_success 'corrupt second commit object' \ ' - perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack && + "$PERL_PATH" -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack && test_must_fail git fsck --full ' diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh index 59fc2f06e0..892a537989 100755 --- a/t/t6013-rev-list-reverse-parents.sh +++ b/t/t6013-rev-list-reverse-parents.sh @@ -25,7 +25,7 @@ test_expect_success 'set up --reverse example' ' test_expect_success '--reverse --parents --full-history combines correctly' ' git rev-list --parents --full-history master -- foo | - perl -e "print reverse <>" > expected && + "$PERL_PATH" -e "print reverse <>" > expected && git rev-list --reverse --parents --full-history master -- foo \ > actual && test_cmp actual expected @@ -33,7 +33,7 @@ test_expect_success '--reverse --parents --full-history combines correctly' ' test_expect_success '--boundary does too' ' git rev-list --boundary --parents --full-history master ^root -- foo | - perl -e "print reverse <>" > expected && + "$PERL_PATH" -e "print reverse <>" > expected && git rev-list --boundary --reverse --parents --full-history \ master ^root -- foo > actual && test_cmp actual expected diff --git a/t/t7508-status.sh b/t/t7508-status.sh index 28e184829c..c206f4777a 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -941,7 +941,7 @@ test_expect_success 'status -s submodule summary (clean submodule)' ' test_expect_success 'status -z implies porcelain' ' git status --porcelain | - perl -pe "s/\012/\000/g" >expect && + "$PERL_PATH" -pe "s/\012/\000/g" >expect && git status -z >output && test_cmp expect output ' diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh index c3c22f7764..bf6caa4dc3 100755 --- a/t/t8006-blame-textconv.sh +++ b/t/t8006-blame-textconv.sh @@ -10,7 +10,7 @@ find_blame() { cat >helper <<'EOF' #!/bin/sh grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; } -perl -p -e 's/^bin: /converted: /' "$1" +"$PERL_PATH" -p -e 's/^bin: /converted: /' "$1" EOF chmod +x helper diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 8cfdfe790f..9a40f1e199 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -29,7 +29,7 @@ fi compare_svn_head_with () { # extract just the log message and strip out committer info. # don't use --limit here since svn 1.1.x doesn't have it, - LC_ALL="$a_utf8_locale" svn log `git svn info --url` | perl -w -e ' + LC_ALL="$a_utf8_locale" svn log `git svn info --url` | "$PERL_PATH" -w -e ' use bytes; $/ = ("-"x72) . "\n"; my @x = <STDIN>; diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh index d60da63f7a..c17aa3186f 100755 --- a/t/t9137-git-svn-dcommit-clobber-series.sh +++ b/t/t9137-git-svn-dcommit-clobber-series.sh @@ -20,8 +20,8 @@ test_expect_success '(supposedly) non-conflicting change from SVN' ' test x"`sed -n -e 61p < file`" = x61 && svn_cmd co "$svnrepo" tmp && (cd tmp && - perl -i.bak -p -e "s/^58$/5588/" file && - perl -i.bak -p -e "s/^61$/6611/" file && + "$PERL_PATH" -i.bak -p -e "s/^58$/5588/" file && + "$PERL_PATH" -i.bak -p -e "s/^61$/6611/" file && poke file && test x"`sed -n -e 58p < file`" = x5588 && test x"`sed -n -e 61p < file`" = x6611 && @@ -40,8 +40,8 @@ test_expect_success 'some unrelated changes to git' " test_expect_success 'change file but in unrelated area' " test x\"\`sed -n -e 4p < file\`\" = x4 && test x\"\`sed -n -e 7p < file\`\" = x7 && - perl -i.bak -p -e 's/^4\$/4444/' file && - perl -i.bak -p -e 's/^7\$/7777/' file && + "$PERL_PATH" -i.bak -p -e 's/^4\$/4444/' file && + "$PERL_PATH" -i.bak -p -e 's/^7\$/7777/' file && test x\"\`sed -n -e 4p < file\`\" = x4444 && test x\"\`sed -n -e 7p < file\`\" = x7777 && git commit -m '4 => 4444, 7 => 7777' file && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index c17f52e586..2fcf269469 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -12,7 +12,7 @@ test_description='test git fast-import utility' # This could be written as "head -c $1", but IRIX "head" does not # support the -c option. head_c () { - perl -e ' + "$PERL_PATH" -e ' my $len = $ARGV[1]; while ($len > 0) { my $s; diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index b00196bd23..3e821f958b 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -424,13 +424,13 @@ test_expect_success 'fast-export quotes pathnames' ' --cacheinfo 100644 $blob "path with \\backslash" \ --cacheinfo 100644 $blob "path with space" && git commit -m addition && - git ls-files -z -s | perl -0pe "s{\\t}{$&subdir/}" >index && + git ls-files -z -s | "$PERL_PATH" -0pe "s{\\t}{$&subdir/}" >index && git read-tree --empty && git update-index -z --index-info <index && git commit -m rename && git read-tree --empty && git commit -m deletion && - git fast-export HEAD >export.out && + git fast-export -M HEAD >export.out && git rev-list HEAD >expect && git init result && cd result && diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 0f410c45f7..07c2e157cb 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -45,7 +45,8 @@ test_expect_success 'git p4 sync uninitialized repo' ' test_when_finished cleanup_git && ( cd "$git" && - test_must_fail git p4 sync + test_must_fail git p4 sync 2>errs && + test_i18ngrep "Perhaps you never did" errs ) ' @@ -126,150 +127,24 @@ test_expect_success 'clone two dirs, @all, conflicting files' ' ' test_expect_success 'exit when p4 fails to produce marshaled output' ' - badp4dir="$TRASH_DIRECTORY/badp4dir" && - mkdir "$badp4dir" && - test_when_finished "rm \"$badp4dir/p4\" && rmdir \"$badp4dir\"" && - cat >"$badp4dir"/p4 <<-EOF && + mkdir badp4dir && + test_when_finished "rm badp4dir/p4 && rmdir badp4dir" && + cat >badp4dir/p4 <<-EOF && #!$SHELL_PATH exit 1 EOF - chmod 755 "$badp4dir"/p4 && - PATH="$badp4dir:$PATH" git p4 clone --dest="$git" //depot >errs 2>&1 ; retval=$? && - test $retval -eq 1 && - test_must_fail grep -q Traceback errs -' - -test_expect_success 'add p4 files with wildcards in the names' ' - ( - cd "$cli" && - echo file-wild-hash >file-wild#hash && - echo file-wild-star >file-wild\*star && - echo file-wild-at >file-wild@at && - echo file-wild-percent >file-wild%percent && - p4 add -f file-wild* && - p4 submit -d "file wildcards" - ) -' - -test_expect_success 'wildcard files git p4 clone' ' - git p4 clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - test -f file-wild#hash && - test -f file-wild\*star && - test -f file-wild@at && - test -f file-wild%percent - ) -' - -test_expect_success 'wildcard files submit back to p4, add' ' - test_when_finished cleanup_git && - git p4 clone --dest="$git" //depot && - ( - cd "$git" && - echo git-wild-hash >git-wild#hash && - echo git-wild-star >git-wild\*star && - echo git-wild-at >git-wild@at && - echo git-wild-percent >git-wild%percent && - git add git-wild* && - git commit -m "add some wildcard filenames" && - git config git-p4.skipSubmitEdit true && - git p4 submit - ) && - ( - cd "$cli" && - test_path_is_file git-wild#hash && - test_path_is_file git-wild\*star && - test_path_is_file git-wild@at && - test_path_is_file git-wild%percent - ) -' - -test_expect_success 'wildcard files submit back to p4, modify' ' - test_when_finished cleanup_git && - git p4 clone --dest="$git" //depot && - ( - cd "$git" && - echo new-line >>git-wild#hash && - echo new-line >>git-wild\*star && - echo new-line >>git-wild@at && - echo new-line >>git-wild%percent && - git add git-wild* && - git commit -m "modify the wildcard files" && - git config git-p4.skipSubmitEdit true && - git p4 submit - ) && - ( - cd "$cli" && - test_line_count = 2 git-wild#hash && - test_line_count = 2 git-wild\*star && - test_line_count = 2 git-wild@at && - test_line_count = 2 git-wild%percent - ) -' - -test_expect_success 'wildcard files submit back to p4, copy' ' - test_when_finished cleanup_git && - git p4 clone --dest="$git" //depot && - ( - cd "$git" && - cp file2 git-wild-cp#hash && - git add git-wild-cp#hash && - cp git-wild\*star file-wild-3 && - git add file-wild-3 && - git commit -m "wildcard copies" && - git config git-p4.detectCopies true && - git config git-p4.detectCopiesHarder true && - git config git-p4.skipSubmitEdit true && - git p4 submit - ) && - ( - cd "$cli" && - test_path_is_file git-wild-cp#hash && - test_path_is_file file-wild-3 - ) -' - -test_expect_success 'wildcard files submit back to p4, rename' ' - test_when_finished cleanup_git && - git p4 clone --dest="$git" //depot && - ( - cd "$git" && - git mv git-wild@at file-wild-4 && - git mv file-wild-3 git-wild-cp%percent && - git commit -m "wildcard renames" && - git config git-p4.detectRenames true && - git config git-p4.skipSubmitEdit true && - git p4 submit - ) && - ( - cd "$cli" && - test_path_is_missing git-wild@at && - test_path_is_file git-wild-cp%percent - ) -' - -test_expect_success 'wildcard files submit back to p4, delete' ' - test_when_finished cleanup_git && - git p4 clone --dest="$git" //depot && + chmod 755 badp4dir/p4 && ( - cd "$git" && - git rm git-wild* && - git commit -m "delete the wildcard files" && - git config git-p4.skipSubmitEdit true && - git p4 submit + PATH="$TRASH_DIRECTORY/badp4dir:$PATH" && + export PATH && + test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1 ) && - ( - cd "$cli" && - test_path_is_missing git-wild#hash && - test_path_is_missing git-wild\*star && - test_path_is_missing git-wild@at && - test_path_is_missing git-wild%percent - ) + cat errs && + ! test_i18ngrep Traceback errs ' test_expect_success 'clone bare' ' + rm -rf "$git" && git p4 clone --dest="$git" --bare //depot && test_when_finished cleanup_git && ( @@ -280,133 +155,6 @@ test_expect_success 'clone bare' ' ) ' -p4_add_user() { - name=$1 fullname=$2 && - p4 user -f -i <<-EOF && - User: $name - Email: $name@localhost - FullName: $fullname - EOF - p4 passwd -P secret $name -} - -p4_grant_admin() { - name=$1 && - { - p4 protect -o && - echo " admin user $name * //depot/..." - } | p4 protect -i -} - -p4_check_commit_author() { - file=$1 user=$2 && - p4 changes -m 1 //depot/$file | grep -q $user -} - -make_change_by_user() { - file=$1 name=$2 email=$3 && - echo "username: a change by $name" >>"$file" && - git add "$file" && - git commit --author "$name <$email>" -m "a change by $name" -} - -# Test username support, submitting as user 'alice' -test_expect_success 'preserve users' ' - p4_add_user alice Alice && - p4_add_user bob Bob && - p4_grant_admin alice && - git p4 clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - echo "username: a change by alice" >>file1 && - echo "username: a change by bob" >>file2 && - git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 && - git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 && - git config git-p4.skipSubmitEditCheck true && - P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user && - p4_check_commit_author file1 alice && - p4_check_commit_author file2 bob - ) -' - -# Test username support, submitting as bob, who lacks admin rights. Should -# not submit change to p4 (git diff should show deltas). -test_expect_success 'refuse to preserve users without perms' ' - git p4 clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEditCheck true && - echo "username-noperms: a change by alice" >>file1 && - git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 && - P4EDITOR=touch P4USER=bob P4PASSWD=secret && - export P4EDITOR P4USER P4PASSWD && - test_must_fail git p4 commit --preserve-user && - ! git diff --exit-code HEAD..p4/master - ) -' - -# What happens with unknown author? Without allowMissingP4Users it should fail. -test_expect_success 'preserve user where author is unknown to p4' ' - git p4 clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEditCheck true && - echo "username-bob: a change by bob" >>file1 && - git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 && - echo "username-unknown: a change by charlie" >>file1 && - git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 && - P4EDITOR=touch P4USER=alice P4PASSWD=secret && - export P4EDITOR P4USER P4PASSWD && - test_must_fail git p4 commit --preserve-user && - ! git diff --exit-code HEAD..p4/master && - - echo "$0: repeat with allowMissingP4Users enabled" && - git config git-p4.allowMissingP4Users true && - git config git-p4.preserveUser true && - git p4 commit && - git diff --exit-code HEAD..p4/master && - p4_check_commit_author file1 alice - ) -' - -# If we're *not* using --preserve-user, git p4 should warn if we're submitting -# changes that are not all ours. -# Test: user in p4 and user unknown to p4. -# Test: warning disabled and user is the same. -test_expect_success 'not preserving user with mixed authorship' ' - git p4 clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEditCheck true && - p4_add_user derek Derek && - - make_change_by_user usernamefile3 Derek derek@localhost && - P4EDITOR=cat P4USER=alice P4PASSWD=secret && - export P4EDITOR P4USER P4PASSWD && - git p4 commit |\ - grep "git author derek@localhost does not match" && - - make_change_by_user usernamefile3 Charlie charlie@localhost && - git p4 commit |\ - grep "git author charlie@localhost does not match" && - - make_change_by_user usernamefile3 alice alice@localhost && - git p4 commit |\ - test_must_fail grep "git author.*does not match" && - - git config git-p4.skipUserNameCheck true && - make_change_by_user usernamefile3 Charlie charlie@localhost && - git p4 commit |\ - test_must_fail grep "git author.*does not match" && - - p4_check_commit_author usernamefile3 alice - ) -' - marshal_dump() { what=$1 "$PYTHON_PATH" -c 'import marshal, sys; d = marshal.load(sys.stdin); print d["'$what'"]' @@ -429,146 +177,6 @@ test_expect_success 'initial import time from top change time' ' ) ' -# Rename a file and confirm that rename is not detected in P4. -# Rename the new file again with detectRenames option enabled and confirm that -# this is detected in P4. -# Rename the new file again adding an extra line, configure a big threshold in -# detectRenames and confirm that rename is not detected in P4. -# Repeat, this time with a smaller threshold and confirm that the rename is -# detected in P4. -test_expect_success 'detect renames' ' - git p4 clone --dest="$git" //depot@all && - test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEdit true && - - git mv file1 file4 && - git commit -a -m "Rename file1 to file4" && - git diff-tree -r -M HEAD && - git p4 submit && - p4 filelog //depot/file4 && - p4 filelog //depot/file4 | test_must_fail grep -q "branch from" && - - git mv file4 file5 && - git commit -a -m "Rename file4 to file5" && - git diff-tree -r -M HEAD && - git config git-p4.detectRenames true && - git p4 submit && - p4 filelog //depot/file5 && - p4 filelog //depot/file5 | grep -q "branch from //depot/file4" && - - git mv file5 file6 && - echo update >>file6 && - git add file6 && - git commit -a -m "Rename file5 to file6 with changes" && - git diff-tree -r -M HEAD && - level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && - test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && - git config git-p4.detectRenames $(($level + 2)) && - git p4 submit && - p4 filelog //depot/file6 && - p4 filelog //depot/file6 | test_must_fail grep -q "branch from" && - - git mv file6 file7 && - echo update >>file7 && - git add file7 && - git commit -a -m "Rename file6 to file7 with changes" && - git diff-tree -r -M HEAD && - level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && - test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && - git config git-p4.detectRenames $(($level - 2)) && - git p4 submit && - p4 filelog //depot/file7 && - p4 filelog //depot/file7 | grep -q "branch from //depot/file6" - ) -' - -# Copy a file and confirm that copy is not detected in P4. -# Copy a file with detectCopies option enabled and confirm that copy is not -# detected in P4. -# Modify and copy a file with detectCopies option enabled and confirm that copy -# is detected in P4. -# Copy a file with detectCopies and detectCopiesHarder options enabled and -# confirm that copy is detected in P4. -# Modify and copy a file, configure a bigger threshold in detectCopies and -# confirm that copy is not detected in P4. -# Modify and copy a file, configure a smaller threshold in detectCopies and -# confirm that copy is detected in P4. -test_expect_success 'detect copies' ' - git p4 clone --dest="$git" //depot@all && - test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEdit true && - - cp file2 file8 && - git add file8 && - git commit -a -m "Copy file2 to file8" && - git diff-tree -r -C HEAD && - git p4 submit && - p4 filelog //depot/file8 && - p4 filelog //depot/file8 | test_must_fail grep -q "branch from" && - - cp file2 file9 && - git add file9 && - git commit -a -m "Copy file2 to file9" && - git diff-tree -r -C HEAD && - git config git-p4.detectCopies true && - git p4 submit && - p4 filelog //depot/file9 && - p4 filelog //depot/file9 | test_must_fail grep -q "branch from" && - - echo "file2" >>file2 && - cp file2 file10 && - git add file2 file10 && - git commit -a -m "Modify and copy file2 to file10" && - git diff-tree -r -C HEAD && - git p4 submit && - p4 filelog //depot/file10 && - p4 filelog //depot/file10 | grep -q "branch from //depot/file" && - - cp file2 file11 && - git add file11 && - git commit -a -m "Copy file2 to file11" && - git diff-tree -r -C --find-copies-harder HEAD && - src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && - test "$src" = file10 && - git config git-p4.detectCopiesHarder true && - git p4 submit && - p4 filelog //depot/file11 && - p4 filelog //depot/file11 | grep -q "branch from //depot/file" && - - cp file2 file12 && - echo "some text" >>file12 && - git add file12 && - git commit -a -m "Copy file2 to file12 with changes" && - git diff-tree -r -C --find-copies-harder HEAD && - level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && - test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && - src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && - test "$src" = file10 && - git config git-p4.detectCopies $(($level + 2)) && - git p4 submit && - p4 filelog //depot/file12 && - p4 filelog //depot/file12 | test_must_fail grep -q "branch from" && - - cp file2 file13 && - echo "different text" >>file13 && - git add file13 && - git commit -a -m "Copy file2 to file13 with changes" && - git diff-tree -r -C --find-copies-harder HEAD && - level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && - test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && - src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && - test "$src" = file10 && - git config git-p4.detectCopies $(($level - 2)) && - git p4 submit && - p4 filelog //depot/file13 && - p4 filelog //depot/file13 | grep -q "branch from //depot/file" - ) -' - test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh index 353dcfbe09..fb3c8ec12c 100755 --- a/t/t9805-git-p4-skip-submit-edit.sh +++ b/t/t9805-git-p4-skip-submit-edit.sh @@ -78,20 +78,19 @@ test_expect_success 'skipSubmitEditCheck' ' test_expect_success 'no config, edited' ' git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && - ed="$TRASH_DIRECTORY/ed.sh" && - test_when_finished "rm \"$ed\"" && - cat >"$ed" <<-EOF && + test_when_finished "rm ed.sh" && + cat >ed.sh <<-EOF && #!$SHELL_PATH sleep 1 touch "\$1" exit 0 EOF - chmod 755 "$ed" && + chmod 755 ed.sh && ( cd "$git" && echo line >>file1 && git commit -a -m "change 5" && - P4EDITOR="" EDITOR="\"$ed\"" git p4 submit && + P4EDITOR="" EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" git p4 submit && p4 changes //depot/... >wc && test_line_count = 5 wc ) diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 2892367830..fa40cc8bb5 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -39,10 +39,9 @@ test_expect_success 'clone --branch' ' ' test_expect_success 'clone --changesfile' ' - cf="$TRASH_DIRECTORY/cf" && - test_when_finished "rm \"$cf\"" && - printf "1\n3\n" >"$cf" && - git p4 clone --changesfile="$cf" --dest="$git" //depot && + test_when_finished "rm cf" && + printf "1\n3\n" >cf && + git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -55,10 +54,9 @@ test_expect_success 'clone --changesfile' ' ' test_expect_success 'clone --changesfile, @all' ' - cf="$TRASH_DIRECTORY/cf" && - test_when_finished "rm \"$cf\"" && - printf "1\n3\n" >"$cf" && - test_must_fail git p4 clone --changesfile="$cf" --dest="$git" //depot@all + test_when_finished "rm cf" && + printf "1\n3\n" >cf && + test_must_fail git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot@all ' # imports both master and p4/master in refs/heads @@ -128,7 +126,7 @@ test_expect_success 'clone --use-client-spec' ' exec >/dev/null && test_must_fail git p4 clone --dest="$git" --use-client-spec ) && - cli2="$TRASH_DIRECTORY/cli2" && + cli2=$(test-path-utils real_path "$TRASH_DIRECTORY/cli2") && mkdir -p "$cli2" && test_when_finished "rmdir \"$cli2\"" && ( @@ -151,7 +149,6 @@ test_expect_success 'clone --use-client-spec' ' cleanup_git && # same thing again, this time with variable instead of option - mkdir "$git" && ( cd "$git" && git init && diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh index 2f8014a60e..dc92e60cd6 100755 --- a/t/t9808-git-p4-chdir.sh +++ b/t/t9808-git-p4-chdir.sh @@ -21,7 +21,7 @@ test_expect_success 'init depot' ' # environment variable is set test_expect_success 'P4CONFIG and absolute dir clone' ' printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config && - test_when_finished "rm \"$TRASH_DIRECTORY/p4config\"" && + test_when_finished "rm p4config" && test_when_finished cleanup_git && ( P4CONFIG=p4config && export P4CONFIG && @@ -33,7 +33,7 @@ test_expect_success 'P4CONFIG and absolute dir clone' ' # same thing, but with relative directory name, note missing $ on --dest test_expect_success 'P4CONFIG and relative dir clone' ' printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config && - test_when_finished "rm \"$TRASH_DIRECTORY/p4config\"" && + test_when_finished "rm p4config" && test_when_finished cleanup_git && ( P4CONFIG=p4config && export P4CONFIG && diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index d8d9ca4679..e9daa9c4f6 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 rcs keywords' +test_description='git p4 rcs keywords' . ./lib-git-p4.sh @@ -147,7 +147,7 @@ test_expect_success 'scrub ko files differently' ' ) ' -# hack; git-p4 submit should do it on its own +# hack; git p4 submit should do it on its own test_expect_success 'cleanup after failure' ' ( cd "$cli" && @@ -193,7 +193,7 @@ test_expect_success 'do not scrub plain text' ' ) ' -# hack; git-p4 submit should do it on its own +# hack; git p4 submit should do it on its own test_expect_success 'cleanup after failure 2' ' ( cd "$cli" && @@ -244,9 +244,9 @@ test_expect_success 'cope with rcs keyword expansion damage' ' cd "$git" && git config git-p4.skipSubmitEdit true && git config git-p4.attemptRCSCleanup true && - (cd ../cli && p4_append_to_file kwfile1.c) && + (cd "$cli" && p4_append_to_file kwfile1.c) && old_lines=$(wc -l <kwfile1.c) && - perl -n -i -e "print unless m/Revision:/" kwfile1.c && + "$PERL_PATH" -n -i -e "print unless m/Revision:/" kwfile1.c && new_lines=$(wc -l <kwfile1.c) && test $new_lines = $(($old_lines - 1)) && diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh new file mode 100755 index 0000000000..143d413057 --- /dev/null +++ b/t/t9812-git-p4-wildcards.sh @@ -0,0 +1,147 @@ +#!/bin/sh + +test_description='git p4 wildcards' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'add p4 files with wildcards in the names' ' + ( + cd "$cli" && + printf "file2\nhas\nsome\nrandom\ntext\n" >file2 && + p4 add file2 && + echo file-wild-hash >file-wild#hash && + echo file-wild-star >file-wild\*star && + echo file-wild-at >file-wild@at && + echo file-wild-percent >file-wild%percent && + p4 add -f file-wild* && + p4 submit -d "file wildcards" + ) +' + +test_expect_success 'wildcard files git p4 clone' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + test -f file-wild#hash && + test -f file-wild\*star && + test -f file-wild@at && + test -f file-wild%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, add' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo git-wild-hash >git-wild#hash && + echo git-wild-star >git-wild\*star && + echo git-wild-at >git-wild@at && + echo git-wild-percent >git-wild%percent && + git add git-wild* && + git commit -m "add some wildcard filenames" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file git-wild#hash && + test_path_is_file git-wild\*star && + test_path_is_file git-wild@at && + test_path_is_file git-wild%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, modify' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo new-line >>git-wild#hash && + echo new-line >>git-wild\*star && + echo new-line >>git-wild@at && + echo new-line >>git-wild%percent && + git add git-wild* && + git commit -m "modify the wildcard files" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_line_count = 2 git-wild#hash && + test_line_count = 2 git-wild\*star && + test_line_count = 2 git-wild@at && + test_line_count = 2 git-wild%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, copy' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + cp file2 git-wild-cp#hash && + git add git-wild-cp#hash && + cp git-wild\*star file-wild-3 && + git add file-wild-3 && + git commit -m "wildcard copies" && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file git-wild-cp#hash && + test_path_is_file file-wild-3 + ) +' + +test_expect_success 'wildcard files submit back to p4, rename' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git mv git-wild@at file-wild-4 && + git mv file-wild-3 git-wild-cp%percent && + git commit -m "wildcard renames" && + git config git-p4.detectRenames true && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing git-wild@at && + test_path_is_file git-wild-cp%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, delete' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git rm git-wild* && + git commit -m "delete the wildcard files" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing git-wild#hash && + test_path_is_missing git-wild\*star && + test_path_is_missing git-wild@at && + test_path_is_missing git-wild%percent + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh new file mode 100755 index 0000000000..f2e85e518b --- /dev/null +++ b/t/t9813-git-p4-preserve-users.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +test_description='git p4 preserve users' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'create files' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + echo file1 >file1 && + echo file2 >file2 && + p4 add file1 file2 && + p4 submit -d "add files" + ) +' + +p4_add_user() { + name=$1 fullname=$2 && + p4 user -f -i <<-EOF && + User: $name + Email: $name@localhost + FullName: $fullname + EOF + p4 passwd -P secret $name +} + +p4_grant_admin() { + name=$1 && + { + p4 protect -o && + echo " admin user $name * //depot/..." + } | p4 protect -i +} + +p4_check_commit_author() { + file=$1 user=$2 && + p4 changes -m 1 //depot/$file | grep -q $user +} + +make_change_by_user() { + file=$1 name=$2 email=$3 && + echo "username: a change by $name" >>"$file" && + git add "$file" && + git commit --author "$name <$email>" -m "a change by $name" +} + +# Test username support, submitting as user 'alice' +test_expect_success 'preserve users' ' + p4_add_user alice Alice && + p4_add_user bob Bob && + p4_grant_admin alice && + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + echo "username: a change by alice" >>file1 && + echo "username: a change by bob" >>file2 && + git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 && + git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 && + git config git-p4.skipSubmitEditCheck true && + P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user && + p4_check_commit_author file1 alice && + p4_check_commit_author file2 bob + ) +' + +# Test username support, submitting as bob, who lacks admin rights. Should +# not submit change to p4 (git diff should show deltas). +test_expect_success 'refuse to preserve users without perms' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEditCheck true && + echo "username-noperms: a change by alice" >>file1 && + git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 && + P4EDITOR=touch P4USER=bob P4PASSWD=secret && + export P4EDITOR P4USER P4PASSWD && + test_must_fail git p4 commit --preserve-user && + ! git diff --exit-code HEAD..p4/master + ) +' + +# What happens with unknown author? Without allowMissingP4Users it should fail. +test_expect_success 'preserve user where author is unknown to p4' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEditCheck true && + echo "username-bob: a change by bob" >>file1 && + git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 && + echo "username-unknown: a change by charlie" >>file1 && + git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 && + P4EDITOR=touch P4USER=alice P4PASSWD=secret && + export P4EDITOR P4USER P4PASSWD && + test_must_fail git p4 commit --preserve-user && + ! git diff --exit-code HEAD..p4/master && + + echo "$0: repeat with allowMissingP4Users enabled" && + git config git-p4.allowMissingP4Users true && + git config git-p4.preserveUser true && + git p4 commit && + git diff --exit-code HEAD..p4/master && + p4_check_commit_author file1 alice + ) +' + +# If we're *not* using --preserve-user, git-p4 should warn if we're submitting +# changes that are not all ours. +# Test: user in p4 and user unknown to p4. +# Test: warning disabled and user is the same. +test_expect_success 'not preserving user with mixed authorship' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEditCheck true && + p4_add_user derek Derek && + + make_change_by_user usernamefile3 Derek derek@localhost && + P4EDITOR=cat P4USER=alice P4PASSWD=secret && + export P4EDITOR P4USER P4PASSWD && + git p4 commit |\ + grep "git author derek@localhost does not match" && + + make_change_by_user usernamefile3 Charlie charlie@localhost && + git p4 commit |\ + grep "git author charlie@localhost does not match" && + + make_change_by_user usernamefile3 alice alice@localhost && + git p4 commit |\ + test_must_fail grep "git author.*does not match" && + + git config git-p4.skipUserNameCheck true && + make_change_by_user usernamefile3 Charlie charlie@localhost && + git p4 commit |\ + test_must_fail grep "git author.*does not match" && + + p4_check_commit_author usernamefile3 alice + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh new file mode 100755 index 0000000000..84fffb3142 --- /dev/null +++ b/t/t9814-git-p4-rename.sh @@ -0,0 +1,206 @@ +#!/bin/sh + +test_description='git p4 rename' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +# We rely on this behavior to detect for p4 move availability. +test_expect_success 'p4 help unknown returns 1' ' + ( + cd "$cli" && + ( + p4 help client >errs 2>&1 + echo $? >retval + ) + echo 0 >expected && + test_cmp expected retval && + rm retval && + ( + p4 help nosuchcommand >errs 2>&1 + echo $? >retval + ) + echo 1 >expected && + test_cmp expected retval && + rm retval + ) +' + +test_expect_success 'create files' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + cat >file1 <<-EOF && + A large block of text + in file1 that will generate + enough context so that rename + and copy detection will find + something interesting to do. + EOF + cat >file2 <<-EOF && + /* + * This blob looks a bit + * different. + */ + int main(int argc, char **argv) + { + char text[200]; + + strcpy(text, "copy/rename this"); + printf("text is %s\n", text); + return 0; + } + EOF + p4 add file1 file2 && + p4 submit -d "add files" + ) +' + +# Rename a file and confirm that rename is not detected in P4. +# Rename the new file again with detectRenames option enabled and confirm that +# this is detected in P4. +# Rename the new file again adding an extra line, configure a big threshold in +# detectRenames and confirm that rename is not detected in P4. +# Repeat, this time with a smaller threshold and confirm that the rename is +# detected in P4. +test_expect_success 'detect renames' ' + git p4 clone --dest="$git" //depot@all && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + + git mv file1 file4 && + git commit -a -m "Rename file1 to file4" && + git diff-tree -r -M HEAD && + git p4 submit && + p4 filelog //depot/file4 && + p4 filelog //depot/file4 | test_must_fail grep -q "branch from" && + + git mv file4 file5 && + git commit -a -m "Rename file4 to file5" && + git diff-tree -r -M HEAD && + git config git-p4.detectRenames true && + git p4 submit && + p4 filelog //depot/file5 && + p4 filelog //depot/file5 | grep -q "branch from //depot/file4" && + + git mv file5 file6 && + echo update >>file6 && + git add file6 && + git commit -a -m "Rename file5 to file6 with changes" && + git diff-tree -r -M HEAD && + level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && + test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && + git config git-p4.detectRenames $(($level + 2)) && + git p4 submit && + p4 filelog //depot/file6 && + p4 filelog //depot/file6 | test_must_fail grep -q "branch from" && + + git mv file6 file7 && + echo update >>file7 && + git add file7 && + git commit -a -m "Rename file6 to file7 with changes" && + git diff-tree -r -M HEAD && + level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && + test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && + git config git-p4.detectRenames $(($level - 2)) && + git p4 submit && + p4 filelog //depot/file7 && + p4 filelog //depot/file7 | grep -q "branch from //depot/file6" + ) +' + +# Copy a file and confirm that copy is not detected in P4. +# Copy a file with detectCopies option enabled and confirm that copy is not +# detected in P4. +# Modify and copy a file with detectCopies option enabled and confirm that copy +# is detected in P4. +# Copy a file with detectCopies and detectCopiesHarder options enabled and +# confirm that copy is detected in P4. +# Modify and copy a file, configure a bigger threshold in detectCopies and +# confirm that copy is not detected in P4. +# Modify and copy a file, configure a smaller threshold in detectCopies and +# confirm that copy is detected in P4. +test_expect_success 'detect copies' ' + git p4 clone --dest="$git" //depot@all && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + + cp file2 file8 && + git add file8 && + git commit -a -m "Copy file2 to file8" && + git diff-tree -r -C HEAD && + git p4 submit && + p4 filelog //depot/file8 && + p4 filelog //depot/file8 | test_must_fail grep -q "branch from" && + + cp file2 file9 && + git add file9 && + git commit -a -m "Copy file2 to file9" && + git diff-tree -r -C HEAD && + git config git-p4.detectCopies true && + git p4 submit && + p4 filelog //depot/file9 && + p4 filelog //depot/file9 | test_must_fail grep -q "branch from" && + + echo "file2" >>file2 && + cp file2 file10 && + git add file2 file10 && + git commit -a -m "Modify and copy file2 to file10" && + git diff-tree -r -C HEAD && + git p4 submit && + p4 filelog //depot/file10 && + p4 filelog //depot/file10 | grep -q "branch from //depot/file" && + + cp file2 file11 && + git add file11 && + git commit -a -m "Copy file2 to file11" && + git diff-tree -r -C --find-copies-harder HEAD && + src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && + test "$src" = file10 && + git config git-p4.detectCopiesHarder true && + git p4 submit && + p4 filelog //depot/file11 && + p4 filelog //depot/file11 | grep -q "branch from //depot/file" && + + cp file2 file12 && + echo "some text" >>file12 && + git add file12 && + git commit -a -m "Copy file2 to file12 with changes" && + git diff-tree -r -C --find-copies-harder HEAD && + level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && + test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && + src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && + test "$src" = file10 -o "$src" = file11 && + git config git-p4.detectCopies $(($level + 2)) && + git p4 submit && + p4 filelog //depot/file12 && + p4 filelog //depot/file12 | test_must_fail grep -q "branch from" && + + cp file2 file13 && + echo "different text" >>file13 && + git add file13 && + git commit -a -m "Copy file2 to file13 with changes" && + git diff-tree -r -C --find-copies-harder HEAD && + level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && + test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && + src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && + test "$src" = file10 -o "$src" = file11 -o "$src" = file12 && + git config git-p4.detectCopies $(($level - 2)) && + git p4 submit && + p4 filelog //depot/file13 && + p4 filelog //depot/file13 | grep -q "branch from //depot/file" + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 7b3b4bef30..16397691d9 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -76,11 +76,11 @@ test_decode_color () { } nul_to_q () { - perl -pe 'y/\000/Q/' + "$PERL_PATH" -pe 'y/\000/Q/' } q_to_nul () { - perl -pe 'y/Q/\000/' + "$PERL_PATH" -pe 'y/Q/\000/' } q_to_cr () { diff --git a/t/test-lib.sh b/t/test-lib.sh index 9e2b71132a..acda33d177 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -494,6 +494,8 @@ export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM . "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS +export PERL_PATH + if test -z "$GIT_TEST_CMP" then if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT" diff --git a/test-credential.c b/test-credential.c deleted file mode 100644 index dee200e7f2..0000000000 --- a/test-credential.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "cache.h" -#include "credential.h" -#include "string-list.h" - -static const char usage_msg[] = -"test-credential <fill|approve|reject> [helper...]"; - -int main(int argc, const char **argv) -{ - const char *op; - struct credential c = CREDENTIAL_INIT; - int i; - - op = argv[1]; - if (!op) - usage(usage_msg); - for (i = 2; i < argc; i++) - string_list_append(&c.helpers, argv[i]); - - if (credential_read(&c, stdin) < 0) - die("unable to read credential from stdin"); - - if (!strcmp(op, "fill")) { - credential_fill(&c); - if (c.username) - printf("username=%s\n", c.username); - if (c.password) - printf("password=%s\n", c.password); - } - else if (!strcmp(op, "approve")) - credential_approve(&c); - else if (!strcmp(op, "reject")) - credential_reject(&c); - else - usage(usage_msg); - - return 0; -} |