diff options
79 files changed, 926 insertions, 376 deletions
diff --git a/Documentation/RelNotes/2.7.1.txt b/Documentation/RelNotes/2.7.1.txt new file mode 100644 index 0000000000..6553d69e33 --- /dev/null +++ b/Documentation/RelNotes/2.7.1.txt @@ -0,0 +1,87 @@ +Git v2.7.1 Release Notes +======================== + +Fixes since v2.7 +---------------- + + * An earlier change in 2.5.x-era broke users' hooks and aliases by + exporting GIT_WORK_TREE to point at the root of the working tree, + interfering when they tried to use a different working tree without + setting GIT_WORK_TREE environment themselves. + + * The "exclude_list" structure has the usual "alloc, nr" pair of + fields to be used by ALLOC_GROW(), but clear_exclude_list() forgot + to reset 'alloc' to 0 when it cleared 'nr' to discard the managed + array. + + * "git send-email" was confused by escaped quotes stored in the alias + files saved by "mutt", which has been corrected. + + * A few unportable C construct have been spotted by clang compiler + and have been fixed. + + * The documentation has been updated to hint the connection between + the '--signoff' option and DCO. + + * "git reflog" incorrectly assumed that all objects that used to be + at the tip of a ref must be commits, which caused it to segfault. + + * The ignore mechanism saw a few regressions around untracked file + listing and sparse checkout selection areas in 2.7.0; the change + that is responsible for the regression has been reverted. + + * Some codepaths used fopen(3) when opening a fixed path in $GIT_DIR + (e.g. COMMIT_EDITMSG) that is meant to be left after the command is + done. This however did not work well if the repository is set to + be shared with core.sharedRepository and the umask of the previous + user is tighter. They have been made to work better by calling + unlink(2) and retrying after fopen(3) fails with EPERM. + + * Asking gitweb for a nonexistent commit left a warning in the server + log. + + * "git rebase", unlike all other callers of "gc --auto", did not + ignore the exit code from "gc --auto". + + * Many codepaths that run "gc --auto" before exiting kept packfiles + mapped and left the file descriptors to them open, which was not + friendly to systems that cannot remove files that are open. They + now close the packs before doing so. + + * A recent optimization to filter-branch in v2.7.0 introduced a + regression when --prune-empty filter is used, which has been + corrected. + + * The description for SANITY prerequisite the test suite uses has + been clarified both in the comment and in the implementation. + + * "git tag" started listing a tag "foo" as "tags/foo" when a branch + named "foo" exists in the same repository; remove this unnecessary + disambiguation, which is a regression introduced in v2.7.0. + + * The way "git svn" uses auth parameter was broken by Subversion + 1.9.0 and later. + + * The "split" subcommand of "git subtree" (in contrib/) incorrectly + skipped merges when it shouldn't, which was corrected. + + * A few options of "git diff" did not work well when the command was + run from a subdirectory. + + * dirname() emulation has been added, as Msys2 lacks it. + + * The underlying machinery used by "ls-files -o" and other commands + have been taught not to create empty submodule ref cache for a + directory that is not a submodule. This removes a ton of wasted + CPU cycles. + + * Drop a few old "todo" items by deciding that the change one of them + suggests is not such a good idea, and doing the change the other + one suggested to do. + + * Documentation for "git fetch --depth" has been updated for clarity. + + * The command line completion learned a handful of additional options + and command specific syntax. + +Also includes a handful of documentation and test updates. diff --git a/Documentation/RelNotes/2.7.2.txt b/Documentation/RelNotes/2.7.2.txt new file mode 100644 index 0000000000..f7846a2569 --- /dev/null +++ b/Documentation/RelNotes/2.7.2.txt @@ -0,0 +1,27 @@ +Git v2.7.2 Release Notes +======================== + +Fixes since v2.7.1 +------------------ + + * The low-level merge machinery has been taught to use CRLF line + termination when inserting conflict markers to merged contents that + are themselves CRLF line-terminated. + + * "git worktree" had a broken code that attempted to auto-fix + possible inconsistency that results from end-users moving a + worktree to different places without telling Git (the original + repository needs to maintain backpointers to its worktrees, but + "mv" run by end-users who are not familiar with that fact will + obviously not adjust them), which actually made things worse + when triggered. + + * "git push --force-with-lease" has been taught to report if the push + needed to force (or fast-forwarded). + + * The emulated "yes" command used in our test scripts has been + tweaked not to spend too much time generating unnecessary output + that is not used, to help those who test on Windows where it would + not stop until it fills the pipe buffer due to lack of SIGPIPE. + +Also includes tiny documentation and test updates. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 45583d8454..78cd2653cd 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -8,10 +8,11 @@ option old data in `.git/FETCH_HEAD` will be overwritten. --depth=<depth>:: - Deepen or shorten the history of a 'shallow' repository created by - `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1]) - to the specified number of commits from the tip of each remote - branch history. Tags for the deepened commits are not fetched. + Limit fetching to the specified number of commits from the tip of + each remote branch history. If fetching to a 'shallow' repository + created by `git clone` with `--depth=<depth>` option (see + linkgit:git-clone[1]), deepen or shorten the history to the specified + number of commits. Tags for the deepened commits are not fetched. --unshallow:: If the source repository is complete, convert a shallow diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index fe5282f130..6a96a669c2 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -24,7 +24,7 @@ remove paths that do not exist in the working tree anymore. The "index" holds a snapshot of the content of the working tree, and it is this snapshot that is taken as the contents of the next commit. Thus -after making any changes to the working directory, and before running +after making any changes to the working tree, and before running the commit command, you must use the `add` command to add any new or modified files to the index. diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 452c1feb23..13cdd7f3b6 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -35,6 +35,7 @@ OPTIONS --signoff:: Add a `Signed-off-by:` line to the commit message, using the committer identity of yourself. + See the signoff option in linkgit:git-commit[1] for more information. -k:: --keep:: diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 77da29a474..6154e57238 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -100,6 +100,7 @@ effect to your index in a row. -s:: --signoff:: Add Signed-off-by line at the end of the commit message. + See the signoff option in linkgit:git-commit[1] for more information. -S[<keyid>]:: --gpg-sign[=<keyid>]:: diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index 641681f61a..51a7e26a8e 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -37,9 +37,7 @@ OPTIONS to false, 'git clean' will refuse to delete files or directories unless given -f, -n or -i. Git will refuse to delete directories with .git sub directory or file unless a second -f - is given. This affects also git submodules where the storage area - of the removed submodule under .git/modules/ is not removed until - -f is given twice. + is given. -i:: --interactive:: diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 6bf000dac3..789b668f77 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -190,15 +190,14 @@ objects from the source repository into a pack in the cloned repository. --depth <depth>:: Create a 'shallow' clone with a history truncated to the - specified number of revisions. + specified number of commits. Implies `--single-branch` unless + `--no-single-branch` is given to fetch the histories near the + tips of all branches. --[no-]single-branch:: Clone only the history leading to the tip of a single branch, either specified by the `--branch` option or the primary - branch remote's `HEAD` points at. When creating a shallow - clone with the `--depth` option, this is the default, unless - `--no-single-branch` is given to fetch the histories near the - tips of all branches. + branch remote's `HEAD` points at. Further fetches into the resulting repository will only update the remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 7f34a5b331..9ec6b3cc17 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -154,7 +154,11 @@ OPTIONS -s:: --signoff:: Add Signed-off-by line by the committer at the end of the commit - log message. + log message. The meaning of a signoff depends on the project, + but it typically certifies that committer has + the rights to submit this work under the same license and + agrees to a Developer Certificate of Origin + (see http://developercertificate.org/ for more information). -n:: --no-verify:: diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index c6f073cea4..2e3e96f663 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -92,7 +92,11 @@ refname:: The name of the ref (the part after $GIT_DIR/). For a non-ambiguous short name of the ref append `:short`. The option core.warnAmbiguousRefs is used to select the strict - abbreviation mode. + abbreviation mode. If `strip=<N>` is appended, strips `<N>` + slash-separated path components from the front of the refname + (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`. + `<N>` must be a positive integer. If a displayed ref has fewer + components than `<N>`, the command aborts with an error. objecttype:: The type of the object (`blob`, `tree`, `commit`, `tag`). @@ -142,6 +146,11 @@ In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. +For commit and tag objects, the special `creatordate` and `creator` +fields will correspond to the appropriate date or name-email-date tuple +from the `committer` or `tagger` fields depending on the object type. +These are intended for working on a mix of annotated and lightweight tags. + Fields that have name-email-date tuple as its value (`author`, `committer`, and `tagger`) can be suffixed with `name`, `email`, and `date` to extract the named component. @@ -153,8 +162,8 @@ line is 'contents:body', where body is all of the lines after the first blank line. The optional GPG signature is `contents:signature`. The first `N` lines of the message is obtained using `contents:lines=N`. -For sorting purposes, fields with numeric values sort in numeric -order (`objectsize`, `authordate`, `committerdate`, `taggerdate`). +For sorting purposes, fields with numeric values sort in numeric order +(`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`). All other fields are used to sort in their byte-value order. There is also an option to sort by versions, this can be done by using diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index e3cdaeb958..b149e09065 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -109,6 +109,7 @@ include::diff-options.txt[] --signoff:: Add `Signed-off-by:` line to the commit message, using the committer identity of yourself. + See the signoff option in linkgit:git-commit[1] for more information. --stdout:: Print all commits to the standard output in mbox format, diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index b15139ffdc..573616a04a 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -89,6 +89,7 @@ effect to your index in a row. -s:: --signoff:: Add Signed-off-by line at the end of the commit message. + See the signoff option in linkgit:git-commit[1] for more information. --strategy=<strategy>:: Use the given merge strategy. Should only be used once. diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 7220e5eca1..abab4814ec 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -163,7 +163,7 @@ This option is only applicable when listing tags without annotation lines. A string that interpolates `%(fieldname)` from the object pointed at by a ref being shown. The format is the same as that of linkgit:git-for-each-ref[1]. When unspecified, - defaults to `%(refname:short)`. + defaults to `%(refname:strip=2)`. --[no-]merged [<commit>]:: Only list tags whose tips are reachable, or not reachable diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index 5b9ad0429c..62c76c1c89 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -32,11 +32,9 @@ The working tree's administrative files in the repository (see `git worktree prune` in the main or any linked working tree to clean up any stale administrative files. -If you move a linked working tree to another file system, or -within a file system that does not support hard links, you need to run -at least one git command inside the linked working tree -(e.g. `git status`) in order to update its administrative files in the -repository so that they do not get automatically pruned. +If you move a linked working tree, you need to manually update the +administrative files so that they do not get pruned automatically. See +section "DETAILS" for more information. If a linked working tree is stored on a portable device or network share which is not always mounted, you can prevent its administrative files from @@ -137,6 +135,13 @@ thumb is do not make any assumption about whether a path belongs to $GIT_DIR or $GIT_COMMON_DIR when you need to directly access something inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path. +If you move a linked working tree, you need to update the 'gitdir' file +in the entry's directory. For example, if a linked working tree is moved +to `/newpath/test-next` and its `.git` file points to +`/path/main/.git/worktrees/test-next`, then update +`/path/main/.git/worktrees/test-next/gitdir` to reference `/newpath/test-next` +instead. + To prevent a $GIT_DIR/worktrees entry from being pruned (which can be useful in some situations, such as when the entry's working tree is stored on a portable device), add a file named diff --git a/Documentation/git.txt b/Documentation/git.txt index bff6302c0d..d987ad20c9 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of Git, that is available from the 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v2.7.0/git.html[documentation for release 2.7] +* link:v2.7.1/git.html[documentation for release 2.7.1] * release notes for + link:RelNotes/2.7.1.txt[2.7.1], link:RelNotes/2.7.0.txt[2.7]. * link:v2.6.5/git.html[documentation for release 2.6.5] diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 79a1948a0b..473623d631 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -82,12 +82,12 @@ PATTERN FORMAT - An optional prefix "`!`" which negates the pattern; any matching file excluded by a previous pattern will become - included again. + included again. It is not possible to re-include a file if a parent + directory of that file is excluded. Git doesn't list excluded + directories for performance reasons, so any patterns on contained + files have no effect, no matter where they are defined. Put a backslash ("`\`") in front of the first "`!`" for patterns that begin with a literal "`!`", for example, "`\!important!.txt`". - It is possible to re-include a file if a parent directory of that - file is excluded if certain conditions are met. See section NOTES - for detail. - If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find @@ -141,21 +141,6 @@ not tracked by Git remain untracked. To stop tracking a file that is currently tracked, use 'git rm --cached'. -To re-include files or directories when their parent directory is -excluded, the following conditions must be met: - - - The rules to exclude a directory and re-include a subset back must - be in the same .gitignore file. - - - The directory part in the re-include rules must be literal (i.e. no - wildcards) - - - The rules to exclude the parent directory must not end with a - trailing slash. - - - The rules to exclude the parent directory must have at least one - slash. - EXAMPLES -------- diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index e225974253..cafc284359 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -531,6 +531,11 @@ The most notable example is `HEAD`. "Secure Hash Algorithm 1"; a cryptographic hash function. In the context of Git used as a synonym for <<def_object_name,object name>>. +[[def_shallow_clone]]shallow clone:: + Mostly a synonym to <<def_shallow_repository,shallow repository>> + but the phrase makes it more explicit that it was created by + running `git clone --depth=...` command. + [[def_shallow_repository]]shallow repository:: A shallow <<def_repository,repository>> has an incomplete history some of whose <<def_commit,commits>> have <<def_parent,parents>> cauterized away (in other diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 1c790ac74a..ec6bacfcdf 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -2125,8 +2125,37 @@ Allowing web browsing of a repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The gitweb cgi script provides users an easy way to browse your -project's files and history without having to install Git; see the file -gitweb/INSTALL in the Git source tree for instructions on setting it up. +project's revisions, file contents and logs without having to install +Git. Features like RSS/Atom feeds and blame/annotation details may +optionally be enabled. + +The linkgit:git-instaweb[1] command provides a simple way to start +browsing the repository using gitweb. The default server when using +instaweb is lighttpd. + +See the file gitweb/INSTALL in the Git source tree and +linkgit:gitweb[1] for instructions on details setting up a permament +installation with a CGI or Perl capable server. + +[[how-to-get-a-git-repository-with-minimal-history]] +How to get a Git repository with minimal history +------------------------------------------------ + +A <<def_shallow_clone,shallow clone>>, with its truncated +history, is useful when one is interested only in recent history +of a project and getting full history from the upstream is +expensive. + +A <<def_shallow_clone,shallow clone>> is created by specifying +the linkgit:git-clone[1] `--depth` switch. The depth can later be +changed with the linkgit:git-fetch[1] `--depth` switch, or full +history restored with `--unshallow`. + +Merging inside a <<def_shallow_clone,shallow clone>> will work as long +as a merge base is in the recent history. +Otherwise, it will be like merging unrelated histories and may +have to result in huge conflicts. This limitation may make such +a repository unsuitable to be used in merge based workflows. [[sharing-development-examples]] Examples @@ -4636,23 +4665,15 @@ Scan email archives for other stuff left out Scan man pages to see if any assume more background than this manual provides. -Simplify beginning by suggesting disconnected head instead of -temporary branch creation? - Add more good examples. Entire sections of just cookbook examples might be a good idea; maybe make an "advanced examples" section a standard end-of-chapter section? Include cross-references to the glossary, where appropriate. -Document shallow clones? See draft 1.5.0 release notes for some -documentation. - Add a section on working with other version control systems, including CVS, Subversion, and just imports of series of release tarballs. -More details on gitweb? - Write a chapter on using plumbing and writing scripts. Alternates, clone -reference, etc. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 51aa9e6806..330b339d1c 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.7.0 +DEF_VER=v2.7.1 LF=' ' @@ -1 +1 @@ -Documentation/RelNotes/2.7.0.txt
\ No newline at end of file +Documentation/RelNotes/2.7.2.txt
\ No newline at end of file diff --git a/builtin/am.c b/builtin/am.c index 9fb42fdd71..95decc6a59 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1657,7 +1657,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa init_revisions(&rev_info, NULL); rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; - diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1); + diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); add_pending_sha1(&rev_info, "HEAD", our_tree, 0); diff_setup_done(&rev_info.diffopt); run_diff_index(&rev_info, 1); @@ -1939,6 +1939,7 @@ next: */ if (!state->rebasing) { am_destroy(state); + close_all_packs(); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } diff --git a/builtin/clean.c b/builtin/clean.c index d7acb94a95..919157bc2f 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -147,30 +147,6 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset) return 0; } -/* - * Return 1 if the given path is the root of a git repository or - * submodule else 0. Will not return 1 for bare repositories with the - * exception of creating a bare repository in "foo/.git" and calling - * is_git_repository("foo"). - */ -static int is_git_repository(struct strbuf *path) -{ - int ret = 0; - int gitfile_error; - size_t orig_path_len = path->len; - assert(orig_path_len != 0); - strbuf_complete(path, '/'); - strbuf_addstr(path, ".git"); - if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf)) - ret = 1; - if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED || - gitfile_error == READ_GITFILE_ERR_READ_FAILED) - ret = 1; /* This could be a real .git file, take the - * safe option and avoid cleaning */ - strbuf_setlen(path, orig_path_len); - return ret; -} - static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, int dry_run, int quiet, int *dir_gone) { @@ -182,7 +158,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, *dir_gone = 1; - if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_git_repository(path)) { + if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) { if (!quiet) { quote_path_relative(path->buf, prefix, "ed); printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir), diff --git a/builtin/commit.c b/builtin/commit.c index d054f84960..89bf6ad38a 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -761,7 +761,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, hook_arg2 = ""; } - s->fp = fopen(git_path(commit_editmsg), "w"); + s->fp = fopen_for_writing(git_path(commit_editmsg)); if (s->fp == NULL) die_errno(_("could not open '%s'"), git_path(commit_editmsg)); diff --git a/builtin/diff.c b/builtin/diff.c index ed0acca91f..52c98a9217 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -341,7 +341,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } if (no_index) /* If this is a no-index diff, just run it and exit there. */ - diff_no_index(&rev, argc, argv, prefix); + diff_no_index(&rev, argc, argv); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index d9ac5d8410..2471297f71 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -880,7 +880,7 @@ static void export_marks(char *file) FILE *f; int e = 0; - f = fopen(file, "w"); + f = fopen_for_writing(file); if (!f) die_errno("Unable to open marks file %s for writing.", file); diff --git a/builtin/fetch.c b/builtin/fetch.c index c85f3471d4..17f40e10f6 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -837,7 +837,7 @@ static void check_not_current_branch(struct ref *ref_map) static int truncate_fetch_head(void) { const char *filename = git_path_fetch_head(); - FILE *fp = fopen(filename, "w"); + FILE *fp = fopen_for_writing(filename); if (!fp) return error(_("cannot open %s: %s\n"), filename, strerror(errno)); @@ -1221,6 +1221,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) list.strdup_strings = 1; string_list_clear(&list, 0); + close_all_packs(); + argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL); if (verbosity < 0) argv_array_push(&argv_gc_auto, "--quiet"); diff --git a/builtin/merge.c b/builtin/merge.c index 15bf95b3ac..b98a3489bf 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -404,6 +404,7 @@ static void finish(struct commit *head_commit, * We ignore errors in 'gc --auto', since the * user should see them. */ + close_all_packs(); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index ca38131873..f2d6761af6 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1618,7 +1618,7 @@ static void prepare_shallow_update(struct command *commands, continue; si->need_reachability_test[i]++; for (k = 0; k < 32; k++) - if (si->used_shallow[i][j] & (1 << k)) + if (si->used_shallow[i][j] & (1U << k)) si->shallow_ref[j * 32 + k]++; } @@ -1796,6 +1796,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) "gc", "--auto", "--quiet", NULL, }; int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR; + close_all_packs(); run_command_v_opt(argv_gc_auto, opt); } if (auto_update_server_info) diff --git a/builtin/stripspace.c b/builtin/stripspace.c index 7ff8434f7c..15e716ef43 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -35,7 +35,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix) N_("skip and remove all lines starting with comment character"), STRIP_COMMENTS), OPT_CMDMODE('c', "comment-lines", &mode, - N_("prepend comment character and blank to each line"), + N_("prepend comment character and space to each line"), COMMENT_LINES), OPT_END() }; diff --git a/builtin/tag.c b/builtin/tag.c index 8db8c87e57..1705c94665 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -44,11 +44,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con if (!format) { if (filter->lines) { to_free = xstrfmt("%s %%(contents:lines=%d)", - "%(align:15)%(refname:short)%(end)", + "%(align:15)%(refname:strip=2)%(end)", filter->lines); format = to_free; } else - format = "%(refname:short)"; + format = "%(refname:strip=2)"; } verify_ref_format(format); @@ -214,7 +214,7 @@ struct cache_entry { #define CE_INTENT_TO_ADD (1 << 29) #define CE_SKIP_WORKTREE (1 << 30) /* CE_EXTENDED2 is for future extension */ -#define CE_EXTENDED2 (1 << 31) +#define CE_EXTENDED2 (1U << 31) #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE) @@ -456,7 +456,6 @@ extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); extern const char *get_git_dir(void); extern const char *get_git_common_dir(void); -extern int is_git_directory(const char *path); extern char *get_object_directory(void); extern char *get_index_file(void); extern char *get_graft_file(void); @@ -467,6 +466,25 @@ extern const char *get_git_namespace(void); extern const char *strip_namespace(const char *namespaced_ref); extern const char *get_git_work_tree(void); +/* + * Return true if the given path is a git directory; note that this _just_ + * looks at the directory itself. If you want to know whether "foo/.git" + * is a repository, you must feed that path, not just "foo". + */ +extern int is_git_directory(const char *path); + +/* + * Return 1 if the given path is the root of a git repository or + * submodule, else 0. Will not return 1 for bare repositories with the + * exception of creating a bare repository in "foo/.git" and calling + * is_git_repository("foo"). + * + * If we run into read errors, we err on the side of saying "yes, it is", + * as we usually consider sub-repos precious, and would prefer to err on the + * side of not disrupting or deleting them. + */ +extern int is_nonbare_repository_dir(struct strbuf *path); + #define READ_GITFILE_ERR_STAT_FAILED 1 #define READ_GITFILE_ERR_NOT_A_FILE 2 #define READ_GITFILE_ERR_OPEN_FAILED 3 diff --git a/compat/basename.c b/compat/basename.c index d8f8a3c6dc..96bd9533b4 100644 --- a/compat/basename.c +++ b/compat/basename.c @@ -1,15 +1,71 @@ #include "../git-compat-util.h" +#include "../strbuf.h" /* Adapted from libiberty's basename.c. */ char *gitbasename (char *path) { const char *base; - /* Skip over the disk name in MSDOS pathnames. */ - if (has_dos_drive_prefix(path)) - path += 2; + + if (path) + skip_dos_drive_prefix(&path); + + if (!path || !*path) + return "."; + for (base = path; *path; path++) { - if (is_dir_sep(*path)) - base = path + 1; + if (!is_dir_sep(*path)) + continue; + do { + path++; + } while (is_dir_sep(*path)); + if (*path) + base = path; + else + while (--path != base && is_dir_sep(*path)) + *path = '\0'; } return (char *)base; } + +char *gitdirname(char *path) +{ + static struct strbuf buf = STRBUF_INIT; + char *p = path, *slash = NULL, c; + int dos_drive_prefix; + + if (!p) + return "."; + + if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p) + goto dot; + + /* + * POSIX.1-2001 says dirname("/") should return "/", and dirname("//") + * should return "//", but dirname("///") should return "/" again. + */ + if (is_dir_sep(*p)) { + if (!p[1] || (is_dir_sep(p[1]) && !p[2])) + return path; + slash = ++p; + } + while ((c = *(p++))) + if (is_dir_sep(c)) { + char *tentative = p - 1; + + /* POSIX.1-2001 says to ignore trailing slashes */ + while (is_dir_sep(*p)) + p++; + if (*p) + slash = tentative; + } + + if (slash) { + *slash = '\0'; + return path; + } + +dot: + strbuf_reset(&buf); + strbuf_addf(&buf, "%.*s.", dos_drive_prefix, path); + return buf.buf; +} diff --git a/compat/bswap.h b/compat/bswap.h index 7fed637ed0..d47c003544 100644 --- a/compat/bswap.h +++ b/compat/bswap.h @@ -149,11 +149,12 @@ static inline uint64_t git_bswap64(uint64_t x) * and is faster on architectures with memory alignment issues. */ -#if defined(__i386__) || defined(__x86_64__) || \ +#if !defined(NO_UNALIGNED_LOADS) && ( \ + defined(__i386__) || defined(__x86_64__) || \ defined(_M_IX86) || defined(_M_X64) || \ defined(__ppc__) || defined(__ppc64__) || \ defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__s390__) || defined(__s390x__) + defined(__s390__) || defined(__s390x__)) #define get_be16(p) ntohs(*(unsigned short *)(p)) #define get_be32(p) ntohl(*(unsigned int *)(p)) diff --git a/compat/mingw.c b/compat/mingw.c index 5edea29508..9b2a1f552e 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1932,28 +1932,31 @@ pid_t waitpid(pid_t pid, int *status, int options) return -1; } +int mingw_skip_dos_drive_prefix(char **path) +{ + int ret = has_dos_drive_prefix(*path); + *path += ret; + return ret; +} + int mingw_offset_1st_component(const char *path) { - int offset = 0; - if (has_dos_drive_prefix(path)) - offset = 2; + char *pos = (char *)path; /* unc paths */ - else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) { - + if (!skip_dos_drive_prefix(&pos) && + is_dir_sep(pos[0]) && is_dir_sep(pos[1])) { /* skip server name */ - char *pos = strpbrk(path + 2, "\\/"); + pos = strpbrk(pos + 2, "\\/"); if (!pos) return 0; /* Error: malformed unc path */ do { pos++; } while (*pos && !is_dir_sep(*pos)); - - offset = pos - path; } - return offset + is_dir_sep(path[offset]); + return pos + is_dir_sep(*pos) - path; } int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen) diff --git a/compat/mingw.h b/compat/mingw.h index 57ca477d1f..a5fb52f977 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -361,7 +361,10 @@ HANDLE winansi_get_osfhandle(int fd); * git specific compatibility */ -#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') +#define has_dos_drive_prefix(path) \ + (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0) +int mingw_skip_dos_drive_prefix(char **path); +#define skip_dos_drive_prefix mingw_skip_dos_drive_prefix #define is_dir_sep(c) ((c) == '/' || (c) == '\\') static inline char *mingw_find_last_dir_sep(const char *path) { diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 02a0489c00..00d729996f 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1169,7 +1169,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary --no-prefix --src-prefix= --dst-prefix= --inter-hunk-context= --patience --histogram --minimal - --raw --word-diff + --raw --word-diff --word-diff-regex= --dirstat --dirstat= --dirstat-by-file --dirstat-by-file= --cumulative --diff-algorithm= @@ -1687,8 +1687,12 @@ _git_rebase () --preserve-merges --stat --no-stat --committer-date-is-author-date --ignore-date --ignore-whitespace --whitespace= - --autosquash --fork-point --no-fork-point - --autostash + --autosquash --no-autosquash + --fork-point --no-fork-point + --autostash --no-autostash + --verify --no-verify + --keep-empty --root --force-rebase --no-ff + --exec " return @@ -2368,7 +2372,7 @@ _git_show_branch () case "$cur" in --*) __gitcomp " - --all --remotes --topo-order --current --more= + --all --remotes --topo-order --date-order --current --more= --list --independent --merge-base --no-name --color --no-color --sha1-name --sparse --topics --reflog diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index edf36f8c36..5c8372709b 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -479,8 +479,16 @@ copy_or_skip() p="$p -p $parent" fi done - - if [ -n "$identical" ]; then + + copycommit= + if [ -n "$identical" ] && [ -n "$nonidentical" ]; then + extras=$(git rev-list --count $identical..$nonidentical) + if [ "$extras" -ne 0 ]; then + # we need to preserve history along the other branch + copycommit=1 + fi + fi + if [ -n "$identical" ] && [ -z "$copycommit" ]; then echo $identical else copy_commit $rev $tree "$p" || exit $? diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 751aee3a0c..3bf96a9bb6 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -1014,4 +1014,64 @@ test_expect_success 'push split to subproj' ' ) ' +# +# This test covers 2 cases in subtree split copy_or_skip code +# 1) Merges where one parent is a superset of the changes of the other +# parent regarding changes to the subtree, in this case the merge +# commit should be copied +# 2) Merges where only one parent operate on the subtree, and the merge +# commit should be skipped +# +# (1) is checked by ensuring subtree_tip is a descendent of subtree_branch +# (2) should have a check added (not_a_subtree_change shouldn't be present +# on the produced subtree) +# +# Other related cases which are not tested (or currently handled correctly) +# - Case (1) where there are more than 2 parents, it will sometimes correctly copy +# the merge, and sometimes not +# - Merge commit where both parents have same tree as the merge, currently +# will always be skipped, even if they reached that state via different +# set of commits. +# + +next_test +test_expect_success 'subtree descendant check' ' + subtree_test_create_repo "$subtree_test_count" && + test_create_commit "$subtree_test_count" folder_subtree/a && + ( + cd "$subtree_test_count" && + git branch branch + ) && + test_create_commit "$subtree_test_count" folder_subtree/0 && + test_create_commit "$subtree_test_count" folder_subtree/b && + cherry=$(cd "$subtree_test_count"; git rev-parse HEAD) && + ( + cd "$subtree_test_count" && + git checkout branch + ) && + test_create_commit "$subtree_test_count" commit_on_branch && + ( + cd "$subtree_test_count" && + git cherry-pick $cherry && + git checkout master && + git merge -m "merge should be kept on subtree" branch && + git branch no_subtree_work_branch + ) && + test_create_commit "$subtree_test_count" folder_subtree/d && + ( + cd "$subtree_test_count" && + git checkout no_subtree_work_branch + ) && + test_create_commit "$subtree_test_count" not_a_subtree_change && + ( + cd "$subtree_test_count" && + git checkout master && + git merge -m "merge should be skipped on subtree" no_subtree_work_branch && + + git subtree split --prefix folder_subtree/ --branch subtree_tip master && + git subtree split --prefix folder_subtree/ --branch subtree_branch branch && + check_equal $(git rev-list --count subtree_tip..subtree_branch) 0 + ) +' + test_done diff --git a/diff-no-index.c b/diff-no-index.c index 8e0fd270b5..03daadb25a 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -237,12 +237,12 @@ static void fixup_paths(const char **path, struct strbuf *replacement) } void diff_no_index(struct rev_info *revs, - int argc, const char **argv, - const char *prefix) + int argc, const char **argv) { int i, prefixlen; const char *paths[2]; struct strbuf replacement = STRBUF_INIT; + const char *prefix = revs->prefix; diff_setup(&revs->diffopt); for (i = 1; i < argc - 2; ) { @@ -252,7 +252,8 @@ void diff_no_index(struct rev_info *revs, else if (!strcmp(argv[i], "--")) i++; else { - j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); + j = diff_opt_parse(&revs->diffopt, argv + i, argc - i, + revs->prefix); if (j <= 0) die("invalid diff option/value: %s", argv[i]); i += j; @@ -3693,12 +3693,16 @@ static int parse_ws_error_highlight(struct diff_options *opt, const char *arg) return 1; } -int diff_opt_parse(struct diff_options *options, const char **av, int ac) +int diff_opt_parse(struct diff_options *options, + const char **av, int ac, const char *prefix) { const char *arg = av[0]; const char *optarg; int argcount; + if (!prefix) + prefix = ""; + /* Output format options */ if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch") || opt_arg(arg, 'U', "unified", &options->context)) @@ -3915,7 +3919,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--pickaxe-regex")) options->pickaxe_opts |= DIFF_PICKAXE_REGEX; else if ((argcount = short_opt('O', av, &optarg))) { - options->orderfile = optarg; + const char *path = prefix_filename(prefix, strlen(prefix), optarg); + options->orderfile = xstrdup(path); return argcount; } else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) { @@ -3954,9 +3959,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--no-function-context")) DIFF_OPT_CLR(options, FUNCCONTEXT); else if ((argcount = parse_long_opt("output", av, &optarg))) { - options->file = fopen(optarg, "w"); + const char *path = prefix_filename(prefix, strlen(prefix), optarg); + options->file = fopen(path, "w"); if (!options->file) - die_errno("Could not open '%s'", optarg); + die_errno("Could not open '%s'", path); options->close_file = 1; return argcount; } else @@ -91,7 +91,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data) #define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28) #define DIFF_OPT_FUNCCONTEXT (1 << 29) #define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30) -#define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1 << 31) +#define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1U << 31) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_TOUCHED(opts, flag) ((opts)->touched_flags & DIFF_OPT_##flag) @@ -268,7 +268,7 @@ extern int parse_long_opt(const char *opt, const char **argv, extern int git_diff_basic_config(const char *var, const char *value, void *cb); extern int git_diff_ui_config(const char *var, const char *value, void *cb); extern void diff_setup(struct diff_options *); -extern int diff_opt_parse(struct diff_options *, const char **, int); +extern int diff_opt_parse(struct diff_options *, const char **, int, const char *); extern void diff_setup_done(struct diff_options *); #define DIFF_DETECT_RENAME 1 @@ -345,7 +345,7 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *); extern int diff_result_code(struct diff_options *, int); -extern void diff_no_index(struct rev_info *, int, const char **, const char *); +extern void diff_no_index(struct rev_info *, int, const char **); extern int index_differs_from(const char *def, int diff_flags); @@ -564,9 +564,7 @@ void clear_exclude_list(struct exclude_list *el) free(el->excludes); free(el->filebuf); - el->nr = 0; - el->excludes = NULL; - el->filebuf = NULL; + memset(el, 0, sizeof(*el)); } static void trim_trailing_spaces(char *buf) @@ -882,25 +880,6 @@ int match_pathname(const char *pathname, int pathlen, */ if (!patternlen && !namelen) return 1; - /* - * This can happen when we ignore some exclude rules - * on directories in other to see if negative rules - * may match. E.g. - * - * /abc - * !/abc/def/ghi - * - * The pattern of interest is "/abc". On the first - * try, we should match path "abc" with this pattern - * in the "if" statement right above, but the caller - * ignores it. - * - * On the second try with paths within "abc", - * e.g. "abc/xyz", we come here and try to match it - * with "/abc". - */ - if (!patternlen && namelen && *name == '/') - return 1; } return fnmatch_icase_mem(pattern, patternlen, @@ -909,48 +888,6 @@ int match_pathname(const char *pathname, int pathlen, } /* - * Return non-zero if pathname is a directory and an ancestor of the - * literal path in a (negative) pattern. This is used to keep - * descending in "foo" and "foo/bar" when the pattern is - * "!foo/bar/.gitignore". "foo/notbar" will not be descended however. - */ -static int match_neg_path(const char *pathname, int pathlen, int *dtype, - const char *base, int baselen, - const char *pattern, int prefix, int patternlen, - int flags) -{ - assert((flags & EXC_FLAG_NEGATIVE) && !(flags & EXC_FLAG_NODIR)); - - if (*dtype == DT_UNKNOWN) - *dtype = get_dtype(NULL, pathname, pathlen); - if (*dtype != DT_DIR) - return 0; - - if (*pattern == '/') { - pattern++; - patternlen--; - prefix--; - } - - if (baselen) { - if (((pathlen < baselen && base[pathlen] == '/') || - pathlen == baselen) && - !strncmp_icase(pathname, base, pathlen)) - return 1; - pathname += baselen + 1; - pathlen -= baselen + 1; - } - - - if (prefix && - ((pathlen < prefix && pattern[pathlen] == '/') && - !strncmp_icase(pathname, pattern, pathlen))) - return 1; - - return 0; -} - -/* * Scan the given exclude list in reverse to see whether pathname * should be ignored. The first match (i.e. the last on the list), if * any, determines the fate. Returns the exclude_list element which @@ -963,7 +900,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, struct exclude_list *el) { struct exclude *exc = NULL; /* undecided */ - int i, matched_negative_path = 0; + int i; if (!el->nr) return NULL; /* undefined */ @@ -998,18 +935,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, exc = x; break; } - - if ((x->flags & EXC_FLAG_NEGATIVE) && !matched_negative_path && - match_neg_path(pathname, pathlen, dtype, x->base, - x->baselen ? x->baselen - 1 : 0, - exclude, prefix, x->patternlen, x->flags)) - matched_negative_path = 1; - } - if (exc && - !(exc->flags & EXC_FLAG_NEGATIVE) && - !(exc->flags & EXC_FLAG_NODIR) && - matched_negative_path) - exc = NULL; + } return exc; } diff --git a/environment.c b/environment.c index 2da7fe2e06..1cc4aab4ea 100644 --- a/environment.c +++ b/environment.c @@ -235,8 +235,6 @@ void set_git_work_tree(const char *new_work_tree) } git_work_tree_initialized = 1; work_tree = xstrdup(real_path(new_work_tree)); - if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1)) - die("could not set GIT_WORK_TREE to '%s'", work_tree); } const char *get_git_work_tree(void) diff --git a/git-compat-util.h b/git-compat-util.h index 2da0a75a38..693a336ff5 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -253,6 +253,8 @@ struct itimerval { #else #define basename gitbasename extern char *gitbasename(char *); +#define dirname gitdirname +extern char *gitdirname(char *); #endif #ifndef NO_ICONV @@ -335,6 +337,14 @@ static inline int git_has_dos_drive_prefix(const char *path) #define has_dos_drive_prefix git_has_dos_drive_prefix #endif +#ifndef skip_dos_drive_prefix +static inline int git_skip_dos_drive_prefix(char **path) +{ + return 0; +} +#define skip_dos_drive_prefix git_skip_dos_drive_prefix +#endif + #ifndef is_dir_sep static inline int git_is_dir_sep(int c) { @@ -733,6 +743,7 @@ extern int xmkstemp_mode(char *template, int mode); extern int odb_mkstemp(char *template, size_t limit, const char *pattern); extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1); extern char *xgetcwd(void); +extern FILE *fopen_for_writing(const char *path); #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x))) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 95e69b19a7..02c0445be1 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2664,7 +2664,7 @@ sub argsfromdir # co # Obtain list directly. # remove # HERE: TEST: MAYBE client does the recursion for us, # # since it only makes sense to remove stuff already in - # # the sandobx? + # # the sandbox? # ci # HERE: Similar to remove... # # Don't try to implement the confusing/weird # # ci -r bug er.."feature". diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 98f1779cf3..86b2ff1e07 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -404,7 +404,7 @@ while read commit parents; do then tree=$(git write-tree) else - tree="$commit^{tree}" + tree=$(git rev-parse "$commit^{tree}") fi workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \ "$tree" $parentstr < ../message > ../map/$commit || diff --git a/git-rebase.sh b/git-rebase.sh index af7ba5fd90..cf60c43908 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -176,7 +176,7 @@ You can run "git stash pop" or "git stash drop" at any time. finish_rebase () { apply_autostash && - git gc --auto && + { git gc --auto || true; } && rm -rf "$state_dir" } diff --git a/git-send-email.perl b/git-send-email.perl index 6caa5b563f..d356901348 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -524,8 +524,13 @@ my %parse_alias = ( if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { my ($alias, $addr) = ($1, $2); $addr =~ s/#.*$//; # mutt allows # comments - # commas delimit multiple addresses - $aliases{$alias} = [ split_addrs($addr) ]; + # commas delimit multiple addresses + my @addr = split_addrs($addr); + + # quotes may be escaped in the file, + # unescape them so we do not double-escape them later. + s/\\"/"/g foreach @addr; + $aliases{$alias} = \@addr }}}, mailrc => sub { my $fh = shift; while (<$fh>) { if (/^alias\s+(\S+)\s+(.*)$/) { diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 7a5b23acf2..05d7910b7c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -7576,7 +7576,7 @@ sub git_object { git_cmd(), 'cat-file', '-t', $object_id) . ' 2> /dev/null' or die_error(404, "Object does not exist"); $type = <$fd>; - chomp $type; + defined $type && chomp $type; close $fd or die_error(404, "Object does not exist"); diff --git a/mergetools/vimdiff b/mergetools/vimdiff index 1ddfbfcd78..74ea6d5479 100644 --- a/mergetools/vimdiff +++ b/mergetools/vimdiff @@ -9,8 +9,8 @@ merge_cmd () { gvimdiff|vimdiff) if $base_present then - "$merge_tool_path" -f -d -c 'wincmd J' \ - "$MERGED" "$LOCAL" "$BASE" "$REMOTE" + "$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \ + "$LOCAL" "$BASE" "$REMOTE" "$MERGED" else "$merge_tool_path" -f -d -c 'wincmd l' \ "$LOCAL" "$MERGED" "$REMOTE" @@ -782,13 +782,10 @@ const char *relative_path(const char *in, const char *prefix, else if (!prefix_len) return in; - if (have_same_root(in, prefix)) { + if (have_same_root(in, prefix)) /* bypass dos_drive, for "c:" is identical to "C:" */ - if (has_dos_drive_prefix(in)) { - i = 2; - j = 2; - } - } else { + i = j = has_dos_drive_prefix(in); + else { return in; } @@ -943,11 +940,10 @@ const char *remove_leading_path(const char *in, const char *prefix) int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; + int i; - if (has_dos_drive_prefix(src)) { + for (i = has_dos_drive_prefix(src); i > 0; i--) *dst++ = *src++; - *dst++ = *src++; - } dst0 = dst; if (is_dir_sep(*src)) { diff --git a/perl/Git/SVN/Ra.pm b/perl/Git/SVN/Ra.pm index 4a499fcb38..e764696801 100644 --- a/perl/Git/SVN/Ra.pm +++ b/perl/Git/SVN/Ra.pm @@ -81,7 +81,6 @@ sub prepare_config_once { SVN::_Core::svn_config_ensure($config_dir, undef); my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers); my $config = SVN::Core::config_get_config($config_dir); - my $dont_store_passwords = 1; my $conf_t = $config->{'config'}; no warnings 'once'; @@ -93,9 +92,14 @@ sub prepare_config_once { $SVN::_Core::SVN_CONFIG_SECTION_AUTH, $SVN::_Core::SVN_CONFIG_OPTION_STORE_PASSWORDS, 1) == 0) { + my $val = '1'; + if (::compare_svn_version('1.9.0') < 0) { # pre-SVN r1553823 + my $dont_store_passwords = 1; + $val = bless \$dont_store_passwords, "_p_void"; + } SVN::_Core::svn_auth_set_parameter($baton, $SVN::_Core::SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, - bless (\$dont_store_passwords, "_p_void")); + $val); } if (SVN::_Core::svn_config_get_bool($conf_t, $SVN::_Core::SVN_CONFIG_SECTION_AUTH, diff --git a/ref-filter.c b/ref-filter.c index 7bef7f8dac..f097176ed9 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -763,6 +763,29 @@ static inline char *copy_advance(char *dst, const char *src) return dst; } +static const char *strip_ref_components(const char *refname, const char *nr_arg) +{ + char *end; + long nr = strtol(nr_arg, &end, 10); + long remaining = nr; + const char *start = refname; + + if (nr < 1 || *end != '\0') + die(":strip= requires a positive integer argument"); + + while (remaining) { + switch (*start++) { + case '\0': + die("ref '%s' does not have %ld components to :strip", + refname, nr); + case '/': + remaining--; + break; + } + } + return start; +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -909,11 +932,14 @@ static void populate_value(struct ref_array_item *ref) formatp = strchr(name, ':'); if (formatp) { int num_ours, num_theirs; + const char *arg; formatp++; if (!strcmp(formatp, "short")) refname = shorten_unambiguous_ref(refname, warn_ambiguous_refs); + else if (skip_prefix(formatp, "strip=", &arg)) + refname = strip_ref_components(refname, arg); else if (!strcmp(formatp, "track") && (starts_with(name, "upstream") || starts_with(name, "push"))) { diff --git a/reflog-walk.c b/reflog-walk.c index 85b8a54241..0ebd1da5ce 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -221,6 +221,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) struct commit_info *commit_info = get_commit_info(commit, &info->reflogs, 0); struct commit_reflog *commit_reflog; + struct object *logobj; struct reflog_info *reflog; info->last_commit_reflog = NULL; @@ -232,15 +233,20 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) commit->parents = NULL; return; } - - reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; info->last_commit_reflog = commit_reflog; - commit_reflog->recno--; - commit_info->commit = (struct commit *)parse_object(reflog->osha1); - if (!commit_info->commit) { + + do { + reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; + commit_reflog->recno--; + logobj = parse_object(reflog->osha1); + } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT)); + + if (!logobj || logobj->type != OBJ_COMMIT) { + commit_info->commit = NULL; commit->parents = NULL; return; } + commit_info->commit = (struct commit *)logobj; commit->parents = xcalloc(1, sizeof(struct commit_list)); commit->parents->item = commit_info->commit; diff --git a/refs/files-backend.c b/refs/files-backend.c index c648b5e853..3a27f27585 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -933,6 +933,10 @@ static void clear_loose_ref_cache(struct ref_cache *refs) } } +/* + * Create a new submodule ref cache and add it to the internal + * set of caches. + */ static struct ref_cache *create_ref_cache(const char *submodule) { int len; @@ -942,16 +946,12 @@ static struct ref_cache *create_ref_cache(const char *submodule) len = strlen(submodule) + 1; refs = xcalloc(1, sizeof(struct ref_cache) + len); memcpy(refs->name, submodule, len); + refs->next = submodule_ref_caches; + submodule_ref_caches = refs; return refs; } -/* - * Return a pointer to a ref_cache for the specified submodule. For - * the main repository, use submodule==NULL. The returned structure - * will be allocated and initialized but not necessarily populated; it - * should not be freed. - */ -static struct ref_cache *get_ref_cache(const char *submodule) +static struct ref_cache *lookup_ref_cache(const char *submodule) { struct ref_cache *refs; @@ -961,10 +961,20 @@ static struct ref_cache *get_ref_cache(const char *submodule) for (refs = submodule_ref_caches; refs; refs = refs->next) if (!strcmp(submodule, refs->name)) return refs; + return NULL; +} - refs = create_ref_cache(submodule); - refs->next = submodule_ref_caches; - submodule_ref_caches = refs; +/* + * Return a pointer to a ref_cache for the specified submodule. For + * the main repository, use submodule==NULL. The returned structure + * will be allocated and initialized but not necessarily populated; it + * should not be freed. + */ +static struct ref_cache *get_ref_cache(const char *submodule) +{ + struct ref_cache *refs = lookup_ref_cache(submodule); + if (!refs) + refs = create_ref_cache(submodule); return refs; } @@ -1336,16 +1346,24 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs, int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1) { int len = strlen(path), retval; - char *submodule; + struct strbuf submodule = STRBUF_INIT; struct ref_cache *refs; while (len && path[len-1] == '/') len--; if (!len) return -1; - submodule = xstrndup(path, len); - refs = get_ref_cache(submodule); - free(submodule); + + strbuf_add(&submodule, path, len); + refs = lookup_ref_cache(submodule.buf); + if (!refs) { + if (!is_nonbare_repository_dir(&submodule)) { + strbuf_release(&submodule); + return -1; + } + refs = create_ref_cache(submodule.buf); + } + strbuf_release(&submodule); retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0); return retval; @@ -1545,11 +1545,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, } /* - * Bypass the usual "must fast-forward" check but - * replace it with a weaker "the old value must be - * this value we observed". If the remote ref has - * moved and is now different from what we expect, - * reject any push. + * If the remote ref has moved and is now different + * from what we expect, reject any push. * * It also is an error if the user told us to check * with the remote-tracking branch to find the value @@ -1560,10 +1557,14 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, if (ref->expect_old_no_trackback || oidcmp(&ref->old_oid, &ref->old_oid_expect)) reject_reason = REF_STATUS_REJECT_STALE; + else + /* If the ref isn't stale then force the update. */ + force_ref_update = 1; } /* - * The usual "must fast-forward" rules. + * If the update isn't already rejected then check + * the usual "must fast-forward" rules. * * Decide whether an individual refspec A:B can be * pushed. The push will succeed if any of the @@ -1582,7 +1583,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, * passing the --force argument */ - else if (!ref->deletion && !is_null_oid(&ref->old_oid)) { + if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) { if (starts_with(ref->name, "refs/tags/")) reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_object_file(&ref->old_oid)) diff --git a/revision.c b/revision.c index 0a282f533b..14daefb174 100644 --- a/revision.c +++ b/revision.c @@ -2049,7 +2049,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--ignore-missing")) { revs->ignore_missing = 1; } else { - int opts = diff_opt_parse(&revs->diffopt, argv, argc); + int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix); if (!opts) unkv[(*unkc)++] = arg; return opts; @@ -312,6 +312,23 @@ done: return ret; } +int is_nonbare_repository_dir(struct strbuf *path) +{ + int ret = 0; + int gitfile_error; + size_t orig_path_len = path->len; + assert(orig_path_len != 0); + strbuf_complete(path, '/'); + strbuf_addstr(path, ".git"); + if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf)) + ret = 1; + if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED || + gitfile_error == READ_GITFILE_ERR_READ_FAILED) + ret = 1; + strbuf_setlen(path, orig_path_len); + return ret; +} + int is_inside_git_dir(void) { if (inside_git_dir < 0) @@ -434,17 +451,6 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok) return ret; } -static void update_linked_gitdir(const char *gitfile, const char *gitdir) -{ - struct strbuf path = STRBUF_INIT; - struct stat st; - - strbuf_addf(&path, "%s/gitdir", gitdir); - if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL)) - write_file(path.buf, "%s", gitfile); - strbuf_release(&path); -} - /* * Try to read the location of the git directory from the .git file, * return path to git directory if found. @@ -514,7 +520,6 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) error_code = READ_GITFILE_ERR_NOT_A_REPO; goto cleanup_return; } - update_linked_gitdir(path, dir); path = real_path(dir); cleanup_return: diff --git a/t/perf/p7300-clean.sh b/t/perf/p7300-clean.sh index ec94cdd657..7c1888a27e 100755 --- a/t/perf/p7300-clean.sh +++ b/t/perf/p7300-clean.sh @@ -28,4 +28,8 @@ test_perf 'clean many untracked sub dirs, ignore nested git' ' git clean -n -q -f -f -d 100000_sub_dirs/ ' +test_perf 'ls-files -o' ' + git ls-files -o +' + test_done diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh index 9670e8cbe6..3afe0125c9 100755 --- a/t/t0002-gitfile.sh +++ b/t/t0002-gitfile.sh @@ -99,7 +99,7 @@ test_expect_success 'check rev-list' ' test "$SHA" = "$(git rev-list HEAD)" ' -test_expect_success 'setup_git_dir twice in subdir' ' +test_expect_failure 'setup_git_dir twice in subdir' ' git init sgd && ( cd sgd && diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 627ef854d5..f0152a7ab4 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -59,6 +59,9 @@ case $(uname -s) in ;; esac +test_expect_success basename 'test-path-utils basename' +test_expect_success dirname 'test-path-utils dirname' + norm_path "" "" norm_path . "" norm_path ./ "" diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index b79049f6f6..17a194bfa6 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -325,4 +325,17 @@ test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' ' test_cmp expect actual ' +test_expect_success 'no segfaults for reflog containing non-commit sha1s' ' + git update-ref --create-reflog -m "Creating ref" \ + refs/tests/tree-in-reflog HEAD && + git update-ref -m "Forcing tree" refs/tests/tree-in-reflog HEAD^{tree} && + git update-ref -m "Restoring to commit" refs/tests/tree-in-reflog HEAD && + git reflog refs/tests/tree-in-reflog +' + +test_expect_failure 'reflog with non-commit entries displays all entries' ' + git reflog refs/tests/tree-in-reflog >actual && + test_line_count = 3 actual +' + test_done diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index 88be904c09..c525656b2c 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -65,6 +65,13 @@ test_expect_success '--no-empty-directory hides empty directory' ' test_cmp expected3 output ' +test_expect_success 'ls-files --others handles non-submodule .git' ' + mkdir not-a-submodule && + echo foo >not-a-submodule/.git && + git ls-files -o >output && + test_cmp expected1 output +' + test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' ' git init super && git init sub && diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index da257c020f..3fc484e8c3 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -305,29 +305,4 @@ test_expect_success 'ls-files with "**" patterns and no slashes' ' test_cmp expect actual ' -test_expect_success 'negative patterns' ' - git init reinclude && - ( - cd reinclude && - cat >.gitignore <<-\EOF && - /fooo - /foo - !foo/bar/bar - EOF - mkdir fooo && - cat >fooo/.gitignore <<-\EOF && - !/* - EOF - mkdir -p foo/bar && - touch abc foo/def foo/bar/ghi foo/bar/bar && - git ls-files -o --exclude-standard >../actual && - cat >../expected <<-\EOF && - .gitignore - abc - foo/bar/bar - EOF - test_cmp ../expected ../actual - ) -' - test_done diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index d3913f9088..4261403cf6 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -176,4 +176,12 @@ test_expect_success 'git branch --points-at option' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tag not marked' ' + git tag ambiguous && + git branch ambiguous && + echo " ambiguous" >expect && + git branch --list ambiguous >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh index c0460bb0e5..43dd474a12 100755 --- a/t/t4056-diff-order.sh +++ b/t/t4056-diff-order.sh @@ -68,6 +68,12 @@ test_expect_success POSIXPERM,SANITY 'unreadable orderfile' ' test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD ' +test_expect_success "orderfile using option from subdir with --output" ' + mkdir subdir && + git -C subdir diff -O../order_file_1 --output ../actual --name-only HEAD^..HEAD && + test_cmp expect_1 actual +' + for i in 1 2 do test_expect_success "orderfile using option ($i)" ' diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 0ba9db0884..e3ee4bd700 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -708,4 +708,17 @@ test_expect_success 'fetching a one-level ref works' ' ) ' +test_expect_success 'fetching with auto-gc does not lock up' ' + write_script askyesno <<-\EOF && + echo "$*" && + false + EOF + git clone "file://$D" auto-gc && + test_commit test2 && + cd auto-gc && + git config gc.autoPackLimit 1 && + GIT_ASK_YESNO="$D/askyesno" git fetch >fetch.out 2>&1 && + ! grep "Should I try again" fetch.out +' + test_done diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh index c402d8d3d7..c7320121ec 100755 --- a/t/t5533-push-cas.sh +++ b/t/t5533-push-cas.sh @@ -25,7 +25,8 @@ test_expect_success 'push to update (protected)' ' ( cd dst && test_commit D && - test_must_fail git push --force-with-lease=master:master origin master + test_must_fail git push --force-with-lease=master:master origin master 2>err && + grep "stale info" err ) && git ls-remote . refs/heads/master >expect && git ls-remote src refs/heads/master >actual && @@ -37,7 +38,8 @@ test_expect_success 'push to update (protected, forced)' ' ( cd dst && test_commit D && - git push --force --force-with-lease=master:master origin master + git push --force --force-with-lease=master:master origin master 2>err && + grep "forced update" err ) && git ls-remote dst refs/heads/master >expect && git ls-remote src refs/heads/master >actual && @@ -101,7 +103,8 @@ test_expect_success 'push to update (allowed, tracking)' ' ( cd dst && test_commit D && - git push --force-with-lease=master origin master + git push --force-with-lease=master origin master 2>err && + ! grep "forced update" err ) && git ls-remote dst refs/heads/master >expect && git ls-remote src refs/heads/master >actual && @@ -114,7 +117,8 @@ test_expect_success 'push to update (allowed even though no-ff)' ' cd dst && git reset --hard HEAD^ && test_commit D && - git push --force-with-lease=master origin master + git push --force-with-lease=master origin master 2>err && + grep "forced update" err ) && git ls-remote dst refs/heads/master >expect && git ls-remote src refs/heads/master >actual && @@ -147,7 +151,8 @@ test_expect_success 'push to delete (allowed)' ' setup_srcdst_basic && ( cd dst && - git push --force-with-lease=master origin :master + git push --force-with-lease=master origin :master 2>err && + grep deleted err ) && >expect && git ls-remote src refs/heads/master >actual && diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index 190ee903cf..20aee43f95 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -346,4 +346,17 @@ test_expect_success 'conflict at EOF without LF resolved by --union' \ printf "line1\nline2\nline3x\nline3y" >expect.txt && test_cmp expect.txt output.txt' +test_expect_success 'conflict sections match existing line endings' ' + printf "1\\r\\n2\\r\\n3" >crlf-orig.txt && + printf "1\\r\\n2\\r\\n4" >crlf-diff1.txt && + printf "1\\r\\n2\\r\\n5" >crlf-diff2.txt && + test_must_fail git -c core.eol=crlf merge-file -p \ + crlf-diff1.txt crlf-orig.txt crlf-diff2.txt >crlf.txt && + test $(tr "\015" Q <crlf.txt | grep "^[<=>].*Q$" | wc -l) = 3 && + test $(tr "\015" Q <crlf.txt | grep "[345]Q$" | wc -l) = 3 && + test_must_fail git -c core.eol=crlf merge-file -p \ + nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >nolf.txt && + test $(tr "\015" Q <nolf.txt | grep "^[<=>].*Q$" | wc -l) = 0 +' + test_done diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh index 4d5a25eedf..c630aba657 100755 --- a/t/t6050-replace.sh +++ b/t/t6050-replace.sh @@ -351,11 +351,15 @@ test_expect_success 'test --format long' ' test_cmp expected actual ' -test_expect_success 'setup a fake editor' ' - write_script fakeeditor <<-\EOF +test_expect_success 'setup fake editors' ' + write_script fakeeditor <<-\EOF && sed -e "s/A U Thor/A fake Thor/" "$1" >"$1.new" mv "$1.new" "$1" EOF + write_script failingfakeeditor <<-\EOF + ./fakeeditor "$@" + false + EOF ' test_expect_success '--edit with and without already replaced object' ' @@ -372,7 +376,7 @@ test_expect_success '--edit with and without already replaced object' ' test_expect_success '--edit and change nothing or command failed' ' git replace -d "$PARA3" && test_must_fail env GIT_EDITOR=true git replace --edit "$PARA3" && - test_must_fail env GIT_EDITOR="./fakeeditor;false" git replace --edit "$PARA3" && + test_must_fail env GIT_EDITOR="./failingfakeeditor" git replace --edit "$PARA3" && GIT_EDITOR=./fakeeditor git replace --edit "$PARA3" && git replace -l | grep "$PARA3" && git cat-file commit "$PARA3" | grep "A fake Thor" diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 03873b09d1..19a2823025 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -49,11 +49,17 @@ test_atom() { } test_atom head refname refs/heads/master +test_atom head refname:short master +test_atom head refname:strip=1 heads/master +test_atom head refname:strip=2 master test_atom head upstream refs/remotes/origin/master +test_atom head upstream:short origin/master test_atom head push refs/remotes/myfork/master +test_atom head push:short myfork/master test_atom head objecttype commit test_atom head objectsize 171 test_atom head objectname $(git rev-parse refs/heads/master) +test_atom head objectname:short $(git rev-parse --short refs/heads/master) test_atom head tree $(git rev-parse refs/heads/master^{tree}) test_atom head parent '' test_atom head numparent 0 @@ -86,11 +92,13 @@ test_atom head contents 'Initial test_atom head HEAD '*' test_atom tag refname refs/tags/testtag +test_atom tag refname:short testtag test_atom tag upstream '' test_atom tag push '' test_atom tag objecttype tag test_atom tag objectsize 154 test_atom tag objectname $(git rev-parse refs/tags/testtag) +test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) test_atom tag tree '' test_atom tag parent '' test_atom tag numparent '' @@ -126,6 +134,16 @@ test_expect_success 'Check invalid atoms names are errors' ' test_must_fail git for-each-ref --format="%(INVALID)" refs/heads ' +test_expect_success 'arguments to :strip must be positive integers' ' + test_must_fail git for-each-ref --format="%(refname:strip=0)" && + test_must_fail git for-each-ref --format="%(refname:strip=-1)" && + test_must_fail git for-each-ref --format="%(refname:strip=foo)" +' + +test_expect_success 'stripping refnames too far gives an error' ' + test_must_fail git for-each-ref --format="%(refname:strip=3)" +' + test_expect_success 'Check format specifiers are ignored in naming date atoms' ' git for-each-ref --format="%(authordate)" refs/heads && git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && @@ -338,47 +356,14 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do " done -cat >expected <<\EOF -master -testtag -EOF - -test_expect_success 'Check short refname format' ' - (git for-each-ref --format="%(refname:short)" refs/heads && - git for-each-ref --format="%(refname:short)" refs/tags) >actual && - test_cmp expected actual -' - -cat >expected <<EOF -origin/master -EOF - -test_expect_success 'Check short upstream format' ' - git for-each-ref --format="%(upstream:short)" refs/heads >actual && - test_cmp expected actual -' - test_expect_success 'setup for upstream:track[short]' ' test_commit two ' -cat >expected <<EOF -[ahead 1] -EOF - -test_expect_success 'Check upstream:track format' ' - git for-each-ref --format="%(upstream:track)" refs/heads >actual && - test_cmp expected actual -' - -cat >expected <<EOF -> -EOF - -test_expect_success 'Check upstream:trackshort format' ' - git for-each-ref --format="%(upstream:trackshort)" refs/heads >actual && - test_cmp expected actual -' +test_atom head upstream:track '[ahead 1]' +test_atom head upstream:trackshort '>' +test_atom head push:track '[ahead 1]' +test_atom head push:trackshort '>' test_expect_success 'Check that :track[short] cannot be used with other atoms' ' test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null && @@ -398,21 +383,6 @@ test_expect_success 'Check that :track[short] works when upstream is invalid' ' test_cmp expected actual ' -test_expect_success '%(push) supports tracking specifiers, too' ' - echo "[ahead 1]" >expected && - git for-each-ref --format="%(push:track)" refs/heads >actual && - test_cmp expected actual -' - -cat >expected <<EOF -$(git rev-parse --short HEAD) -EOF - -test_expect_success 'Check short objectname format' ' - git for-each-ref --format="%(objectname:short)" refs/heads >actual && - test_cmp expected actual -' - test_expect_success 'Check for invalid refname format' ' test_must_fail git for-each-ref --format="%(refname:INVALID)" ' diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 869e0bf073..edb834187a 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -333,6 +333,14 @@ test_expect_success 'prune empty collapsed merges' ' test_cmp expect actual ' +test_expect_success 'prune empty works even without index/tree filters' ' + git rev-list HEAD >expect && + git commit --allow-empty -m empty && + git filter-branch -f --prune-empty HEAD && + git rev-list HEAD >actual && + test_cmp expect actual +' + test_expect_success '--remap-to-ancestor with filename filters' ' git checkout master && git reset --hard A && diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 3dd2f51e49..c64579fe15 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1558,4 +1558,12 @@ test_expect_success '--no-merged show unmerged tags' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tags not marked' ' + git tag ambiguous && + git branch ambiguous && + echo ambiguous >expect && + git tag -l ambiguous >actual && + test_cmp expect actual +' + test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 3c49536e0e..834d91a691 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -1527,6 +1527,21 @@ test_expect_success $PREREQ 'cccover adds Cc to all mail' ' test_cover_addresses "Cc" ' +test_expect_success $PREREQ 'escaped quotes in sendemail.aliasfiletype=mutt' ' + clean_fake_sendmail && + echo "alias sbd \\\"Dot U. Sir\\\" <somebody@example.org>" >.mutt && + git config --replace-all sendemail.aliasesfile "$(pwd)/.mutt" && + git config sendemail.aliasfiletype mutt && + git send-email \ + --from="Example <nobody@example.com>" \ + --to=sbd \ + --smtp-server="$(pwd)/fake.sendmail" \ + outdir/0001-*.patch \ + 2>errors >out && + grep "^!somebody@example\.org!$" commandline1 && + grep -F "To: \"Dot U. Sir\" <somebody@example.org>" out +' + test_expect_success $PREREQ 'sendemail.aliasfiletype=mailrc' ' clean_fake_sendmail && echo "alias sbd somebody@example.org" >.mailrc && diff --git a/t/test-lib.sh b/t/test-lib.sh index 16c4d7b516..51e4a88c33 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -907,9 +907,11 @@ yes () { y="$*" fi - while echo "$y" + i=0 + while test $i -lt 99 do - : + echo "$y" + i=$(($i+1)) done } @@ -1054,20 +1056,28 @@ test_lazy_prereq NOT_ROOT ' test "$uid" != 0 ' -# On a filesystem that lacks SANITY, a file can be deleted even if -# the containing directory doesn't have write permissions, or a file -# can be accessed even if the containing directory doesn't have read -# or execute permissions, causing our tests that validate that Git -# works sensibly in such situations. +# SANITY is about "can you correctly predict what the filesystem would +# do by only looking at the permission bits of the files and +# directories?" A typical example of !SANITY is running the test +# suite as root, where a test may expect "chmod -r file && cat file" +# to fail because file is supposed to be unreadable after a successful +# chmod. In an environment (i.e. combination of what filesystem is +# being used and who is running the tests) that lacks SANITY, you may +# be able to delete or create a file when the containing directory +# doesn't have write permissions, or access a file even if the +# containing directory doesn't have read or execute permissions. + test_lazy_prereq SANITY ' mkdir SANETESTD.1 SANETESTD.2 && chmod +w SANETESTD.1 SANETESTD.2 && >SANETESTD.1/x 2>SANETESTD.2/x && chmod -w SANETESTD.1 && + chmod -r SANETESTD.1/x && chmod -rx SANETESTD.2 || error "bug in test sript: cannot prepare SANETESTD" + ! test -r SANETESTD.1/x && ! rm SANETESTD.1/x && ! test -f SANETESTD.2/x status=$? diff --git a/test-path-utils.c b/test-path-utils.c index c67bf65b34..c3adcd87b8 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -39,6 +39,130 @@ static void normalize_argv_string(const char **var, const char *input) die("Bad value: %s\n", input); } +struct test_data { + const char *from; /* input: transform from this ... */ + const char *to; /* output: ... to this. */ + const char *alternative; /* output: ... or this. */ +}; + +static int test_function(struct test_data *data, char *(*func)(char *input), + const char *funcname) +{ + int failed = 0, i; + char buffer[1024]; + char *to; + + for (i = 0; data[i].to; i++) { + if (!data[i].from) + to = func(NULL); + else { + strcpy(buffer, data[i].from); + to = func(buffer); + } + if (!strcmp(to, data[i].to)) + continue; + if (!data[i].alternative) + error("FAIL: %s(%s) => '%s' != '%s'\n", + funcname, data[i].from, to, data[i].to); + else if (!strcmp(to, data[i].alternative)) + continue; + else + error("FAIL: %s(%s) => '%s' != '%s', '%s'\n", + funcname, data[i].from, to, data[i].to, + data[i].alternative); + failed = 1; + } + return failed; +} + +static struct test_data basename_data[] = { + /* --- POSIX type paths --- */ + { NULL, "." }, + { "", "." }, + { ".", "." }, + { "..", ".." }, + { "/", "/" }, + { "//", "/", "//" }, + { "///", "/", "//" }, + { "////", "/", "//" }, + { "usr", "usr" }, + { "/usr", "usr" }, + { "/usr/", "usr" }, + { "/usr//", "usr" }, + { "/usr/lib", "lib" }, + { "usr/lib", "lib" }, + { "usr/lib///", "lib" }, + +#if defined(__MINGW32__) || defined(_MSC_VER) + /* --- win32 type paths --- */ + { "\\usr", "usr" }, + { "\\usr\\", "usr" }, + { "\\usr\\\\", "usr" }, + { "\\usr\\lib", "lib" }, + { "usr\\lib", "lib" }, + { "usr\\lib\\\\\\", "lib" }, + { "C:/usr", "usr" }, + { "C:/usr", "usr" }, + { "C:/usr/", "usr" }, + { "C:/usr//", "usr" }, + { "C:/usr/lib", "lib" }, + { "C:usr/lib", "lib" }, + { "C:usr/lib///", "lib" }, + { "C:", "." }, + { "C:a", "a" }, + { "C:/", "/" }, + { "C:///", "/" }, + { "\\", "\\", "/" }, + { "\\\\", "\\", "/" }, + { "\\\\\\", "\\", "/" }, +#endif + { NULL, NULL } +}; + +static struct test_data dirname_data[] = { + /* --- POSIX type paths --- */ + { NULL, "." }, + { "", "." }, + { ".", "." }, + { "..", "." }, + { "/", "/" }, + { "//", "/", "//" }, + { "///", "/", "//" }, + { "////", "/", "//" }, + { "usr", "." }, + { "/usr", "/" }, + { "/usr/", "/" }, + { "/usr//", "/" }, + { "/usr/lib", "/usr" }, + { "usr/lib", "usr" }, + { "usr/lib///", "usr" }, + +#if defined(__MINGW32__) || defined(_MSC_VER) + /* --- win32 type paths --- */ + { "\\", "\\" }, + { "\\\\", "\\\\" }, + { "\\usr", "\\" }, + { "\\usr\\", "\\" }, + { "\\usr\\\\", "\\" }, + { "\\usr\\lib", "\\usr" }, + { "usr\\lib", "usr" }, + { "usr\\lib\\\\\\", "usr" }, + { "C:a", "C:." }, + { "C:/", "C:/" }, + { "C:///", "C:/" }, + { "C:/usr", "C:/" }, + { "C:/usr/", "C:/" }, + { "C:/usr//", "C:/" }, + { "C:/usr/lib", "C:/usr" }, + { "C:usr/lib", "C:usr" }, + { "C:usr/lib///", "C:usr" }, + { "\\\\\\", "\\" }, + { "\\\\\\\\", "\\" }, + { "C:", "C:.", "." }, +#endif + { NULL, NULL } +}; + int main(int argc, char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { @@ -133,6 +257,12 @@ int main(int argc, char **argv) return 0; } + if (argc == 2 && !strcmp(argv[1], "basename")) + return test_function(basename_data, basename, argv[1]); + + if (argc == 2 && !strcmp(argv[1], "dirname")) + return test_function(dirname_data, dirname, argv[1]); + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/worktree.c b/worktree.c index 981f810e80..6181a66f1e 100644 --- a/worktree.c +++ b/worktree.c @@ -176,10 +176,10 @@ struct worktree **get_worktrees(void) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; - if ((linked = get_linked_worktree(d->d_name))) { - ALLOC_GROW(list, counter + 1, alloc); - list[counter++] = linked; - } + if ((linked = get_linked_worktree(d->d_name))) { + ALLOC_GROW(list, counter + 1, alloc); + list[counter++] = linked; + } } closedir(dir); } @@ -375,6 +375,19 @@ FILE *xfdopen(int fd, const char *mode) return stream; } +FILE *fopen_for_writing(const char *path) +{ + FILE *ret = fopen(path, "w"); + + if (!ret && errno == EPERM) { + if (!unlink(path)) + ret = fopen(path, "w"); + else + errno = EPERM; + } + return ret; +} + int xmkstemp(char *template) { int fd; diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index 625198e058..d98f430c91 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -109,7 +109,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, return 0; } -static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) { xrecord_t **recs; int size = 0; @@ -125,6 +125,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add if (add_nl) { i = recs[count - 1]->size; if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') { + if (needs_cr) { + if (dest) + dest[size] = '\r'; + size++; + } + if (dest) dest[size] = '\n'; size++; @@ -133,14 +139,58 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add return size; } -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) +{ + return xdl_recs_copy_0(0, xe, i, count, needs_cr, add_nl, dest); +} + +static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest) +{ + return xdl_recs_copy_0(1, xe, i, count, needs_cr, add_nl, dest); +} + +/* + * Returns 1 if the i'th line ends in CR/LF (if it is the last line and + * has no eol, the preceding line, if any), 0 if it ends in LF-only, and + * -1 if the line ending cannot be determined. + */ +static int is_eol_crlf(xdfile_t *file, int i) { - return xdl_recs_copy_0(0, xe, i, count, add_nl, dest); + long size; + + if (i < file->nrec - 1) + /* All lines before the last *must* end in LF */ + return (size = file->recs[i]->size) > 1 && + file->recs[i]->ptr[size - 2] == '\r'; + if (!file->nrec) + /* Cannot determine eol style from empty file */ + return -1; + if ((size = file->recs[i]->size) && + file->recs[i]->ptr[size - 1] == '\n') + /* Last line; ends in LF; Is it CR/LF? */ + return size > 1 && + file->recs[i]->ptr[size - 2] == '\r'; + if (!i) + /* The only line has no eol */ + return -1; + /* Determine eol from second-to-last line */ + return (size = file->recs[i - 1]->size) > 1 && + file->recs[i - 1]->ptr[size - 2] == '\r'; } -static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m) { - return xdl_recs_copy_0(1, xe, i, count, add_nl, dest); + int needs_cr; + + /* Match post-images' preceding, or first, lines' end-of-line style */ + needs_cr = is_eol_crlf(&xe1->xdf2, m->i1 ? m->i1 - 1 : 0); + if (needs_cr) + needs_cr = is_eol_crlf(&xe2->xdf2, m->i2 ? m->i2 - 1 : 0); + /* Look at pre-image's first line, unless we already settled on LF */ + if (needs_cr) + needs_cr = is_eol_crlf(&xe1->xdf1, 0); + /* If still undecided, use LF-only */ + return needs_cr < 0 ? 0 : needs_cr; } static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, @@ -152,16 +202,17 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, int marker1_size = (name1 ? strlen(name1) + 1 : 0); int marker2_size = (name2 ? strlen(name2) + 1 : 0); int marker3_size = (name3 ? strlen(name3) + 1 : 0); + int needs_cr = is_cr_needed(xe1, xe2, m); if (marker_size <= 0) marker_size = DEFAULT_CONFLICT_MARKER_SIZE; /* Before conflicting part */ - size += xdl_recs_copy(xe1, i, m->i1 - i, 0, + size += xdl_recs_copy(xe1, i, m->i1 - i, 0, 0, dest ? dest + size : NULL); if (!dest) { - size += marker_size + 1 + marker1_size; + size += marker_size + 1 + needs_cr + marker1_size; } else { memset(dest + size, '<', marker_size); size += marker_size; @@ -170,17 +221,19 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, memcpy(dest + size + 1, name1, marker1_size - 1); size += marker1_size; } + if (needs_cr) + dest[size++] = '\r'; dest[size++] = '\n'; } /* Postimage from side #1 */ - size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, + size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1, dest ? dest + size : NULL); if (style == XDL_MERGE_DIFF3) { /* Shared preimage */ if (!dest) { - size += marker_size + 1 + marker3_size; + size += marker_size + 1 + needs_cr + marker3_size; } else { memset(dest + size, '|', marker_size); size += marker_size; @@ -189,25 +242,29 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, memcpy(dest + size + 1, name3, marker3_size - 1); size += marker3_size; } + if (needs_cr) + dest[size++] = '\r'; dest[size++] = '\n'; } - size += xdl_orig_copy(xe1, m->i0, m->chg0, 1, + size += xdl_orig_copy(xe1, m->i0, m->chg0, needs_cr, 1, dest ? dest + size : NULL); } if (!dest) { - size += marker_size + 1; + size += marker_size + 1 + needs_cr; } else { memset(dest + size, '=', marker_size); size += marker_size; + if (needs_cr) + dest[size++] = '\r'; dest[size++] = '\n'; } /* Postimage from side #2 */ - size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, + size += xdl_recs_copy(xe2, m->i2, m->chg2, needs_cr, 1, dest ? dest + size : NULL); if (!dest) { - size += marker_size + 1 + marker2_size; + size += marker_size + 1 + needs_cr + marker2_size; } else { memset(dest + size, '>', marker_size); size += marker_size; @@ -216,6 +273,8 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, memcpy(dest + size + 1, name2, marker2_size - 1); size += marker2_size; } + if (needs_cr) + dest[size++] = '\r'; dest[size++] = '\n'; } return size; @@ -241,21 +300,24 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, marker_size); else if (m->mode & 3) { /* Before conflicting part */ - size += xdl_recs_copy(xe1, i, m->i1 - i, 0, + size += xdl_recs_copy(xe1, i, m->i1 - i, 0, 0, dest ? dest + size : NULL); /* Postimage from side #1 */ - if (m->mode & 1) - size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2), + if (m->mode & 1) { + int needs_cr = is_cr_needed(xe1, xe2, m); + + size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, (m->mode & 2), dest ? dest + size : NULL); + } /* Postimage from side #2 */ if (m->mode & 2) - size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, + size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, 0, dest ? dest + size : NULL); } else continue; i = m->i1 + m->chg1; } - size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, + size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, 0, dest ? dest + size : NULL); return size; } |