diff options
263 files changed, 7988 insertions, 2740 deletions
diff --git a/.gitignore b/.gitignore index 521d8f4fb4..fc445edea9 100644 --- a/.gitignore +++ b/.gitignore @@ -231,7 +231,6 @@ *.ipdb *.dll .vs/ -*.manifest Debug/ Release/ /UpgradeLog*.htm diff --git a/Documentation/RelNotes/2.24.0.txt b/Documentation/RelNotes/2.24.0.txt new file mode 100644 index 0000000000..7f44e7a5f2 --- /dev/null +++ b/Documentation/RelNotes/2.24.0.txt @@ -0,0 +1,210 @@ +Git 2.24 Release Notes +====================== + +Updates since v2.23 +------------------- + +Backward compatibility note + + * Although it is not officially deprecated, "filter-branch" is + showing its age and alternatives are available. From this release, + we started to discourage its uses and hint people about + filter-repo. + +UI, Workflows & Features + + * We now have an active interim maintainer for the Git-Gui part of + the system. Praise and thank Pratyush Yadav for volunteering. + + * The command line parser learned "--end-of-options" notation; the + standard convention for scripters to have hardcoded set of options + first on the command line, and force the command to treat end-user + input as non-options, has been to use "--" as the delimiter, but + that would not work for commands that use "--" as a delimiter + between revs and pathspec. + + * A mechanism to affect the default setting for a (related) group of + configuration variables is introduced. + + * "git fetch" learned "--set-upstream" option to help those who first + clone from their private fork they intend to push to, add the true + upstream via "git remote add" and then "git fetch" from it. + + * Device-tree files learned their own userdiff patterns. + (merge 3c81760bc6 sb/userdiff-dts later to maint). + + * "git rebase --rebase-merges" learned to drive different merge + strategies and pass strategy specific options to them. + + * A new "pre-merge-commit" hook has been introduced. + + * Command line completion updates for "git -c var.name=val" have been + added. + + * The lazy clone machinery has been taught that there can be more + than one promisor remote and consult them in order when downloading + missing objects on demand. + + * The list-objects-filter API (used to create a sparse/lazy clone) + learned to take a combined filter specification. + + * The documentation and tests for "git format-patch" have been + cleaned up. + + * On Windows, the root level of UNC share is now allowed to be used + just like any other directory. + + * The command line completion support (in contrib/) learned about the + "--skip" option of "git revert" and "git cherry-pick". + + * "git rebase --keep-base <upstream>" tries to find the original base + of the topic being rebased and rebase on top of that same base, + which is useful when running the "git rebase -i" (and its limited + variant "git rebase -x"). + + The command also has learned to fast-forward in more cases where it + can instead of replaying to recreate identical commits. + + * A configuration variable tells "git fetch" to write the commit + graph after finishing. + + +Performance, Internal Implementation, Development Support etc. + + * The code to write commit-graph over given commit object names has + been made a bit more robust. + + * The first line of verbose output from each test piece now carries + the test name and number to help scanning with eyeballs. + + * Further clean-up of the initialization code. + + * xmalloc() used to have a mechanism to ditch memory and address + space resources as the last resort upon seeing an allocation + failure from the underlying malloc(), which made the code complex + and thread-unsafe with dubious benefit, as major memory resource + users already do limit their uses with various other mechanisms. + It has been simplified away. + + * Unnecessary full-tree diff in "git log -L" machinery has been + optimized away. + + * The http transport lacked some optimization the native transports + learned to avoid unnecessary ref advertisement, which has been + corrected. + + * Preparation for SHA-256 upgrade continues in the test department. + (merge 0c37c41d13 bc/hash-independent-tests-part-5 later to maint). + + * The memory ownership model of the "git fast-import" got + straightened out. + + * Output from trace2 subsystem is formatted more prettily now. + + * The internal code originally invented for ".gitignore" processing + got reshuffled and renamed to make it less tied to "excluding" and + stress more that it is about "matching", as it has been reused for + things like sparse checkout specification that want to check if a + path is "included". + + + +Fixes since v2.23 +----------------- + + * "git grep --recurse-submodules" that looks at the working tree + files looked at the contents in the index in submodules, instead of + files in the working tree. + (merge 6a289d45c0 mt/grep-submodules-working-tree later to maint). + + * Codepaths to walk tree objects have been audited for integer + overflows and hardened. + (merge 5aa02f9868 jk/tree-walk-overflow later to maint). + + * "git pack-refs" can lose refs that are created while running, which + is getting corrected. + (merge a613d4f817 sc/pack-refs-deletion-racefix later to maint). + + * "git checkout" and "git restore" to re-populate the index from a + tree-ish (typically HEAD) did not work correctly for a path that + was removed and then added again with the intent-to-add bit, when + the corresponding working tree file was empty. This has been + corrected. + + * Compilation fix. + (merge 70597e8386 rs/nedalloc-fixlets later to maint). + + * "git gui" learned to call the clean-up procedure before exiting. + (merge 0d88f3d2c5 py/git-gui-do-quit later to maint). + + * We promoted the "indent heuristics" that decides where to split + diff hunks from experimental to the default a few years ago, but + some stale documentation still marked it as experimental, which has + been corrected. + (merge 64e5e1fba1 sg/diff-indent-heuristic-non-experimental later to maint). + + * Fix a mismerge that happened in 2.22 timeframe. + (merge acb7da05ac en/checkout-mismerge-fix later to maint). + + * "git archive" recorded incorrect length in extended pax header in + some corner cases, which has been corrected. + (merge 71d41ff651 rs/pax-extended-header-length-fix later to maint). + + * On-demand object fetching in lazy clone incorrectly tried to fetch + commits from submodule projects, while still working in the + superproject, which has been corrected. + (merge a63694f523 jt/diff-lazy-fetch-submodule-fix later to maint). + + * Prepare get_short_oid() codepath to be thread-safe. + (merge 7cfcb16b0e rs/sort-oid-array-thread-safe later to maint). + + * "for-each-ref" and friends that show refs did not protect themselves + against ancient tags that did not record tagger names when asked to + show "%(taggername)", which have been corrected. + (merge 8b3f33ef11 mp/for-each-ref-missing-name-or-email later to maint). + + * The "git am" based backend of "git rebase" ignored the result of + updating ".gitattributes" done in one step when replaying + subsequent steps. + (merge 2c65d90f75 bc/reread-attributes-during-rebase later to maint). + + * Tell cURL library to use the same malloc() implementation, with the + xmalloc() wrapper, as the rest of the system, for consistency. + (merge 93b980e58f cb/curl-use-xmalloc later to maint). + + * Build fix to adjust .gitignore to unignore a path that we started to track. + (merge aac6ff7b5b js/visual-studio later to maint). + + * A few implementation fixes in the notes API. + (merge 60fe477a0b mh/notes-duplicate-entries later to maint). + + * Fix an earlier regression to "git push --all" which should have + been forbidden when the target remote repository is set to be a + mirror. + (merge 8e4c8af058 tg/push-all-in-mirror-forbidden later to maint). + + * Fix an earlier regression in the test suite, which mistakenly + stopped running HTTPD tests. + (merge 3960290675 sg/git-test-boolean later to maint). + + * "git rebase --autostash <upstream> <branch>", when <branch> is + different from the current branch, incorrectly moved the tip of the + current branch, which has been corrected. + (merge bf1e28e0ad bw/rebase-autostash-keep-current-branch later to maint). + + * Other code cleanup, docfix, build fix, etc. + (merge d1387d3895 en/fast-import-merge-doc later to maint). + (merge 1c24a54ea4 bm/repository-layout-typofix later to maint). + (merge 415b770b88 ds/midx-expire-repack later to maint). + (merge 19800bdc3f nd/diff-parseopt later to maint). + (merge 58166c2e9d tg/t0021-racefix later to maint). + (merge 7027f508c7 dl/compat-cleanup later to maint). + (merge e770fbfeff jc/test-cleanup later to maint). + (merge 1fd881d404 rs/trace2-dst-warning later to maint). + (merge 7e92756751 mh/http-urlmatch-cleanup later to maint). + (merge 9784f97321 mh/release-commit-memory-fix later to maint). + (merge 60d198d022 tb/banned-vsprintf-namefix later to maint). + (merge 80e3658647 rs/help-unknown-ref-does-not-return later to maint). + (merge 0a8bc7068f dt/remote-helper-doc-re-lock-option later to maint). + (merge 27fd1e4ea7 en/merge-options-ff-and-friends later to maint). + (merge 502c386ff9 sg/clean-nested-repo-with-ignored later to maint). diff --git a/Documentation/RelNotes/2.7.1.txt b/Documentation/RelNotes/2.7.1.txt index 6553d69e33..6323feaf64 100644 --- a/Documentation/RelNotes/2.7.1.txt +++ b/Documentation/RelNotes/2.7.1.txt @@ -10,7 +10,7 @@ Fixes since v2.7 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 + fields to be used by ALLOC_GROW(), but clear_pattern_list() forgot to reset 'alloc' to 0 when it cleared 'nr' to discard the managed array. diff --git a/Documentation/RelNotes/2.8.0.txt b/Documentation/RelNotes/2.8.0.txt index 25079710fa..5fbe1b86ee 100644 --- a/Documentation/RelNotes/2.8.0.txt +++ b/Documentation/RelNotes/2.8.0.txt @@ -270,7 +270,7 @@ notes for details). 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 + fields to be used by ALLOC_GROW(), but clear_pattern_list() forgot to reset 'alloc' to 0 when it cleared 'nr' to discard the managed array. diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 6d589e118c..1a60cc1329 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -372,9 +372,9 @@ such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:". Some parts of the system have dedicated maintainers with their own repositories. -- `git-gui/` comes from git-gui project, maintained by Pat Thoyts: +- `git-gui/` comes from git-gui project, maintained by Pratyush Yadav: - git://repo.or.cz/git-gui.git + https://github.com/prati0100/git-gui.git - `gitk-git/` comes from Paul Mackerras's gitk project: diff --git a/Documentation/config.txt b/Documentation/config.txt index e3f5bc3396..77f3b1486b 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -345,6 +345,8 @@ include::config/difftool.txt[] include::config/fastimport.txt[] +include::config/feature.txt[] + include::config/fetch.txt[] include::config/format.txt[] diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt index 75538d27e7..852d2ba37a 100644 --- a/Documentation/config/core.txt +++ b/Documentation/config/core.txt @@ -86,7 +86,9 @@ core.untrackedCache:: it will automatically be removed, if set to `false`. Before setting it to `true`, you should check that mtime is working properly on your system. - See linkgit:git-update-index[1]. `keep` by default. + See linkgit:git-update-index[1]. `keep` by default, unless + `feature.manyFiles` is enabled which sets this setting to + `true` by default. core.checkStat:: When missing or is set to `default`, many fields in the stat @@ -577,7 +579,7 @@ the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1]. core.commitGraph:: If true, then git will read the commit-graph file (if it exists) - to parse the graph structure of commits. Defaults to false. See + to parse the graph structure of commits. Defaults to true. See linkgit:git-commit-graph[1] for more information. core.useReplaceRefs:: diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt index 5afb5a2cbc..ff09f1cf73 100644 --- a/Documentation/config/diff.txt +++ b/Documentation/config/diff.txt @@ -189,7 +189,7 @@ diff.guitool:: include::../mergetools-diff.txt[] diff.indentHeuristic:: - Set this option to `true` to enable experimental heuristics + Set this option to `false` to disable the default heuristics that shift diff hunk boundaries to make patches easier to read. diff.algorithm:: diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt new file mode 100644 index 0000000000..875f8c8a66 --- /dev/null +++ b/Documentation/config/feature.txt @@ -0,0 +1,37 @@ +feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults. + +feature.experimental:: + Enable config options that are new to Git, and are being considered for + future defaults. Config settings included here may be added or removed + with each release, including minor version updates. These settings may + have unintended interactions since they are so new. Please enable this + setting if you are interested in providing feedback on experimental + features. The new default values are: ++ +* `pack.useSparse=true` uses a new algorithm when constructing a pack-file +which can improve `git push` performance in repos with many files. ++ +* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by +skipping more commits at a time, reducing the number of round trips. ++ +* `fetch.writeCommitGraph=true` writes a commit-graph after every `git fetch` +command that downloads a pack-file from a remote. Using the `--split` option, +most executions will create a very small commit-graph file on top of the +existing commit-graph file(s). Occasionally, these files will merge and the +write may take longer. Having an updated commit-graph file helps performance +of many Git commands, including `git merge-base`, `git push -f`, and +`git log --graph`. + +feature.manyFiles:: + Enable config options that optimize for repos with many files in the + working directory. With many files, commands such as `git status` and + `git checkout` may be slow and these new defaults improve performance: ++ +* `index.version=4` enables path-prefix compression in the index. ++ +* `core.untrackedCache=true` enables the untracked cache. This setting assumes +that mtime is working on your machine. diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt index ba890b5884..e8cb20547c 100644 --- a/Documentation/config/fetch.txt +++ b/Documentation/config/fetch.txt @@ -59,7 +59,8 @@ fetch.negotiationAlgorithm:: effort to converge faster, but may result in a larger-than-necessary packfile; The default is "default" which instructs Git to use the default algorithm that never skips commits (unless the server has acknowledged it or one - of its descendants). + of its descendants). If `feature.experimental` is enabled, then this + setting defaults to "skipping". Unknown values will cause 'git fetch' to error out. + See also the `--negotiation-tip` option for linkgit:git-fetch[1]. @@ -68,3 +69,13 @@ fetch.showForcedUpdates:: Set to false to enable `--no-show-forced-updates` in linkgit:git-fetch[1] and linkgit:git-pull[1] commands. Defaults to true. + +fetch.writeCommitGraph:: + Set to true to write a commit-graph after every `git fetch` command + that downloads a pack-file from a remote. Using the `--split` option, + most executions will create a very small commit-graph file on top of + the existing commit-graph file(s). Occasionally, these files will + merge and the write may take longer. Having an updated commit-graph + file helps performance of many Git commands, including `git merge-base`, + `git push -f`, and `git log --graph`. Defaults to false, unless + `feature.experimental` is true. diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt index 414a5a8a9d..cb629fa769 100644 --- a/Documentation/config/format.txt +++ b/Documentation/config/format.txt @@ -77,6 +77,7 @@ format.coverLetter:: A boolean that controls whether to generate a cover-letter when format-patch is invoked, but in addition can be set to "auto", to generate a cover-letter only when there's more than one patch. + Default is false. format.outputDirectory:: Set a custom directory to store the resulting files instead of the diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt index 02b92b18b5..00ea0a678e 100644 --- a/Documentation/config/gc.txt +++ b/Documentation/config/gc.txt @@ -63,7 +63,7 @@ gc.writeCommitGraph:: If true, then gc will rewrite the commit-graph file when linkgit:git-gc[1] is run. When using `git gc --auto` the commit-graph will be updated if housekeeping is - required. Default is false. See linkgit:git-commit-graph[1] + required. Default is true. See linkgit:git-commit-graph[1] for details. gc.logExpiry:: diff --git a/Documentation/config/index.txt b/Documentation/config/index.txt index f181503041..7cb50b37e9 100644 --- a/Documentation/config/index.txt +++ b/Documentation/config/index.txt @@ -24,3 +24,4 @@ index.threads:: index.version:: Specify the version with which new index files should be initialized. This does not affect existing repositories. + If `feature.manyFiles` is enabled, then the default is 4. diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt index 9cdcfa7324..1d66f0c992 100644 --- a/Documentation/config/pack.txt +++ b/Documentation/config/pack.txt @@ -112,7 +112,8 @@ pack.useSparse:: objects. This can have significant performance benefits when computing a pack to send a small change. However, it is possible that extra objects are added to the pack-file if the included - commits contain certain types of direct renames. + commits contain certain types of direct renames. Default is `false` + unless `feature.experimental` is enabled. pack.writeBitmaps (deprecated):: This is a deprecated synonym for `repack.writeBitmaps`. diff --git a/Documentation/config/remote.txt b/Documentation/config/remote.txt index 6c4cad83a2..a8e6437a90 100644 --- a/Documentation/config/remote.txt +++ b/Documentation/config/remote.txt @@ -76,3 +76,11 @@ remote.<name>.pruneTags:: + See also `remote.<name>.prune` and the PRUNING section of linkgit:git-fetch[1]. + +remote.<name>.promisor:: + When set to true, this remote will be used to fetch promisor + objects. + +remote.<name>.partialclonefilter:: + The filter that will be applied when fetching from this + promisor remote. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 3c9b4f9e09..99df1f3d4e 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -169,6 +169,13 @@ ifndef::git-pull[] Disable recursive fetching of submodules (this has the same effect as using the `--recurse-submodules=no` option). +--set-upstream:: + If the remote is fetched successfully, pull and add upstream + (tracking) reference, used by argument-less + linkgit:git-pull[1] and other commands. For more information, + see `branch.<name>.merge` and `branch.<name>.remote` in + linkgit:git-config[1]. + --submodule-prefix=<path>:: Prepend <path> to paths printed in informative messages such as "Fetching submodule foo". This option is used diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt index cc940eb9ad..784e934009 100644 --- a/Documentation/git-fast-export.txt +++ b/Documentation/git-fast-export.txt @@ -17,9 +17,9 @@ This program dumps the given revisions in a form suitable to be piped into 'git fast-import'. You can use it as a human-readable bundle replacement (see -linkgit:git-bundle[1]), or as a kind of an interactive -'git filter-branch'. - +linkgit:git-bundle[1]), or as a format that can be edited before being +fed to 'git fast-import' in order to do history rewrites (an ability +relied on by tools like 'git filter-repo'). OPTIONS ------- diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index fad327aecc..0bb276269e 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -391,7 +391,7 @@ change to the project. ('encoding' SP <encoding>)? data ('from' SP <commit-ish> LF)? - ('merge' SP <commit-ish> LF)? + ('merge' SP <commit-ish> LF)* (filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)* LF? .... diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 6b53dd7e06..5876598852 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -16,6 +16,19 @@ SYNOPSIS [--original <namespace>] [-d <directory>] [-f | --force] [--state-branch <branch>] [--] [<rev-list options>...] +WARNING +------- +'git filter-branch' has a plethora of pitfalls that can produce non-obvious +manglings of the intended history rewrite (and can leave you with little +time to investigate such problems since it has such abysmal performance). +These safety and performance issues cannot be backward compatibly fixed and +as such, its use is not recommended. Please use an alternative history +filtering tool such as https://github.com/newren/git-filter-repo/[git +filter-repo]. If you still need to use 'git filter-branch', please +carefully read <<SAFETY>> (and <<PERFORMANCE>>) to learn about the land +mines of filter-branch, and then vigilantly avoid as many of the hazards +listed there as reasonably possible. + DESCRIPTION ----------- Lets you rewrite Git revision history by rewriting the branches mentioned @@ -445,36 +458,236 @@ warned. (or if your git-gc is not new enough to support arguments to `--prune`, use `git repack -ad; git prune` instead). -NOTES ------ - -git-filter-branch allows you to make complex shell-scripted rewrites -of your Git history, but you probably don't need this flexibility if -you're simply _removing unwanted data_ like large files or passwords. -For those operations you may want to consider -http://rtyley.github.io/bfg-repo-cleaner/[The BFG Repo-Cleaner], -a JVM-based alternative to git-filter-branch, typically at least -10-50x faster for those use-cases, and with quite different -characteristics: - -* Any particular version of a file is cleaned exactly _once_. The BFG, - unlike git-filter-branch, does not give you the opportunity to - handle a file differently based on where or when it was committed - within your history. This constraint gives the core performance - benefit of The BFG, and is well-suited to the task of cleansing bad - data - you don't care _where_ the bad data is, you just want it - _gone_. - -* By default The BFG takes full advantage of multi-core machines, - cleansing commit file-trees in parallel. git-filter-branch cleans - commits sequentially (i.e. in a single-threaded manner), though it - _is_ possible to write filters that include their own parallelism, - in the scripts executed against each commit. - -* The http://rtyley.github.io/bfg-repo-cleaner/#examples[command options] - are much more restrictive than git-filter branch, and dedicated just - to the tasks of removing unwanted data- e.g: - `--strip-blobs-bigger-than 1M`. +[[PERFORMANCE]] +PERFORMANCE +----------- + +The performance of git-filter-branch is glacially slow; its design makes it +impossible for a backward-compatible implementation to ever be fast: + +* In editing files, git-filter-branch by design checks out each and +every commit as it existed in the original repo. If your repo has 10\^5 +files and 10\^5 commits, but each commit only modifies 5 files, then +git-filter-branch will make you do 10\^10 modifications, despite only +having (at most) 5*10^5 unique blobs. + +* If you try and cheat and try to make git-filter-branch only work on +files modified in a commit, then two things happen + + ** you run into problems with deletions whenever the user is simply + trying to rename files (because attempting to delete files that + don't exist looks like a no-op; it takes some chicanery to remap + deletes across file renames when the renames happen via arbitrary + user-provided shell) + + ** even if you succeed at the map-deletes-for-renames chicanery, you + still technically violate backward compatibility because users are + allowed to filter files in ways that depend upon topology of + commits instead of filtering solely based on file contents or names + (though this has not been observed in the wild). + +* Even if you don't need to edit files but only want to e.g. rename or +remove some and thus can avoid checking out each file (i.e. you can use +--index-filter), you still are passing shell snippets for your filters. +This means that for every commit, you have to have a prepared git repo +where those filters can be run. That's a significant setup. + +* Further, several additional files are created or updated per commit by +git-filter-branch. Some of these are for supporting the convenience +functions provided by git-filter-branch (such as map()), while others +are for keeping track of internal state (but could have also been +accessed by user filters; one of git-filter-branch's regression tests +does so). This essentially amounts to using the filesystem as an IPC +mechanism between git-filter-branch and the user-provided filters. +Disks tend to be a slow IPC mechanism, and writing these files also +effectively represents a forced synchronization point between separate +processes that we hit with every commit. + +* The user-provided shell commands will likely involve a pipeline of +commands, resulting in the creation of many processes per commit. +Creating and running another process takes a widely varying amount of +time between operating systems, but on any platform it is very slow +relative to invoking a function. + +* git-filter-branch itself is written in shell, which is kind of slow. +This is the one performance issue that could be backward-compatibly +fixed, but compared to the above problems that are intrinsic to the +design of git-filter-branch, the language of the tool itself is a +relatively minor issue. + + ** Side note: Unfortunately, people tend to fixate on the + written-in-shell aspect and periodically ask if git-filter-branch + could be rewritten in another language to fix the performance + issues. Not only does that ignore the bigger intrinsic problems + with the design, it'd help less than you'd expect: if + git-filter-branch itself were not shell, then the convenience + functions (map(), skip_commit(), etc) and the `--setup` argument + could no longer be executed once at the beginning of the program + but would instead need to be prepended to every user filter (and + thus re-executed with every commit). + +The https://github.com/newren/git-filter-repo/[git filter-repo] tool is +an alternative to git-filter-branch which does not suffer from these +performance problems or the safety problems (mentioned below). For those +with existing tooling which relies upon git-filter-branch, 'git +repo-filter' also provides +https://github.com/newren/git-filter-repo/blob/master/contrib/filter-repo-demos/filter-lamely[filter-lamely], +a drop-in git-filter-branch replacement (with a few caveats). While +filter-lamely suffers from all the same safety issues as +git-filter-branch, it at least ameloriates the performance issues a +little. + +[[SAFETY]] +SAFETY +------ + +git-filter-branch is riddled with gotchas resulting in various ways to +easily corrupt repos or end up with a mess worse than what you started +with: + +* Someone can have a set of "working and tested filters" which they +document or provide to a coworker, who then runs them on a different OS +where the same commands are not working/tested (some examples in the +git-filter-branch manpage are also affected by this). BSD vs. GNU +userland differences can really bite. If lucky, error messages are +spewed. But just as likely, the commands either don't do the filtering +requested, or silently corrupt by making some unwanted change. The +unwanted change may only affect a few commits, so it's not necessarily +obvious either. (The fact that problems won't necessarily be obvious +means they are likely to go unnoticed until the rewritten history is in +use for quite a while, at which point it's really hard to justify +another flag-day for another rewrite.) + +* Filenames with spaces are often mishandled by shell snippets since +they cause problems for shell pipelines. Not everyone is familiar with +find -print0, xargs -0, git-ls-files -z, etc. Even people who are +familiar with these may assume such flags are not relevant because +someone else renamed any such files in their repo back before the person +doing the filtering joined the project. And often, even those familiar +with handling arguments with spaces may not do so just because they +aren't in the mindset of thinking about everything that could possibly +go wrong. + +* Non-ascii filenames can be silently removed despite being in a desired +directory. Keeping only wanted paths is often done using pipelines like +`git ls-files | grep -v ^WANTED_DIR/ | xargs git rm`. ls-files will +only quote filenames if needed, so folks may not notice that one of the +files didn't match the regex (at least not until it's much too late). +Yes, someone who knows about core.quotePath can avoid this (unless they +have other special characters like \t, \n, or "), and people who use +ls-files -z with something other than grep can avoid this, but that +doesn't mean they will. + +* Similarly, when moving files around, one can find that filenames with +non-ascii or special characters end up in a different directory, one +that includes a double quote character. (This is technically the same +issue as above with quoting, but perhaps an interesting different way +that it can and has manifested as a problem.) + +* It's far too easy to accidentally mix up old and new history. It's +still possible with any tool, but git-filter-branch almost invites it. +If lucky, the only downside is users getting frustrated that they don't +know how to shrink their repo and remove the old stuff. If unlucky, +they merge old and new history and end up with multiple "copies" of each +commit, some of which have unwanted or sensitive files and others which +don't. This comes about in multiple different ways: + + ** the default to only doing a partial history rewrite ('--all' is not + the default and few examples show it) + + ** the fact that there's no automatic post-run cleanup + + ** the fact that --tag-name-filter (when used to rename tags) doesn't + remove the old tags but just adds new ones with the new name + + ** the fact that little educational information is provided to inform + users of the ramifications of a rewrite and how to avoid mixing old + and new history. For example, this man page discusses how users + need to understand that they need to rebase their changes for all + their branches on top of new history (or delete and reclone), but + that's only one of multiple concerns to consider. See the + "DISCUSSION" section of the git filter-repo manual page for more + details. + +* Annotated tags can be accidentally converted to lightweight tags, due +to either of two issues: + + ** Someone can do a history rewrite, realize they messed up, restore + from the backups in refs/original/, and then redo their + git-filter-branch command. (The backup in refs/original/ is not a + real backup; it dereferences tags first.) + + ** Running git-filter-branch with either --tags or --all in your + <rev-list options>. In order to retain annotated tags as + annotated, you must use --tag-name-filter (and must not have + restored from refs/original/ in a previously botched rewrite). + +* Any commit messages that specify an encoding will become corrupted +by the rewrite; git-filter-branch ignores the encoding, takes the original +bytes, and feeds it to commit-tree without telling it the proper +encoding. (This happens whether or not --msg-filter is used.) + +* Commit messages (even if they are all UTF-8) by default become +corrupted due to not being updated -- any references to other commit +hashes in commit messages will now refer to no-longer-extant commits. + +* There are no facilities for helping users find what unwanted crud they +should delete, which means they are much more likely to have incomplete +or partial cleanups that sometimes result in confusion and people +wasting time trying to understand. (For example, folks tend to just +look for big files to delete instead of big directories or extensions, +and once they do so, then sometime later folks using the new repository +who are going through history will notice a build artifact directory +that has some files but not others, or a cache of dependencies +(node_modules or similar) which couldn't have ever been functional since +it's missing some files.) + +* If --prune-empty isn't specified, then the filtering process can +create hoards of confusing empty commits + +* If --prune-empty is specified, then intentionally placed empty +commits from before the filtering operation are also pruned instead of +just pruning commits that became empty due to filtering rules. + +* If --prune empty is specified, sometimes empty commits are missed +and left around anyway (a somewhat rare bug, but it happens...) + +* A minor issue, but users who have a goal to update all names and +emails in a repository may be led to --env-filter which will only update +authors and committers, missing taggers. + +* If the user provides a --tag-name-filter that maps multiple tags to +the same name, no warning or error is provided; git-filter-branch simply +overwrites each tag in some undocumented pre-defined order resulting in +only one tag at the end. (A git-filter-branch regression test requires +this surprising behavior.) + +Also, the poor performance of git-filter-branch often leads to safety +issues: + +* Coming up with the correct shell snippet to do the filtering you want +is sometimes difficult unless you're just doing a trivial modification +such as deleting a couple files. Unfortunately, people often learn if +the snippet is right or wrong by trying it out, but the rightness or +wrongness can vary depending on special circumstances (spaces in +filenames, non-ascii filenames, funny author names or emails, invalid +timezones, presence of grafts or replace objects, etc.), meaning they +may have to wait a long time, hit an error, then restart. The +performance of git-filter-branch is so bad that this cycle is painful, +reducing the time available to carefully re-check (to say nothing about +what it does to the patience of the person doing the rewrite even if +they do technically have more time available). This problem is extra +compounded because errors from broken filters may not be shown for a +long time and/or get lost in a sea of output. Even worse, broken +filters often just result in silent incorrect rewrites. + +* To top it all off, even when users finally find working commands, they +naturally want to share them. But they may be unaware that their repo +didn't have some special cases that someone else's does. So, when +someone else with a different repository runs the same commands, they +get hit by the problems above. Or, the user just runs commands that +really were vetted for special cases, but they run it on a different OS +where it doesn't work, as noted above. GIT --- diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index b9b97e63ae..0ac56f4b70 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -17,9 +17,9 @@ SYNOPSIS [--signature-file=<file>] [-n | --numbered | -N | --no-numbered] [--start-number <n>] [--numbered-files] - [--in-reply-to=Message-Id] [--suffix=.<sfx>] + [--in-reply-to=<message id>] [--suffix=.<sfx>] [--ignore-if-in-upstream] - [--rfc] [--subject-prefix=Subject-Prefix] + [--rfc] [--subject-prefix=<subject prefix>] [(--reroll-count|-v) <n>] [--to=<email>] [--cc=<email>] [--[no-]cover-letter] [--quiet] @@ -159,9 +159,9 @@ Beware that the default for 'git send-email' is to thread emails itself. If you want `git format-patch` to take care of threading, you will want to ensure that threading is disabled for `git send-email`. ---in-reply-to=Message-Id:: +--in-reply-to=<message id>:: Make the first mail (or all the mails with `--no-thread`) appear as a - reply to the given Message-Id, which avoids breaking threads to + reply to the given <message id>, which avoids breaking threads to provide a new patch series. --ignore-if-in-upstream:: @@ -171,9 +171,9 @@ will want to ensure that threading is disabled for `git send-email`. patches being generated, and any patch that matches is ignored. ---subject-prefix=<Subject-Prefix>:: +--subject-prefix=<subject prefix>:: Instead of the standard '[PATCH]' prefix in the subject - line, instead use '[<Subject-Prefix>]'. This + line, instead use '[<subject prefix>]'. This allows for useful naming of a patch series, and can be combined with the `--numbered` option. @@ -314,7 +314,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`. --base=<commit>:: Record the base tree information to identify the state the patch series applies to. See the BASE TREE INFORMATION section - below for details. + below for details. If <commit> is "auto", a base commit is + automatically chosen. --root:: Treat the revision argument as a <revision range>, even if it @@ -330,8 +331,9 @@ CONFIGURATION ------------- You can specify extra mail header lines to be added to each message, defaults for the subject prefix and file suffix, number patches when -outputting more than one patch, add "To" or "Cc:" headers, configure -attachments, and sign off patches with configuration variables. +outputting more than one patch, add "To:" or "Cc:" headers, configure +attachments, change the patch output directory, and sign off patches +with configuration variables. ------------ [format] @@ -343,7 +345,8 @@ attachments, and sign off patches with configuration variables. cc = <email> attach [ = mime-boundary-string ] signOff = true - coverletter = auto + outputDirectory = <directory> + coverLetter = auto ------------ diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index 247f765604..0c114ad1ca 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -115,15 +115,14 @@ NOTES ----- 'git gc' tries very hard not to delete objects that are referenced -anywhere in your repository. In -particular, it will keep not only objects referenced by your current set -of branches and tags, but also objects referenced by the index, -remote-tracking branches, refs saved by 'git filter-branch' in -refs/original/, reflogs (which may reference commits in branches -that were later amended or rewound), and anything else in the refs/* namespace. -If you are expecting some objects to be deleted and they aren't, check -all of those locations and decide whether it makes sense in your case to -remove those references. +anywhere in your repository. In particular, it will keep not only +objects referenced by your current set of branches and tags, but also +objects referenced by the index, remote-tracking branches, notes saved +by 'git notes' under refs/notes/, reflogs (which may reference commits +in branches that were later amended or rewound), and anything else in +the refs/* namespace. If you are expecting some objects to be deleted +and they aren't, check all of those locations and decide whether it +makes sense in your case to remove those references. On the other hand, when 'git gc' runs concurrently with another process, there is a risk of it deleting an object that the other process is using diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 01fd52dc70..092529c619 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit] - [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]] + [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]] [--[no-]allow-unrelated-histories] [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...] 'git merge' (--continue | --abort | --quit) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 6156609cf7..639a4179d1 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -8,8 +8,8 @@ git-rebase - Reapply commits on top of another base tip SYNOPSIS -------- [verse] -'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] - [<upstream> [<branch>]] +'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] + [--onto <newbase> | --keep-base] [<upstream> [<branch>]] 'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>] 'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch) @@ -217,6 +217,24 @@ As a special case, you may use "A\...B" as a shortcut for the merge base of A and B if there is exactly one merge base. You can leave out at most one of A and B, in which case it defaults to HEAD. +--keep-base:: + Set the starting point at which to create the new commits to the + merge base of <upstream> <branch>. Running + 'git rebase --keep-base <upstream> <branch>' is equivalent to + running 'git rebase --onto <upstream>... <upstream>'. ++ +This option is useful in the case where one is developing a feature on +top of an upstream branch. While the feature is being worked on, the +upstream branch may advance and it may not be the best idea to keep +rebasing on top of the upstream but to keep the base commit as-is. ++ +Although both this option and --fork-point find the merge base between +<upstream> and <branch>, this option uses the merge base as the _starting +point_ on which new commits will be created, whereas --fork-point uses +the merge base to determine the _set of commits_ which will be rebased. ++ +See also INCOMPATIBLE OPTIONS below. + <upstream>:: Upstream branch to compare against. May be any valid commit, not just an existing branch name. Defaults to the configured @@ -369,6 +387,10 @@ ends up being empty, the <upstream> will be used as a fallback. + If either <upstream> or --root is given on the command line, then the default is `--no-fork-point`, otherwise the default is `--fork-point`. ++ +If your branch was based on <upstream> but <upstream> was rewound and +your branch contains commits which were dropped, this option can be used +with `--keep-base` in order to drop those commits from your branch. --ignore-whitespace:: --whitespace=<option>:: @@ -543,8 +565,8 @@ In addition, the following pairs of options are incompatible: * --preserve-merges and --interactive * --preserve-merges and --signoff * --preserve-merges and --rebase-merges - * --rebase-merges and --strategy - * --rebase-merges and --strategy-option + * --keep-base and --onto + * --keep-base and --root BEHAVIORAL DIFFERENCES ----------------------- @@ -832,7 +854,8 @@ Hard case: The changes are not the same.:: This happens if the 'subsystem' rebase had conflicts, or used `--interactive` to omit, edit, squash, or fixup commits; or if the upstream used one of `commit --amend`, `reset`, or - `filter-branch`. + a full history rewriting command like + https://github.com/newren/git-filter-repo[`filter-repo`]. The easy case @@ -870,7 +893,7 @@ NOTE: While an "easy case recovery" sometimes appears to be successful --interactive` will be **resurrected**! The idea is to manually tell 'git rebase' "where the old 'subsystem' -ended and your 'topic' began", that is, what the old merge-base +ended and your 'topic' began", that is, what the old merge base between them was. You will have to find a way to name the last commit of the old 'subsystem', for example: diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt index 246dc9943c..f271d758c3 100644 --- a/Documentation/git-replace.txt +++ b/Documentation/git-replace.txt @@ -123,10 +123,10 @@ The following format are available: CREATING REPLACEMENT OBJECTS ---------------------------- -linkgit:git-filter-branch[1], linkgit:git-hash-object[1] and -linkgit:git-rebase[1], among other git commands, can be used to create -replacement objects from existing objects. The `--edit` option can -also be used with 'git replace' to create a replacement object by +linkgit:git-hash-object[1], linkgit:git-rebase[1], and +https://github.com/newren/git-filter-repo[git-filter-repo], among other git commands, can be used to +create replacement objects from existing objects. The `--edit` option +can also be used with 'git replace' to create a replacement object by editing an existing object. If you want to replace many blobs, trees or commits that are part of a @@ -148,13 +148,13 @@ pending objects. SEE ALSO -------- linkgit:git-hash-object[1] -linkgit:git-filter-branch[1] linkgit:git-rebase[1] linkgit:git-tag[1] linkgit:git-branch[1] linkgit:git-commit[1] linkgit:git-var[1] linkgit:git[1] +https://github.com/newren/git-filter-repo[git-filter-repo] GIT --- diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 30711625fd..53774f5b64 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -769,11 +769,11 @@ option for (hopefully) obvious reasons. + This option is NOT recommended as it makes it difficult to track down old references to SVN revision numbers in existing documentation, bug -reports and archives. If you plan to eventually migrate from SVN to Git -and are certain about dropping SVN history, consider -linkgit:git-filter-branch[1] instead. filter-branch also allows -reformatting of metadata for ease-of-reading and rewriting authorship -info for non-"svn.authorsFile" users. +reports, and archives. If you plan to eventually migrate from SVN to +Git and are certain about dropping SVN history, consider +https://github.com/newren/git-filter-repo[git-filter-repo] instead. +filter-repo also allows reformatting of metadata for ease-of-reading +and rewriting authorship info for non-"svn.authorsFile" users. svn.useSvmProps:: svn-remote.<name>.useSvmProps:: diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index fb1d188d44..c5a528c667 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -810,6 +810,8 @@ patterns are available: - `css` suitable for cascading style sheets. +- `dts` suitable for devicetree (DTS) files. + - `fortran` suitable for source code in the Fortran language. - `fountain` suitable for Fountain documents. diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 1ed3ca33b7..4b32876b6e 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -37,6 +37,12 @@ arguments. Here are the rules: file called HEAD in your work tree, `git diff HEAD` is ambiguous, and you have to say either `git diff HEAD --` or `git diff -- HEAD` to disambiguate. + + * Because `--` disambiguates revisions and paths in some commands, it + cannot be used for those commands to separate options and revisions. + You can use `--end-of-options` for this (it also works for commands + that do not distinguish between revisions in paths, in which case it + is simply an alias for `--`). + When writing a script that is expected to handle random user-input, it is a good practice to make it explicit which arguments are which by placing diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 82cd573776..50365f2914 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -103,6 +103,28 @@ The default 'pre-commit' hook, when enabled--and with the `hooks.allownonascii` config option unset or set to false--prevents the use of non-ASCII filenames. +pre-merge-commit +~~~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-merge[1], and can be bypassed +with the `--no-verify` option. It takes no parameters, and is +invoked after the merge has been carried out successfully and before +obtaining the proposed commit log message to +make a commit. Exiting with a non-zero status from this script +causes the `git merge` command to abort before creating a commit. + +The default 'pre-merge-commit' hook, when enabled, runs the +'pre-commit' hook, if the latter is enabled. + +This hook is invoked with the environment variable +`GIT_EDITOR=:` if the command will not bring up an editor +to modify the commit message. + +If the merge cannot be carried out automatically, the conflicts +need to be resolved and the result committed separately (see +linkgit:git-merge[1]). At that point, this hook will not be executed, +but the 'pre-commit' hook will, if it is enabled. + prepare-commit-msg ~~~~~~~~~~~~~~~~~~ @@ -425,10 +447,12 @@ post-rewrite This hook is invoked by commands that rewrite commits (linkgit:git-commit[1] when called with `--amend` and -linkgit:git-rebase[1]; currently `git filter-branch` does 'not' call -it!). Its first argument denotes the command it was invoked by: -currently one of `amend` or `rebase`. Further command-dependent -arguments may be passed in the future. +linkgit:git-rebase[1]; however, full-history (re)writing tools like +linkgit:git-fast-import[1] or +https://github.com/newren/git-filter-repo[git-filter-repo] typically +do not call it!). Its first argument denotes the command it was +invoked by: currently one of `amend` or `rebase`. Further +command-dependent arguments may be passed in the future. The hook receives a list of the rewritten commits on stdin, in the format diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt index 43f80c8068..a5c3c04371 100644 --- a/Documentation/gitremote-helpers.txt +++ b/Documentation/gitremote-helpers.txt @@ -297,9 +297,13 @@ Supported if the helper has the "option" capability. same batch are complete. Only objects which were reported in the output of 'list' with a sha1 may be fetched this way. + -Optionally may output a 'lock <file>' line indicating a file under -GIT_DIR/objects/pack which is keeping a pack until refs can be -suitably updated. +Optionally may output a 'lock <file>' line indicating the full path of +a file under `$GIT_DIR/objects/pack` which is keeping a pack until +refs can be suitably updated. The path must end with `.keep`. This is +a mechanism to name a <pack,idx,keep> tuple by giving only the keep +component. The kept pack will not be deleted by a concurrent repack, +even though its objects may not be referenced until the fetch completes. +The `.keep` file will be deleted at the conclusion of the fetch. + If option 'check-connectivity' is requested, the helper must output 'connectivity-ok' if the clone is self-contained and connected. diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 216b11ee88..d6388f10bb 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -59,7 +59,7 @@ objects/[0-9a-f][0-9a-f]:: here are often called 'unpacked' (or 'loose') objects. objects/pack:: - Packs (files that store many object in compressed form, + Packs (files that store many objects in compressed form, along with index files to allow them to be randomly accessed) are found in this directory. diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 79a00d2a4a..59b8ff1e51 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -40,20 +40,24 @@ set to `no` at the beginning of them. case of a merge conflict. --ff:: - When the merge resolves as a fast-forward, only update the branch - pointer, without creating a merge commit. This is the default - behavior. - --no-ff:: - Create a merge commit even when the merge resolves as a - fast-forward. This is the default behaviour when merging an - annotated (and possibly signed) tag that is not stored in - its natural place in 'refs/tags/' hierarchy. - --ff-only:: - Refuse to merge and exit with a non-zero status unless the - current `HEAD` is already up to date or the merge can be - resolved as a fast-forward. + Specifies how a merge is handled when the merged-in history is + already a descendant of the current history. `--ff` is the + default unless merging an annotated (and possibly signed) tag + that is not stored in its natural place in the `refs/tags/` + hierarchy, in which case `--no-ff` is assumed. ++ +With `--ff`, when possible resolve the merge as a fast-forward (only +update the branch pointer to match the merged branch; do not create a +merge commit). When not possible (when the merged-in history is not a +descendant of the current history), create a merge commit. ++ +With `--no-ff`, create a merge commit in all cases, even when the merge +could instead be resolved as a fast-forward. ++ +With `--ff-only`, resolve the merge as a fast-forward when possible. +When not possible, refuse to merge and exit with a non-zero status. -S[<keyid>]:: --gpg-sign[=<keyid>]:: @@ -105,6 +109,10 @@ option can be used to override --squash. + With --squash, --commit is not allowed, and will fail. +--no-verify:: + This option bypasses the pre-merge and commit-msg hooks. + See also linkgit:githooks[5]. + -s <strategy>:: --strategy=<strategy>:: Use the given merge strategy; can be supplied more than diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index bb1251c036..90ff9e2bea 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -756,6 +756,22 @@ explicitly-given commit or tree. Note that the form '--filter=sparse:path=<path>' that wants to read from an arbitrary path on the filesystem has been dropped for security reasons. ++ +Multiple '--filter=' flags can be specified to combine filters. Only +objects which are accepted by every filter are included. ++ +The form '--filter=combine:<filter1>+<filter2>+...<filterN>' can also be +used to combined several filters, but this is harder than just repeating +the '--filter' flag and is usually not necessary. Filters are joined by +'{plus}' and individual filters are %-encoded (i.e. URL-encoded). +Besides the '{plus}' and '%' characters, the following characters are +reserved and also must be encoded: `~!@#$^&*()[]{}\;",<>?`+'`+ +as well as all characters with ASCII code <= `0x20`, which includes +space and newline. ++ +Other arbitrary characters can also be encoded. For instance, +'combine:tree:3+blob:none' and 'combine:tree%3A3+blob%3Anone' are +equivalent. --no-filter:: Turn off any previous `--filter=` argument. diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt index 5abb8e8b1f..76b6e4f71b 100644 --- a/Documentation/technical/api-directory-listing.txt +++ b/Documentation/technical/api-directory-listing.txt @@ -111,11 +111,11 @@ marked. If you to exclude files, make sure you have loaded index first. * Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0, sizeof(dir))`. -* To add single exclude pattern, call `add_exclude_list()` and then - `add_exclude()`. +* To add single exclude pattern, call `add_pattern_list()` and then + `add_pattern()`. * To add patterns from a file (e.g. `.git/info/exclude`), call - `add_excludes_from_file()` , and/or set `dir.exclude_per_dir`. A + `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`. A short-hand function `setup_standard_excludes()` can be used to set up the standard set of exclude settings. diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt index bde18622a8..7962e32854 100644 --- a/Documentation/technical/api-tree-walking.txt +++ b/Documentation/technical/api-tree-walking.txt @@ -62,9 +62,7 @@ Initializing `setup_traverse_info`:: Initialize a `traverse_info` given the pathname of the tree to start - traversing from. The `base` argument is assumed to be the `path` - member of the `name_entry` being recursed into unless the tree is a - top-level tree in which case the empty string ("") is used. + traversing from. Walking ------- @@ -140,6 +138,10 @@ same in the next callback invocation. This utilizes the memory structure of a tree entry to avoid the overhead of using a generic strlen(). +`strbuf_make_traverse_path`:: + + Convenience wrapper to `make_traverse_path` into a strbuf. + Authors ------- diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt index 896c7b3878..210373e258 100644 --- a/Documentation/technical/partial-clone.txt +++ b/Documentation/technical/partial-clone.txt @@ -30,12 +30,20 @@ advance* during clone and fetch operations and thereby reduce download times and disk usage. Missing objects can later be "demand fetched" if/when needed. +A remote that can later provide the missing objects is called a +promisor remote, as it promises to send the objects when +requested. Initialy Git supported only one promisor remote, the origin +remote from which the user cloned and that was configured in the +"extensions.partialClone" config option. Later support for more than +one promisor remote has been implemented. + Use of partial clone requires that the user be online and the origin -remote be available for on-demand fetching of missing objects. This may -or may not be problematic for the user. For example, if the user can -stay within the pre-selected subset of the source tree, they may not -encounter any missing objects. Alternatively, the user could try to -pre-fetch various objects if they know that they are going offline. +remote or other promisor remotes be available for on-demand fetching +of missing objects. This may or may not be problematic for the user. +For example, if the user can stay within the pre-selected subset of +the source tree, they may not encounter any missing objects. +Alternatively, the user could try to pre-fetch various objects if they +know that they are going offline. Non-Goals @@ -100,18 +108,18 @@ or commits that reference missing trees. Handling Missing Objects ------------------------ -- An object may be missing due to a partial clone or fetch, or missing due - to repository corruption. To differentiate these cases, the local - repository specially indicates such filtered packfiles obtained from the - promisor remote as "promisor packfiles". +- An object may be missing due to a partial clone or fetch, or missing + due to repository corruption. To differentiate these cases, the + local repository specially indicates such filtered packfiles + obtained from promisor remotes as "promisor packfiles". + These promisor packfiles consist of a "<name>.promisor" file with arbitrary contents (like the "<name>.keep" files), in addition to their "<name>.pack" and "<name>.idx" files. - The local repository considers a "promisor object" to be an object that - it knows (to the best of its ability) that the promisor remote has promised - that it has, either because the local repository has that object in one of + it knows (to the best of its ability) that promisor remotes have promised + that they have, either because the local repository has that object in one of its promisor packfiles, or because another promisor object refers to it. + When Git encounters a missing object, Git can see if it is a promisor object @@ -123,12 +131,12 @@ expensive-to-modify list of missing objects.[a] - Since almost all Git code currently expects any referenced object to be present locally and because we do not want to force every command to do a dry-run first, a fallback mechanism is added to allow Git to attempt - to dynamically fetch missing objects from the promisor remote. + to dynamically fetch missing objects from promisor remotes. + When the normal object lookup fails to find an object, Git invokes -fetch-object to try to get the object from the server and then retry -the object lookup. This allows objects to be "faulted in" without -complicated prediction algorithms. +promisor_remote_get_direct() to try to get the object from a promisor +remote and then retry the object lookup. This allows objects to be +"faulted in" without complicated prediction algorithms. + For efficiency reasons, no check as to whether the missing object is actually a promisor object is performed. @@ -157,8 +165,7 @@ and prefetch those objects in bulk. + We are not happy with this global variable and would like to remove it, but that requires significant refactoring of the object code to pass an -additional flag. We hope that concurrent efforts to add an ODB API can -encompass this. +additional flag. Fetching Missing Objects @@ -182,21 +189,63 @@ has been updated to not use any object flags when the corresponding argument though they are not necessary. +Using many promisor remotes +--------------------------- + +Many promisor remotes can be configured and used. + +This allows for example a user to have multiple geographically-close +cache servers for fetching missing blobs while continuing to do +filtered `git-fetch` commands from the central server. + +When fetching objects, promisor remotes are tried one after the other +until all the objects have been fetched. + +Remotes that are considered "promisor" remotes are those specified by +the following configuration variables: + +- `extensions.partialClone = <name>` + +- `remote.<name>.promisor = true` + +- `remote.<name>.partialCloneFilter = ...` + +Only one promisor remote can be configured using the +`extensions.partialClone` config variable. This promisor remote will +be the last one tried when fetching objects. + +We decided to make it the last one we try, because it is likely that +someone using many promisor remotes is doing so because the other +promisor remotes are better for some reason (maybe they are closer or +faster for some kind of objects) than the origin, and the origin is +likely to be the remote specified by extensions.partialClone. + +This justification is not very strong, but one choice had to be made, +and anyway the long term plan should be to make the order somehow +fully configurable. + +For now though the other promisor remotes will be tried in the order +they appear in the config file. + Current Limitations ------------------- -- The remote used for a partial clone (or the first partial fetch - following a regular clone) is marked as the "promisor remote". +- It is not possible to specify the order in which the promisor + remotes are tried in other ways than the order in which they appear + in the config file. + -We are currently limited to a single promisor remote and only that -remote may be used for subsequent partial fetches. +It is also not possible to specify an order to be used when fetching +from one remote and a different order when fetching from another +remote. + +- It is not possible to push only specific objects to a promisor + remote. + -We accept this limitation because we believe initial users of this -feature will be using it on repositories with a strong single central -server. +It is not possible to push at the same time to multiple promisor +remote in a specific order. -- Dynamic object fetching will only ask the promisor remote for missing - objects. We assume that the promisor remote has a complete view of the +- Dynamic object fetching will only ask promisor remotes for missing + objects. We assume that promisor remotes have a complete view of the repository and can satisfy all such requests. - Repack essentially treats promisor and non-promisor packfiles as 2 @@ -218,15 +267,17 @@ server. Future Work ----------- -- Allow more than one promisor remote and define a strategy for fetching - missing objects from specific promisor remotes or of iterating over the - set of promisor remotes until a missing object is found. +- Improve the way to specify the order in which promisor remotes are + tried. + -A user might want to have multiple geographically-close cache servers -for fetching missing blobs while continuing to do filtered `git-fetch` -commands from the central server, for example. +For example this could allow to specify explicitly something like: +"When fetching from this remote, I want to use these promisor remotes +in this order, though, when pushing or fetching to that remote, I want +to use those promisor remotes in that order." + +- Allow pushing to promisor remotes. + -Or the user might want to work in a triangular work flow with multiple +The user might want to work in a triangular work flow with multiple promisor remotes that each have an incomplete view of the repository. - Allow repack to work on promisor packfiles (while keeping them distinct diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index a1539a7ce6..98f88a28d3 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.23.0 +DEF_VER=v2.23.GIT LF=' ' @@ -884,7 +884,6 @@ LIB_OBJS += ewah/ewah_io.o LIB_OBJS += ewah/ewah_rlw.o LIB_OBJS += exec-cmd.o LIB_OBJS += fetch-negotiator.o -LIB_OBJS += fetch-object.o LIB_OBJS += fetch-pack.o LIB_OBJS += fsck.o LIB_OBJS += fsmonitor.o @@ -948,6 +947,7 @@ LIB_OBJS += preload-index.o LIB_OBJS += pretty.o LIB_OBJS += prio-queue.o LIB_OBJS += progress.o +LIB_OBJS += promisor-remote.o LIB_OBJS += prompt.o LIB_OBJS += protocol.o LIB_OBJS += quote.o @@ -965,6 +965,7 @@ LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o +LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o @@ -1 +1 @@ -Documentation/RelNotes/2.23.0.txt
\ No newline at end of file +Documentation/RelNotes/2.24.0.txt
\ No newline at end of file @@ -4643,6 +4643,7 @@ static int apply_patch(struct apply_state *state, struct patch *list = NULL, **listp = &list; int skipped_patch = 0; int res = 0; + int flush_attributes = 0; state->patch_input_file = filename; if (read_patch_file(&buf, fd) < 0) @@ -4670,6 +4671,14 @@ static int apply_patch(struct apply_state *state, patch_stats(state, patch); *listp = patch; listp = &patch->next; + + if ((patch->new_name && + ends_with_path_components(patch->new_name, + GITATTRIBUTES_FILE)) || + (patch->old_name && + ends_with_path_components(patch->old_name, + GITATTRIBUTES_FILE))) + flush_attributes = 1; } else { if (state->apply_verbosity > verbosity_normal) @@ -4746,6 +4755,8 @@ static int apply_patch(struct apply_state *state, if (state->summary && state->apply_verbosity > verbosity_silent) summary_patch_list(list); + if (flush_attributes) + reset_parsed_attributes(); end: free_patch_list(list); strbuf_release(&buf); diff --git a/archive-tar.c b/archive-tar.c index 3e53aac1e6..e16d3f756d 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -142,19 +142,25 @@ static int stream_blocked(const struct object_id *oid) * string and appends it to a struct strbuf. */ static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword, - const char *value, unsigned int valuelen) + const char *value, size_t valuelen) { - int len, tmp; + size_t orig_len = sb->len; + size_t len, tmp; /* "%u %s=%s\n" */ len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1; - for (tmp = len; tmp > 9; tmp /= 10) + for (tmp = 1; len / 10 >= tmp; tmp *= 10) len++; strbuf_grow(sb, len); - strbuf_addf(sb, "%u %s=", len, keyword); + strbuf_addf(sb, "%"PRIuMAX" %s=", (uintmax_t)len, keyword); strbuf_add(sb, value, valuelen); strbuf_addch(sb, '\n'); + + if (len != sb->len - orig_len) + BUG("pax extended header length miscalculated as %"PRIuMAX + ", should be %"PRIuMAX, + (uintmax_t)len, (uintmax_t)(sb->len - orig_len)); } /* @@ -259,7 +259,7 @@ struct pattern { const char *pattern; int patternlen; int nowildcardlen; - unsigned flags; /* EXC_FLAG_* */ + unsigned flags; /* PATTERN_FLAG_* */ }; /* @@ -400,11 +400,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, char *p = (char *)&(res->state[num_attr]); memcpy(p, name, namelen); res->u.pat.pattern = p; - parse_exclude_pattern(&res->u.pat.pattern, + parse_path_pattern(&res->u.pat.pattern, &res->u.pat.patternlen, &res->u.pat.flags, &res->u.pat.nowildcardlen); - if (res->u.pat.flags & EXC_FLAG_NEGATIVE) { + if (res->u.pat.flags & PATTERN_FLAG_NEGATIVE) { warning(_("Negative patterns are ignored in git attributes\n" "Use '\\!' for literal leading exclamation.")); goto fail_return; @@ -991,10 +991,10 @@ static int path_matches(const char *pathname, int pathlen, int prefix = pat->nowildcardlen; int isdir = (pathlen && pathname[pathlen - 1] == '/'); - if ((pat->flags & EXC_FLAG_MUSTBEDIR) && !isdir) + if ((pat->flags & PATTERN_FLAG_MUSTBEDIR) && !isdir) return 0; - if (pat->flags & EXC_FLAG_NODIR) { + if (pat->flags & PATTERN_FLAG_NODIR) { return match_basename(pathname + basename_offset, pathlen - basename_offset - isdir, pattern, prefix, @@ -26,7 +26,7 @@ #define vsprintf(...) BANNED(vsprintf) #else #define sprintf(buf,fmt,arg) BANNED(sprintf) -#define vsprintf(buf,fmt,arg) BANNED(sprintf) +#define vsprintf(buf,fmt,arg) BANNED(vsprintf) #endif #endif /* BANNED_H */ diff --git a/builtin/am.c b/builtin/am.c index 1aea657a7f..ee7305eaa6 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1272,7 +1272,9 @@ static void get_commit_info(struct am_state *state, struct commit *commit) buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); ident_line = find_commit_header(buffer, "author", &ident_len); - + if (!ident_line) + die(_("missing author line in commit %s"), + oid_to_hex(&commit->object.oid)); if (split_ident_line(&id, ident_line, ident_len) < 0) die(_("invalid ident line: %.*s"), (int)ident_len, ident_line); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 995d47c85a..d6a1aa74cd 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -15,6 +15,7 @@ #include "sha1-array.h" #include "packfile.h" #include "object-store.h" +#include "promisor-remote.h" struct batch_options { int enabled; @@ -524,8 +525,8 @@ static int batch_objects(struct batch_options *opt) if (opt->all_objects) { struct object_cb_data cb; - if (repository_format_partial_clone) - warning("This repository has extensions.partialClone set. Some objects may not be loaded."); + if (has_promisor_remote()) + warning("This repository uses promisor remotes. Some objects may not be loaded."); cb.opt = opt; cb.expand = &data; diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 599097304b..5a4f92395f 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -32,19 +32,19 @@ static const struct option check_ignore_options[] = { OPT_END() }; -static void output_exclude(const char *path, struct exclude *exclude) +static void output_pattern(const char *path, struct path_pattern *pattern) { - char *bang = (exclude && exclude->flags & EXC_FLAG_NEGATIVE) ? "!" : ""; - char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : ""; + char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : ""; + char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : ""; if (!nul_term_line) { if (!verbose) { write_name_quoted(path, stdout, '\n'); } else { - if (exclude) { - quote_c_style(exclude->el->src, NULL, stdout, 0); + if (pattern) { + quote_c_style(pattern->pl->src, NULL, stdout, 0); printf(":%d:%s%s%s\t", - exclude->srcpos, - bang, exclude->pattern, slash); + pattern->srcpos, + bang, pattern->pattern, slash); } else { printf("::\t"); @@ -56,11 +56,11 @@ static void output_exclude(const char *path, struct exclude *exclude) if (!verbose) { printf("%s%c", path, '\0'); } else { - if (exclude) + if (pattern) printf("%s%c%d%c%s%s%s%c%s%c", - exclude->el->src, '\0', - exclude->srcpos, '\0', - bang, exclude->pattern, slash, '\0', + pattern->pl->src, '\0', + pattern->srcpos, '\0', + bang, pattern->pattern, slash, '\0', path, '\0'); else printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0'); @@ -74,7 +74,7 @@ static int check_ignore(struct dir_struct *dir, const char *full_path; char *seen; int num_ignored = 0, i; - struct exclude *exclude; + struct path_pattern *pattern; struct pathspec pathspec; if (!argc) { @@ -103,15 +103,15 @@ static int check_ignore(struct dir_struct *dir, seen = find_pathspecs_matching_against_index(&pathspec, &the_index); for (i = 0; i < pathspec.nr; i++) { full_path = pathspec.items[i].match; - exclude = NULL; + pattern = NULL; if (!seen[i]) { int dtype = DT_UNKNOWN; - exclude = last_exclude_matching(dir, &the_index, + pattern = last_matching_pattern(dir, &the_index, full_path, &dtype); } - if (!quiet && (exclude || show_non_matching)) - output_exclude(pathspec.items[i].original, exclude); - if (exclude) + if (!quiet && (pattern || show_non_matching)) + output_pattern(pathspec.items[i].original, pattern); + if (pattern) num_ignored++; } free(seen); diff --git a/builtin/checkout.c b/builtin/checkout.c index 6123f732a2..1283727761 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -126,6 +126,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base, if (pos >= 0) { struct cache_entry *old = active_cache[pos]; if (ce->ce_mode == old->ce_mode && + !ce_intent_to_add(old) && oideq(&ce->oid, &old->oid)) { old->ce_flags |= CE_UPDATE; discard_cache_entry(ce); @@ -730,13 +731,6 @@ static int merge_working_tree(const struct checkout_opts *opts, "the following files:\n%s"), sb.buf); strbuf_release(&sb); - if (repo_index_has_changes(the_repository, - get_commit_tree(old_branch_info->commit), - &sb)) - warning(_("staged changes in the following files may be lost: %s"), - sb.buf); - strbuf_release(&sb); - /* Do more real merge */ /* @@ -1714,6 +1708,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.checkout_index = -2; /* default on */ opts.checkout_worktree = -2; /* default on */ + if (argc == 3 && !strcmp(argv[1], "-b")) { + /* + * User ran 'git checkout -b <branch>' and expects + * the same behavior as 'git switch -c <branch>'. + */ + opts.switch_branch_doing_nothing_is_ok = 0; + opts.only_merge_on_switching_branches = 1; + } + options = parse_options_dup(checkout_options); options = add_common_options(&opts, options); options = add_common_switch_branch_options(&opts, options); diff --git a/builtin/clean.c b/builtin/clean.c index d5579da716..851beb7f0d 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -648,7 +648,7 @@ static int filter_by_patterns_cmd(void) struct strbuf confirm = STRBUF_INIT; struct strbuf **ignore_list; struct string_list_item *item; - struct exclude_list *el; + struct pattern_list *pl; int changed = -1, i; for (;;) { @@ -671,7 +671,7 @@ static int filter_by_patterns_cmd(void) break; memset(&dir, 0, sizeof(dir)); - el = add_exclude_list(&dir, EXC_CMDL, "manual exclude"); + pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude"); ignore_list = strbuf_split_max(&confirm, ' ', 0); for (i = 0; ignore_list[i]; i++) { @@ -679,7 +679,7 @@ static int filter_by_patterns_cmd(void) if (!ignore_list[i]->len) continue; - add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1)); + add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1)); } changed = 0; @@ -901,7 +901,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct pathspec pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; - struct exclude_list *el; + struct pattern_list *pl; struct string_list_item *item; const char *qname; struct option options[] = { @@ -958,9 +958,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (!ignored) setup_standard_excludes(&dir); - el = add_exclude_list(&dir, EXC_CMDL, "--exclude option"); + pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option"); for (i = 0; i < exclude_list.nr; i++) - add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1)); + add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1)); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, diff --git a/builtin/clone.c b/builtin/clone.c index f665b28ccc..2048b6760a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -1160,13 +1160,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport->server_options = &server_options; if (filter_options.choice) { - struct strbuf expanded_filter_spec = STRBUF_INIT; - expand_list_objects_filter_spec(&filter_options, - &expanded_filter_spec); + const char *spec = + expand_list_objects_filter_spec(&filter_options); transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, - expanded_filter_spec.buf); + spec); transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); - strbuf_release(&expanded_filter_spec); } if (transport->smart_options && !deepen && !filter_options.choice) diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 38027b83d9..57863619b7 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -154,7 +154,7 @@ static int graph_write(int argc, const char **argv) struct string_list *commit_hex = NULL; struct string_list lines; int result = 0; - unsigned int flags = COMMIT_GRAPH_PROGRESS; + enum commit_graph_write_flags flags = COMMIT_GRAPH_WRITE_PROGRESS; static struct option builtin_commit_graph_write_options[] = { OPT_STRING(0, "object-dir", &opts.obj_dir, @@ -192,9 +192,9 @@ static int graph_write(int argc, const char **argv) if (!opts.obj_dir) opts.obj_dir = get_object_directory(); if (opts.append) - flags |= COMMIT_GRAPH_APPEND; + flags |= COMMIT_GRAPH_WRITE_APPEND; if (opts.split) - flags |= COMMIT_GRAPH_SPLIT; + flags |= COMMIT_GRAPH_WRITE_SPLIT; read_replace_refs = 0; @@ -213,8 +213,10 @@ static int graph_write(int argc, const char **argv) if (opts.stdin_packs) pack_indexes = &lines; - if (opts.stdin_commits) + if (opts.stdin_commits) { commit_hex = &lines; + flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS; + } UNLEAK(buf); } diff --git a/builtin/describe.c b/builtin/describe.c index 200154297d..e048f85484 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -313,7 +313,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) */ append_name(n, dst); if (longformat) - append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst); + append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst); if (suffix) strbuf_addstr(dst, suffix); return; diff --git a/builtin/fetch.c b/builtin/fetch.c index 717dd14e89..67c0eb88c6 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -23,6 +23,9 @@ #include "packfile.h" #include "list-objects-filter-options.h" #include "commit-reach.h" +#include "branch.h" +#include "promisor-remote.h" +#include "commit-graph.h" #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000) @@ -50,7 +53,8 @@ static int fetch_prune_tags_config = -1; /* unspecified */ static int prune_tags = -1; /* unspecified */ #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative; +static int all, append, dry_run, force, keep, multiple, update_head_ok; +static int verbosity, deepen_relative, set_upstream; static int progress = -1; static int enable_auto_gc = 1; static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; @@ -123,6 +127,8 @@ static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), OPT_BOOL(0, "all", &all, N_("fetch from all remotes")), + OPT_BOOL(0, "set-upstream", &set_upstream, + N_("set upstream for git pull/fetch")), OPT_BOOL('a', "append", &append, N_("append to .git/FETCH_HEAD instead of overwriting")), OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), @@ -1238,13 +1244,10 @@ static struct transport *prepare_transport(struct remote *remote, int deepen) if (update_shallow) set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); if (filter_options.choice) { - struct strbuf expanded_filter_spec = STRBUF_INIT; - expand_list_objects_filter_spec(&filter_options, - &expanded_filter_spec); - set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, - expanded_filter_spec.buf); + const char *spec = + expand_list_objects_filter_spec(&filter_options); + set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec); set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); - strbuf_release(&expanded_filter_spec); } if (negotiation_tip.nr) { if (transport->smart_options) @@ -1367,6 +1370,51 @@ static int do_fetch(struct transport *transport, retcode = 1; goto cleanup; } + + if (set_upstream) { + struct branch *branch = branch_get("HEAD"); + struct ref *rm; + struct ref *source_ref = NULL; + + /* + * We're setting the upstream configuration for the + * current branch. The relevent upstream is the + * fetched branch that is meant to be merged with the + * current one, i.e. the one fetched to FETCH_HEAD. + * + * When there are several such branches, consider the + * request ambiguous and err on the safe side by doing + * nothing and just emit a warning. + */ + for (rm = ref_map; rm; rm = rm->next) { + if (!rm->peer_ref) { + if (source_ref) { + warning(_("multiple branch detected, incompatible with --set-upstream")); + goto skip; + } else { + source_ref = rm; + } + } + } + if (source_ref) { + if (!strcmp(source_ref->name, "HEAD") || + starts_with(source_ref->name, "refs/heads/")) + install_branch_config(0, + branch->name, + transport->remote->name, + source_ref->name); + else if (starts_with(source_ref->name, "refs/remotes/")) + warning(_("not setting upstream for a remote remote-tracking branch")); + else if (starts_with(source_ref->name, "refs/tags/")) + warning(_("not setting upstream for a remote tag")); + else + warning(_("unknown branch type")); + } else { + warning(_("no source branch found.\n" + "you need to specify exactly one branch with the --set-upstream option.")); + } + } + skip: free_refs(ref_map); /* if neither --no-tags nor --tags was specified, do automated tag @@ -1510,37 +1558,27 @@ static inline void fetch_one_setup_partial(struct remote *remote) * If no prior partial clone/fetch and the current fetch DID NOT * request a partial-fetch, do a normal fetch. */ - if (!repository_format_partial_clone && !filter_options.choice) + if (!has_promisor_remote() && !filter_options.choice) return; /* - * If this is the FIRST partial-fetch request, we enable partial - * on this repo and remember the given filter-spec as the default - * for subsequent fetches to this remote. + * If this is a partial-fetch request, we enable partial on + * this repo if not already enabled and remember the given + * filter-spec as the default for subsequent fetches to this + * remote. */ - if (!repository_format_partial_clone && filter_options.choice) { + if (filter_options.choice) { partial_clone_register(remote->name, &filter_options); return; } /* - * We are currently limited to only ONE promisor remote and only - * allow partial-fetches from the promisor remote. - */ - if (strcmp(remote->name, repository_format_partial_clone)) { - if (filter_options.choice) - die(_("--filter can only be used with the remote " - "configured in extensions.partialClone")); - return; - } - - /* * Do a partial-fetch from the promisor remote using either the * explicitly given filter-spec or inherit the filter-spec from * the config. */ if (!filter_options.choice) - partial_clone_get_default_filter_spec(&filter_options); + partial_clone_get_default_filter_spec(&filter_options, remote->name); return; } @@ -1661,7 +1699,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (depth || deepen_since || deepen_not.nr) deepen = 1; - if (filter_options.choice && !repository_format_partial_clone) + if (filter_options.choice && !has_promisor_remote()) die("--filter can only be used when extensions.partialClone is set"); if (all) { @@ -1695,7 +1733,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } if (remote) { - if (filter_options.choice || repository_format_partial_clone) + if (filter_options.choice || has_promisor_remote()) fetch_one_setup_partial(remote); result = fetch_one(remote, argc, argv, prune_tags_ok); } else { @@ -1722,6 +1760,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) string_list_clear(&list, 0); + prepare_repo_settings(the_repository); + if (the_repository->settings.fetch_write_commit_graph) { + int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT; + struct split_commit_graph_opts split_opts; + memset(&split_opts, 0, sizeof(struct split_commit_graph_opts)); + + if (progress) + commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS; + + write_commit_graph_reachable(get_object_directory(), + commit_graph_flags, + &split_opts); + } + close_object_store(the_repository->objects); if (enable_auto_gc) { diff --git a/builtin/gc.c b/builtin/gc.c index c18efadda5..fadb45489f 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -27,6 +27,7 @@ #include "pack-objects.h" #include "blob.h" #include "tree.h" +#include "promisor-remote.h" #define FAILED_RUN "failed to run %s" @@ -41,7 +42,6 @@ static int aggressive_depth = 50; static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; -static int gc_write_commit_graph; static int detach_auto = 1; static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; @@ -148,7 +148,6 @@ static void gc_config(void) git_config_get_int("gc.aggressivedepth", &aggressive_depth); git_config_get_int("gc.auto", &gc_auto_threshold); git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); - git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph); git_config_get_bool("gc.autodetach", &detach_auto); git_config_get_expiry("gc.pruneexpire", &prune_expire); git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire); @@ -661,7 +660,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_push(&prune, prune_expire); if (quiet) argv_array_push(&prune, "--no-progress"); - if (repository_format_partial_clone) + if (has_promisor_remote()) argv_array_push(&prune, "--exclude-promisor-objects"); if (run_command_v_opt(prune.argv, RUN_GIT_CMD)) @@ -685,11 +684,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix) clean_pack_garbage(); } - if (gc_write_commit_graph && - write_commit_graph_reachable(get_object_directory(), - !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0, - NULL)) - return 1; + prepare_repo_settings(the_repository); + if (the_repository->settings.gc_write_commit_graph == 1) + write_commit_graph_reachable(get_object_directory(), + !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, + NULL); if (auto_gc && too_many_loose_objects()) warning(_("There are too many unreachable loose objects; " diff --git a/builtin/grep.c b/builtin/grep.c index 560051784e..69ac053acc 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -403,7 +403,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, static int grep_submodule(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_id *oid, - const char *filename, const char *path) + const char *filename, const char *path, int cached) { struct repository subrepo; struct repository *superproject = opt->repo; @@ -475,7 +475,7 @@ static int grep_submodule(struct grep_opt *opt, strbuf_release(&base); free(data); } else { - hit = grep_cache(&subopt, pathspec, 1); + hit = grep_cache(&subopt, pathspec, cached); } repo_clear(&subrepo); @@ -523,7 +523,8 @@ static int grep_cache(struct grep_opt *opt, } } else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && submodule_path_match(repo->index, pathspec, name.buf, NULL)) { - hit |= grep_submodule(opt, pathspec, NULL, ce->name, ce->name); + hit |= grep_submodule(opt, pathspec, NULL, ce->name, + ce->name, cached); } else { continue; } @@ -598,7 +599,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, free(data); } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { hit |= grep_submodule(opt, pathspec, &entry.oid, - base->buf, base->buf + tn_len); + base->buf, base->buf + tn_len, + 1); /* ignored */ } strbuf_setlen(base, old_baselen); @@ -1108,8 +1110,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) strbuf_addf(&buf, "+/%s%s", strcmp("less", pager) ? "" : "*", opt.pattern_list->pattern); - string_list_append(&path_list, buf.buf); - strbuf_detach(&buf, NULL); + string_list_append(&path_list, + strbuf_detach(&buf, NULL)); } } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 0d55f73b0b..a23454da6e 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -14,7 +14,7 @@ #include "thread-utils.h" #include "packfile.h" #include "object-store.h" -#include "fetch-object.h" +#include "promisor-remote.h" static const char index_pack_usage[] = "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; @@ -1352,7 +1352,7 @@ static void fix_unresolved_deltas(struct hashfile *f) sorted_by_pos[i] = &ref_deltas[i]; QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare); - if (repository_format_partial_clone) { + if (has_promisor_remote()) { /* * Prefetch the delta bases. */ @@ -1366,8 +1366,8 @@ static void fix_unresolved_deltas(struct hashfile *f) oid_array_append(&to_fetch, &d->oid); } if (to_fetch.nr) - fetch_objects(repository_format_partial_clone, - to_fetch.oid, to_fetch.nr); + promisor_remote_get_direct(the_repository, + to_fetch.oid, to_fetch.nr); oid_array_clear(&to_fetch); } diff --git a/builtin/log.c b/builtin/log.c index 44b10b3415..c4b35fdaf9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -627,6 +627,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) break; case OBJ_TAG: { struct tag *t = (struct tag *)o; + struct object_id *oid = get_tagged_oid(t); if (rev.shown_one) putchar('\n'); @@ -638,10 +639,10 @@ int cmd_show(int argc, const char **argv, const char *prefix) rev.shown_one = 1; if (ret) break; - o = parse_object(the_repository, &t->tagged->oid); + o = parse_object(the_repository, oid); if (!o) ret = error(_("could not read object %s"), - oid_to_hex(&t->tagged->oid)); + oid_to_hex(oid)); objects[i].item = o; i--; break; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 670e8fb93c..f069a028ce 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -492,7 +492,7 @@ static int option_parse_exclude_from(const struct option *opt, BUG_ON_OPT_NEG(unset); exc_given = 1; - add_excludes_from_file(dir, arg); + add_patterns_from_file(dir, arg); return 0; } @@ -516,7 +516,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) int require_work_tree = 0, show_tag = 0, i; const char *max_prefix; struct dir_struct dir; - struct exclude_list *el; + struct pattern_list *pl; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct option builtin_ls_files_options[] = { /* Think twice before adding "--nul" synonym to this */ @@ -594,9 +594,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) argc = parse_options(argc, argv, prefix, builtin_ls_files_options, ls_files_usage, 0); - el = add_exclude_list(&dir, EXC_CMDL, "--exclude option"); + pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option"); for (i = 0; i < exclude_list.nr; i++) { - add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args); + add_pattern(exclude_list.items[i].string, "", 0, pl, --exclude_args); } if (show_tag || show_valid_bit || show_fsmonitor_bit) { tag_cached = "H "; diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 97b54caeb9..e72714a5a8 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -180,8 +180,9 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const stru static char *traverse_path(const struct traverse_info *info, const struct name_entry *n) { - char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz); - return make_traverse_path(path, info, n); + struct strbuf buf = STRBUF_INIT; + strbuf_make_traverse_path(&buf, info, n->path, n->pathlen); + return strbuf_detach(&buf, NULL); } static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result) diff --git a/builtin/merge.c b/builtin/merge.c index e2ccbc44e2..c9746e37b8 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -81,7 +81,7 @@ static int show_progress = -1; static int default_to_upstream = 1; static int signoff; static const char *sign_commit; -static int verify_msg = 1; +static int no_verify; static struct strategy all_strategy[] = { { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, @@ -287,7 +287,7 @@ static struct option builtin_merge_options[] = { N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")), - OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")), + OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")), OPT_END() }; @@ -816,6 +816,18 @@ static void write_merge_heads(struct commit_list *); static void prepare_to_commit(struct commit_list *remoteheads) { struct strbuf msg = STRBUF_INIT; + const char *index_file = get_index_file(); + + if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL)) + abort_commit(remoteheads, NULL); + /* + * Re-read the index as pre-merge-commit hook could have updated it, + * and write it out as a tree. We must do this before we invoke + * the editor and after we invoke run_status above. + */ + if (find_hook("pre-merge-commit")) + discard_cache(); + read_cache_from(index_file); strbuf_addbuf(&msg, &merge_msg); if (squash) BUG("the control must not reach here under --squash"); @@ -842,7 +854,7 @@ static void prepare_to_commit(struct commit_list *remoteheads) abort_commit(remoteheads, NULL); } - if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(), + if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(), "commit-msg", git_path_merge_msg(the_repository), NULL)) abort_commit(remoteheads, NULL); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 76ce906946..5876583220 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -610,12 +610,12 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag, void *cb_data) { struct object_id peeled; - struct object_entry *entry = packlist_find(&to_pack, oid, NULL); + struct object_entry *entry = packlist_find(&to_pack, oid); if (entry) entry->tagged = 1; if (!peel_ref(path, &peeled)) { - entry = packlist_find(&to_pack, &peeled, NULL); + entry = packlist_find(&to_pack, &peeled); if (entry) entry->tagged = 1; } @@ -996,12 +996,11 @@ static int no_try_delta(const char *path) * few lines later when we want to add the new entry. */ static int have_duplicate_entry(const struct object_id *oid, - int exclude, - uint32_t *index_pos) + int exclude) { struct object_entry *entry; - entry = packlist_find(&to_pack, oid, index_pos); + entry = packlist_find(&to_pack, oid); if (!entry) return 0; @@ -1141,13 +1140,12 @@ static void create_object_entry(const struct object_id *oid, uint32_t hash, int exclude, int no_try_delta, - uint32_t index_pos, struct packed_git *found_pack, off_t found_offset) { struct object_entry *entry; - entry = packlist_alloc(&to_pack, oid->hash, index_pos); + entry = packlist_alloc(&to_pack, oid); entry->hash = hash; oe_set_type(entry, type); if (exclude) @@ -1171,11 +1169,10 @@ static int add_object_entry(const struct object_id *oid, enum object_type type, { struct packed_git *found_pack = NULL; off_t found_offset = 0; - uint32_t index_pos; display_progress(progress_state, ++nr_seen); - if (have_duplicate_entry(oid, exclude, &index_pos)) + if (have_duplicate_entry(oid, exclude)) return 0; if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) { @@ -1190,7 +1187,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type, create_object_entry(oid, type, pack_name_hash(name), exclude, name && no_try_delta(name), - index_pos, found_pack, found_offset); + found_pack, found_offset); return 1; } @@ -1199,17 +1196,15 @@ static int add_object_entry_from_bitmap(const struct object_id *oid, int flags, uint32_t name_hash, struct packed_git *pack, off_t offset) { - uint32_t index_pos; - display_progress(progress_state, ++nr_seen); - if (have_duplicate_entry(oid, 0, &index_pos)) + if (have_duplicate_entry(oid, 0)) return 0; if (!want_object_in_pack(oid, 0, &pack, &offset)) return 0; - create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset); + create_object_entry(oid, type, name_hash, 0, 0, pack, offset); return 1; } @@ -1507,7 +1502,7 @@ static int can_reuse_delta(const unsigned char *base_sha1, * First see if we're already sending the base (or it's explicitly in * our "excluded" list). */ - base = packlist_find(&to_pack, &base_oid, NULL); + base = packlist_find(&to_pack, &base_oid); if (base) { if (!in_same_island(&delta->idx.oid, &base->idx.oid)) return 0; @@ -2342,15 +2337,6 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, free(array); } -static void try_to_free_from_threads(size_t size) -{ - packing_data_lock(&to_pack); - release_pack_memory(size); - packing_data_unlock(&to_pack); -} - -static try_to_free_t old_try_to_free_routine; - /* * The main object list is split into smaller lists, each is handed to * one worker. @@ -2391,12 +2377,10 @@ static void init_threaded_search(void) pthread_mutex_init(&cache_mutex, NULL); pthread_mutex_init(&progress_mutex, NULL); pthread_cond_init(&progress_cond, NULL); - old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads); } static void cleanup_threaded_search(void) { - set_try_to_free_routine(old_try_to_free_routine); pthread_cond_destroy(&progress_cond); pthread_mutex_destroy(&cache_mutex); pthread_mutex_destroy(&progress_mutex); @@ -2579,7 +2563,7 @@ static void add_tag_chain(const struct object_id *oid) * it was included via bitmaps, we would not have parsed it * previously). */ - if (packlist_find(&to_pack, oid, NULL)) + if (packlist_find(&to_pack, oid)) return; tag = lookup_tag(the_repository, oid); @@ -2603,7 +2587,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag, if (starts_with(path, "refs/tags/") && /* is a tag? */ !peel_ref(path, &peeled) && /* peelable? */ - packlist_find(&to_pack, &peeled, NULL)) /* object packed? */ + packlist_find(&to_pack, &peeled)) /* object packed? */ add_tag_chain(oid); return 0; } @@ -2715,10 +2699,6 @@ static int git_pack_config(const char *k, const char *v, void *cb) use_bitmap_index_default = git_config_bool(k, v); return 0; } - if (!strcmp(k, "pack.usesparse")) { - sparse = git_config_bool(k, v); - return 0; - } if (!strcmp(k, "pack.threads")) { delta_search_threads = git_config_int(k, v); if (delta_search_threads < 0) @@ -2803,7 +2783,7 @@ static void show_object(struct object *obj, const char *name, void *data) for (p = strchr(name, '/'); p; p = strchr(p + 1, '/')) depth++; - ent = packlist_find(&to_pack, &obj->oid, NULL); + ent = packlist_find(&to_pack, &obj->oid); if (ent && depth > oe_tree_depth(&to_pack, ent)) oe_set_tree_depth(&to_pack, ent, depth); } @@ -3034,7 +3014,7 @@ static void loosen_unused_packed_objects(void) for (i = 0; i < p->num_objects; i++) { nth_packed_object_oid(&oid, p, i); - if (!packlist_find(&to_pack, &oid, NULL) && + if (!packlist_find(&to_pack, &oid) && !has_sha1_pack_kept_or_nonlocal(&oid) && !loosened_object_can_be_discarded(&oid, p->mtime)) if (force_object_loose(&oid, p->mtime)) @@ -3343,6 +3323,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs = 0; sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0); + prepare_repo_settings(the_repository); + if (!sparse && the_repository->settings.pack_use_sparse != -1) + sparse = the_repository->settings.pack_use_sparse; + reset_pack_idx_option(&pack_idx_opts); git_config(git_pack_config, NULL); diff --git a/builtin/pull.c b/builtin/pull.c index f1eaf6e6ed..d25ff13a60 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -129,6 +129,7 @@ static char *opt_refmap; static char *opt_ipv4; static char *opt_ipv6; static int opt_show_forced_updates = -1; +static char *set_upstream; static struct option pull_options[] = { /* Shared options */ @@ -243,6 +244,9 @@ static struct option pull_options[] = { PARSE_OPT_NOARG), OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates, N_("check for forced-updates on all updated branches")), + OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL, + N_("set upstream for git pull/fetch"), + PARSE_OPT_NOARG), OPT_END() }; @@ -556,6 +560,8 @@ static int run_fetch(const char *repo, const char **refspecs) argv_array_push(&args, "--show-forced-updates"); else if (opt_show_forced_updates == 0) argv_array_push(&args, "--no-show-forced-updates"); + if (set_upstream) + argv_array_push(&args, set_upstream); if (repo) { argv_array_push(&args, repo); diff --git a/builtin/push.c b/builtin/push.c index 021dd3b1e4..3742daf7b0 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -385,30 +385,14 @@ static int push_with_options(struct transport *transport, struct refspec *rs, } static int do_push(const char *repo, int flags, - const struct string_list *push_options) + const struct string_list *push_options, + struct remote *remote) { int i, errs; - struct remote *remote = pushremote_get(repo); const char **url; int url_nr; struct refspec *push_refspec = &rs; - if (!remote) { - if (repo) - die(_("bad repository '%s'"), repo); - die(_("No configured push destination.\n" - "Either specify the URL from the command-line or configure a remote repository using\n" - "\n" - " git remote add <name> <url>\n" - "\n" - "and then push using the remote name\n" - "\n" - " git push <name>\n")); - } - - if (remote->mirror) - flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); - if (push_options->nr) flags |= TRANSPORT_PUSH_OPTIONS; @@ -548,6 +532,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) struct string_list push_options_cmdline = STRING_LIST_INIT_DUP; struct string_list *push_options; const struct string_list_item *item; + struct remote *remote; struct option options[] = { OPT__VERBOSITY(&verbosity), @@ -602,20 +587,6 @@ int cmd_push(int argc, const char **argv, const char *prefix) die(_("--delete is incompatible with --all, --mirror and --tags")); if (deleterefs && argc < 2) die(_("--delete doesn't make sense without any refs")); - if (flags & TRANSPORT_PUSH_ALL) { - if (tags) - die(_("--all and --tags are incompatible")); - if (argc >= 2) - die(_("--all can't be combined with refspecs")); - } - if (flags & TRANSPORT_PUSH_MIRROR) { - if (tags) - die(_("--mirror and --tags are incompatible")); - if (argc >= 2) - die(_("--mirror can't be combined with refspecs")); - } - if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) - die(_("--all and --mirror are incompatible")); if (recurse_submodules == RECURSE_SUBMODULES_CHECK) flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK; @@ -632,11 +603,43 @@ int cmd_push(int argc, const char **argv, const char *prefix) set_refspecs(argv + 1, argc - 1, repo); } + remote = pushremote_get(repo); + if (!remote) { + if (repo) + die(_("bad repository '%s'"), repo); + die(_("No configured push destination.\n" + "Either specify the URL from the command-line or configure a remote repository using\n" + "\n" + " git remote add <name> <url>\n" + "\n" + "and then push using the remote name\n" + "\n" + " git push <name>\n")); + } + + if (remote->mirror) + flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); + + if (flags & TRANSPORT_PUSH_ALL) { + if (tags) + die(_("--all and --tags are incompatible")); + if (argc >= 2) + die(_("--all can't be combined with refspecs")); + } + if (flags & TRANSPORT_PUSH_MIRROR) { + if (tags) + die(_("--mirror and --tags are incompatible")); + if (argc >= 2) + die(_("--mirror can't be combined with refspecs")); + } + if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) + die(_("--all and --mirror are incompatible")); + for_each_string_list_item(item, push_options) if (strchr(item->string, '\n')) die(_("push options must not have new line characters")); - rc = do_push(repo, flags, push_options); + rc = do_push(repo, flags, push_options, remote); string_list_clear(&push_options_cmdline, 0); string_list_clear(&push_options_config, 0); if (rc == -1) diff --git a/builtin/rebase.c b/builtin/rebase.c index 670096c065..4a20582e72 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -29,8 +29,8 @@ #include "rebase-interactive.h" static char const * const builtin_rebase_usage[] = { - N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " - "[<upstream>] [<branch>]"), + N_("git rebase [-i] [options] [--exec <cmd>] " + "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"), N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " "--root [<branch>]"), N_("git rebase --continue | --abort | --skip | --edit-todo"), @@ -62,7 +62,7 @@ struct rebase_options { const char *onto_name; const char *revisions; const char *switch_to; - int root; + int root, root_with_onto; struct object_id *squash_onto; struct commit *restrict_revision; int dont_finish_rebase; @@ -374,6 +374,7 @@ static int run_rebase_interactive(struct rebase_options *opts, flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0; flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; + flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0; flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; switch (command) { @@ -1260,24 +1261,44 @@ static int is_linear_history(struct commit *from, struct commit *to) return 1; } -static int can_fast_forward(struct commit *onto, struct object_id *head_oid, - struct object_id *merge_base) +static int can_fast_forward(struct commit *onto, struct commit *upstream, + struct commit *restrict_revision, + struct object_id *head_oid, struct object_id *merge_base) { struct commit *head = lookup_commit(the_repository, head_oid); - struct commit_list *merge_bases; - int res; + struct commit_list *merge_bases = NULL; + int res = 0; if (!head) - return 0; + goto done; merge_bases = get_merge_bases(onto, head); - if (merge_bases && !merge_bases->next) { - oidcpy(merge_base, &merge_bases->item->object.oid); - res = oideq(merge_base, &onto->object.oid); - } else { + if (!merge_bases || merge_bases->next) { oidcpy(merge_base, &null_oid); - res = 0; + goto done; } + + oidcpy(merge_base, &merge_bases->item->object.oid); + if (!oideq(merge_base, &onto->object.oid)) + goto done; + + if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base)) + goto done; + + if (!upstream) + goto done; + + free_commit_list(merge_bases); + merge_bases = get_merge_bases(upstream, head); + if (!merge_bases || merge_bases->next) + goto done; + + if (!oideq(&onto->object.oid, &merge_bases->item->object.oid)) + goto done; + + res = 1; + +done: free_commit_list(merge_bases); return res && is_linear_history(onto, head); } @@ -1376,6 +1397,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct rebase_options options = REBASE_OPTIONS_INIT; const char *branch_name; int ret, flags, total_argc, in_progress = 0; + int keep_base = 0; int ok_to_skip_pre_rebase = 0; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; @@ -1394,6 +1416,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_STRING(0, "onto", &options.onto_name, N_("revision"), N_("rebase onto given branch instead of upstream")), + OPT_BOOL(0, "keep-base", &keep_base, + N_("use the merge-base of upstream and branch as the current base")), OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, N_("allow pre-rebase hook to run")), OPT_NEGBIT('q', "quiet", &options.flags, @@ -1547,6 +1571,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) warning(_("git rebase --preserve-merges is deprecated. " "Use --rebase-merges instead.")); + if (keep_base) { + if (options.onto_name) + die(_("cannot combine '--keep-base' with '--onto'")); + if (options.root) + die(_("cannot combine '--keep-base' with '--root'")); + } + if (action != ACTION_NONE && !in_progress) die(_("No rebase in progress?")); setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); @@ -1833,15 +1864,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) "'--reschedule-failed-exec'")); } - if (options.rebase_merges) { - if (strategy_options.nr) - die(_("cannot combine '--rebase-merges' with " - "'--strategy-option'")); - if (options.strategy) - die(_("cannot combine '--rebase-merges' with " - "'--strategy'")); - } - if (!options.root) { if (argc < 1) { struct branch *branch; @@ -1872,7 +1894,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.squash_onto = &squash_onto; options.onto_name = squash_onto_name = xstrdup(oid_to_hex(&squash_onto)); - } + } else + options.root_with_onto = 1; + options.upstream_name = NULL; options.upstream = NULL; if (argc > 1) @@ -1882,12 +1906,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } /* Make sure the branch to rebase onto is valid. */ - if (!options.onto_name) + if (keep_base) { + strbuf_reset(&buf); + strbuf_addstr(&buf, options.upstream_name); + strbuf_addstr(&buf, "..."); + options.onto_name = xstrdup(buf.buf); + } else if (!options.onto_name) options.onto_name = options.upstream_name; if (strstr(options.onto_name, "...")) { - if (get_oid_mb(options.onto_name, &merge_base) < 0) - die(_("'%s': need exactly one merge base"), - options.onto_name); + if (get_oid_mb(options.onto_name, &merge_base) < 0) { + if (keep_base) + die(_("'%s': need exactly one merge base with branch"), + options.upstream_name); + else + die(_("'%s': need exactly one merge base"), + options.onto_name); + } options.onto = lookup_commit_or_die(&merge_base, options.onto_name); } else { @@ -1968,9 +2002,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) state_dir_path("autostash", &options); struct child_process stash = CHILD_PROCESS_INIT; struct object_id oid; - struct commit *head = - lookup_commit_reference(the_repository, - &options.orig_head); argv_array_pushl(&stash.args, "stash", "create", "autostash", NULL); @@ -1991,17 +2022,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.state_dir); write_file(autostash, "%s", oid_to_hex(&oid)); printf(_("Created autostash: %s\n"), buf.buf); - if (reset_head(&head->object.oid, "reset --hard", + if (reset_head(NULL, "reset --hard", NULL, RESET_HEAD_HARD, NULL, NULL) < 0) die(_("could not reset --hard")); - printf(_("HEAD is now at %s"), - find_unique_abbrev(&head->object.oid, - DEFAULT_ABBREV)); - strbuf_reset(&buf); - pp_commit_easy(CMIT_FMT_ONELINE, head, &buf); - if (buf.len > 0) - printf(" %s", buf.buf); - putchar('\n'); if (discard_index(the_repository->index) < 0 || repo_read_index(the_repository) < 0) @@ -2022,13 +2045,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* * Check if we are already based on onto with linear history, - * but this should be done only when upstream and onto are the same - * and if this is not an interactive rebase. + * in which case we could fast-forward without replacing the commits + * with new commits recreated by replaying their changes. This + * optimization must not be done if this is an interactive rebase. */ - if (can_fast_forward(options.onto, &options.orig_head, &merge_base) && - !is_interactive(&options) && !options.restrict_revision && - options.upstream && - !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { + if (can_fast_forward(options.onto, options.upstream, options.restrict_revision, + &options.orig_head, &merge_base) && + !is_interactive(&options)) { int flag; if (!(options.flags & REBASE_FORCE)) { diff --git a/builtin/repack.c b/builtin/repack.c index 632c0c0a79..3b3dd14372 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -11,6 +11,7 @@ #include "midx.h" #include "packfile.h" #include "object-store.h" +#include "promisor-remote.h" static int delta_base_offset = 1; static int pack_kept_objects = -1; @@ -361,7 +362,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd.args, "--all"); argv_array_push(&cmd.args, "--reflog"); argv_array_push(&cmd.args, "--indexed-objects"); - if (repository_format_partial_clone) + if (has_promisor_remote()) argv_array_push(&cmd.args, "--exclude-promisor-objects"); if (write_bitmaps > 0) argv_array_push(&cmd.args, "--write-bitmap-index"); diff --git a/builtin/replace.c b/builtin/replace.c index 644b21ca8d..2a4afb3b93 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -421,7 +421,7 @@ static int check_one_mergetag(struct commit *commit, if (get_oid(mergetag_data->argv[i], &oid) < 0) return error(_("not a valid object name: '%s'"), mergetag_data->argv[i]); - if (oideq(&tag->tagged->oid, &oid)) + if (oideq(get_tagged_oid(tag), &oid)) return 0; /* found */ } diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 301ccb970b..b8dc2e1fba 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -473,8 +473,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) die(_("object filtering requires --objects")); if (filter_options.choice == LOFC_SPARSE_OID && !filter_options.sparse_oid_value) - die(_("invalid sparse value '%s'"), - filter_options.filter_spec); + die( + _("invalid sparse value '%s'"), + list_objects_filter_spec( + &filter_options)); continue; } if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { diff --git a/builtin/update-index.c b/builtin/update-index.c index dff2f4b837..49302d98c5 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -966,6 +966,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) struct parse_opt_ctx_t ctx; strbuf_getline_fn getline_fn; int parseopt_state = PARSE_OPT_UNKNOWN; + struct repository *r = the_repository; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"), @@ -1180,11 +1181,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) remove_split_index(&the_index); } + prepare_repo_settings(r); switch (untracked_cache) { case UC_UNSPECIFIED: break; case UC_DISABLE: - if (git_config_get_untracked_cache() == 1) + if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE) warning(_("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache")); @@ -1196,7 +1198,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) return !test_if_untracked_cache_is_supported(); case UC_ENABLE: case UC_FORCE: - if (git_config_get_untracked_cache() == 0) + if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE) warning(_("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache")); diff --git a/builtin/worktree.c b/builtin/worktree.c index a5bb02b207..7f094f8170 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -880,7 +880,7 @@ static void check_clean_worktree(struct worktree *wt, original_path); ret = xread(cp.out, buf, sizeof(buf)); if (ret) - die(_("'%s' is dirty, use --force to delete it"), + die(_("'%s' contains modified or untracked files, use --force to delete it"), original_path); close(cp.out); ret = finish_command(&cp); diff --git a/bulk-checkin.c b/bulk-checkin.c index 39ee7d6107..583aacb9e3 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -197,7 +197,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state, git_hash_ctx ctx; unsigned char obuf[16384]; unsigned header_len; - struct hashfile_checkpoint checkpoint; + struct hashfile_checkpoint checkpoint = {0}; struct pack_idx_entry *idx = NULL; seekback = lseek(fd, 0, SEEK_CUR); diff --git a/cache-tree.c b/cache-tree.c index 706ffcf188..0e5724fad7 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -5,6 +5,7 @@ #include "cache-tree.h" #include "object-store.h" #include "replace-object.h" +#include "promisor-remote.h" #ifndef DEBUG_CACHE_TREE #define DEBUG_CACHE_TREE 0 @@ -357,7 +358,7 @@ static int update_one(struct cache_tree *it, } ce_missing_ok = mode == S_IFGITLINK || missing_ok || - (repository_format_partial_clone && + (has_promisor_remote() && ce_skip_worktree(ce)); if (is_null_oid(oid) || (!ce_missing_ok && !has_object_file(oid))) { @@ -713,7 +714,7 @@ static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root if (!info->prev) return root; our_parent = find_cache_tree_from_traversal(root, info->prev); - return cache_tree_find(our_parent, info->name.path); + return cache_tree_find(our_parent, info->name); } int cache_tree_matches_traversal(struct cache_tree *root, @@ -636,6 +636,9 @@ int daemonize(void); * at least 'nr' entries; the number of entries currently allocated * is 'alloc', using the standard growing factor alloc_nr() macro. * + * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some + * added niceties. + * * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'. */ #define ALLOC_GROW(x, nr, alloc) \ @@ -649,6 +652,25 @@ int daemonize(void); } \ } while (0) +/* + * Similar to ALLOC_GROW but handles updating of the nr value and + * zeroing the bytes of the newly-grown array elements. + * + * DO NOT USE any expression with side-effect for any of the + * arguments. + */ +#define ALLOC_GROW_BY(x, nr, increase, alloc) \ + do { \ + if (increase) { \ + size_t new_nr = nr + (increase); \ + if (new_nr < nr) \ + BUG("negative growth in ALLOC_GROW_BY"); \ + ALLOC_GROW(x, new_nr, alloc); \ + memset((x) + nr, 0, sizeof(*(x)) * (increase)); \ + nr = new_nr; \ + } \ + } while (0) + /* Initialize and use the cache information */ struct lock_file; void preload_index(struct index_state *index, @@ -937,8 +959,6 @@ extern int grafts_replace_parents; #define GIT_REPO_VERSION 0 #define GIT_REPO_VERSION_READ 1 extern int repository_format_precious_objects; -extern char *repository_format_partial_clone; -extern const char *core_partial_clone_filter_default; extern int repository_format_worktree_config; /* @@ -160,7 +160,7 @@ linux-clang|linux-gcc) export CC=gcc-8 fi - export GIT_TEST_HTTPD=YesPlease + export GIT_TEST_HTTPD=true # The Linux build installs the defined dependency versions below. # The OS X build installs much more recent versions, whichever diff --git a/commit-graph.c b/commit-graph.c index fe954ab5f8..9b02d2c426 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -467,7 +467,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir) static int prepare_commit_graph(struct repository *r) { struct object_directory *odb; - int config_value; if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0)) die("dying as requested by the '%s' variable on commit-graph load!", @@ -477,9 +476,10 @@ static int prepare_commit_graph(struct repository *r) return !!r->objects->commit_graph; r->objects->commit_graph_attempted = 1; + prepare_repo_settings(r); + if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) && - (repo_config_get_bool(r, "core.commitgraph", &config_value) || - !config_value)) + r->settings.core_commit_graph != 1) /* * This repository is not configured to use commit graphs, so * do not load one. (But report commit_graph_attempted anyway @@ -783,7 +783,8 @@ struct write_commit_graph_context { unsigned append:1, report_progress:1, - split:1; + split:1, + check_oids:1; const struct split_commit_graph_opts *split_opts; }; @@ -1134,7 +1135,8 @@ static int add_ref_to_list(const char *refname, return 0; } -int write_commit_graph_reachable(const char *obj_dir, unsigned int flags, +int write_commit_graph_reachable(const char *obj_dir, + enum commit_graph_write_flags flags, const struct split_commit_graph_opts *split_opts) { struct string_list list = STRING_LIST_INIT_DUP; @@ -1193,8 +1195,8 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx, return 0; } -static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx, - struct string_list *commit_hex) +static int fill_oids_from_commit_hex(struct write_commit_graph_context *ctx, + struct string_list *commit_hex) { uint32_t i; struct strbuf progress_title = STRBUF_INIT; @@ -1215,20 +1217,21 @@ static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx, struct commit *result; display_progress(ctx->progress, i + 1); - if (commit_hex->items[i].string && - parse_oid_hex(commit_hex->items[i].string, &oid, &end)) - continue; - - result = lookup_commit_reference_gently(ctx->r, &oid, 1); - - if (result) { + if (!parse_oid_hex(commit_hex->items[i].string, &oid, &end) && + (result = lookup_commit_reference_gently(ctx->r, &oid, 1))) { ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc); oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid)); ctx->oids.nr++; + } else if (ctx->check_oids) { + error(_("invalid commit object id: %s"), + commit_hex->items[i].string); + return -1; } } stop_progress(&ctx->progress); strbuf_release(&progress_title); + + return 0; } static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx) @@ -1752,7 +1755,7 @@ out: int write_commit_graph(const char *obj_dir, struct string_list *pack_indexes, struct string_list *commit_hex, - unsigned int flags, + enum commit_graph_write_flags flags, const struct split_commit_graph_opts *split_opts) { struct write_commit_graph_context *ctx; @@ -1773,9 +1776,10 @@ int write_commit_graph(const char *obj_dir, if (len && ctx->obj_dir[len - 1] == '/') ctx->obj_dir[len - 1] = 0; - ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0; - ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0; - ctx->split = flags & COMMIT_GRAPH_SPLIT ? 1 : 0; + ctx->append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0; + ctx->report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0; + ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0; + ctx->check_oids = flags & COMMIT_GRAPH_WRITE_CHECK_OIDS ? 1 : 0; ctx->split_opts = split_opts; if (ctx->split) { @@ -1830,8 +1834,10 @@ int write_commit_graph(const char *obj_dir, goto cleanup; } - if (commit_hex) - fill_oids_from_commit_hex(ctx, commit_hex); + if (commit_hex) { + if ((res = fill_oids_from_commit_hex(ctx, commit_hex))) + goto cleanup; + } if (!pack_indexes && !commit_hex) fill_oids_from_all_packs(ctx); diff --git a/commit-graph.h b/commit-graph.h index df9a3b20e4..486e64e591 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -71,9 +71,13 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd, */ int generation_numbers_enabled(struct repository *r); -#define COMMIT_GRAPH_APPEND (1 << 0) -#define COMMIT_GRAPH_PROGRESS (1 << 1) -#define COMMIT_GRAPH_SPLIT (1 << 2) +enum commit_graph_write_flags { + COMMIT_GRAPH_WRITE_APPEND = (1 << 0), + COMMIT_GRAPH_WRITE_PROGRESS = (1 << 1), + COMMIT_GRAPH_WRITE_SPLIT = (1 << 2), + /* Make sure that each OID in the input is a valid commit OID. */ + COMMIT_GRAPH_WRITE_CHECK_OIDS = (1 << 3) +}; struct split_commit_graph_opts { int size_multiple; @@ -87,12 +91,13 @@ struct split_commit_graph_opts { * is not compatible with the commit-graph feature, then the * methods will return 0 without writing a commit-graph. */ -int write_commit_graph_reachable(const char *obj_dir, unsigned int flags, +int write_commit_graph_reachable(const char *obj_dir, + enum commit_graph_write_flags flags, const struct split_commit_graph_opts *split_opts); int write_commit_graph(const char *obj_dir, struct string_list *pack_indexes, struct string_list *commit_hex, - unsigned int flags, + enum commit_graph_write_flags flags, const struct split_commit_graph_opts *split_opts); #define COMMIT_GRAPH_VERIFY_SHALLOW (1 << 0) @@ -364,8 +364,8 @@ struct object_id *get_commit_tree_oid(const struct commit *commit) void release_commit_memory(struct parsed_object_pool *pool, struct commit *c) { set_commit_tree(c, NULL); - c->index = 0; free_commit_buffer(pool, c); + c->index = 0; free_commit_list(c->parents); c->object.parsed = 0; diff --git a/common-main.c b/common-main.c index 582a7b1886..71e21dd20a 100644 --- a/common-main.c +++ b/common-main.c @@ -39,16 +39,16 @@ int main(int argc, const char **argv) git_resolve_executable_dir(argv[0]); - trace2_initialize(); - trace2_cmd_start(argv); - trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP); - git_setup_gettext(); initialize_the_repository(); attr_start(); + trace2_initialize(); + trace2_cmd_start(argv); + trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP); + result = cmd_main(argc, argv); trace2_cmd_exit(result); diff --git a/compat/mingw.c b/compat/mingw.c index 738f0a826a..7a0d619fb6 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1161,14 +1161,21 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd, int isexe, int exe_only) { char path[MAX_PATH]; + wchar_t wpath[MAX_PATH]; snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd); - if (!isexe && access(path, F_OK) == 0) + if (xutftowcs_path(wpath, path) < 0) + return NULL; + + if (!isexe && _waccess(wpath, F_OK) == 0) return xstrdup(path); - path[strlen(path)-4] = '\0'; - if ((!exe_only || isexe) && access(path, F_OK) == 0) - if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY)) + wpath[wcslen(wpath)-4] = '\0'; + if ((!exe_only || isexe) && _waccess(wpath, F_OK) == 0) { + if (!(GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY)) { + path[strlen(path)-4] = '\0'; return xstrdup(path); + } + } return NULL; } @@ -1265,7 +1272,7 @@ static wchar_t *make_environment_block(char **deltaenv) } ALLOC_ARRAY(result, size); - memcpy(result, wenv, size * sizeof(*wenv)); + COPY_ARRAY(result, wenv, size); FreeEnvironmentStringsW(wenv); return result; } @@ -1309,7 +1316,7 @@ static wchar_t *make_environment_block(char **deltaenv) continue; size = wcslen(array[i]) + 1; - memcpy(p, array[i], size * sizeof(*p)); + COPY_ARRAY(p, array[i], size); p += size; } *p = L'\0'; diff --git a/compat/mingw.h b/compat/mingw.h index a03e40e6e2..9ad204c57c 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -11,7 +11,7 @@ typedef _sigset_t sigset_t; #undef _POSIX_THREAD_SAFE_FUNCTIONS #endif -extern int mingw_core_config(const char *var, const char *value, void *cb); +int mingw_core_config(const char *var, const char *value, void *cb); #define platform_core_config mingw_core_config /* @@ -443,7 +443,7 @@ static inline void convert_slashes(char *path) *path = '/'; } #define PATH_SEP ';' -extern char *mingw_query_user_email(void); +char *mingw_query_user_email(void); #define query_user_email mingw_query_user_email #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800) #define PRIuMAX "I64u" @@ -580,4 +580,4 @@ int main(int argc, const char **argv); /* * Used by Pthread API implementation for Windows */ -extern int err_win_to_posix(DWORD winerr); +int err_win_to_posix(DWORD winerr); diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h index b833ff9225..9134349590 100644 --- a/compat/nedmalloc/malloc.c.h +++ b/compat/nedmalloc/malloc.c.h @@ -1755,10 +1755,10 @@ static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) { assert(sl->l != 0); assert(sl->threadid == CURRENT_THREAD); if (--sl->c == 0) { - sl->threadid = 0; volatile unsigned int* lp = &sl->l; int prev = 0; int ret; + sl->threadid = 0; __asm__ __volatile__ ("lock; xchgl %0, %1" : "=r" (ret) : "m" (*(lp)), "0"(prev) @@ -3066,7 +3066,7 @@ static int init_mparams(void) { #if !ONLY_MSPACES /* Set up lock for main malloc area */ gm->mflags = mparams.default_mflags; - INITIAL_LOCK(&gm->mutex); + (void)INITIAL_LOCK(&gm->mutex); #endif #if (FOOTERS && !INSECURE) @@ -5017,7 +5017,7 @@ static mstate init_user_mstate(char* tbase, size_t tsize) { mchunkptr msp = align_as_chunk(tbase); mstate m = (mstate)(chunk2mem(msp)); memset(m, 0, msize); - INITIAL_LOCK(&m->mutex); + (void)INITIAL_LOCK(&m->mutex); msp->head = (msize|PINUSE_BIT|CINUSE_BIT); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h index c6cb8dd219..f1cfe73de9 100644 --- a/compat/win32/pthread.h +++ b/compat/win32/pthread.h @@ -50,7 +50,7 @@ typedef struct { DWORD tid; } pthread_t; -extern int pthread_create(pthread_t *thread, const void *unused, +int pthread_create(pthread_t *thread, const void *unused, void *(*start_routine)(void*), void *arg); /* @@ -59,10 +59,10 @@ extern int pthread_create(pthread_t *thread, const void *unused, */ #define pthread_join(a, b) win32_pthread_join(&(a), (b)) -extern int win32_pthread_join(pthread_t *thread, void **value_ptr); +int win32_pthread_join(pthread_t *thread, void **value_ptr); #define pthread_equal(t1, t2) ((t1).tid == (t2).tid) -extern pthread_t pthread_self(void); +pthread_t pthread_self(void); static inline void NORETURN pthread_exit(void *ret) { @@ -275,7 +275,7 @@ static int include_by_branch(const char *cond, size_t cond_len) int flags; int ret; struct strbuf pattern = STRBUF_INIT; - const char *refname = !the_repository || !the_repository->gitdir ? + const char *refname = !the_repository->gitdir ? NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags); const char *shortname; @@ -1379,11 +1379,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb) return 0; } - if (!strcmp(var, "core.partialclonefilter")) { - return git_config_string(&core_partial_clone_filter_default, - var, value); - } - if (!strcmp(var, "core.usereplacerefs")) { read_replace_refs = git_config_bool(var, value); return 0; @@ -2288,30 +2283,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestam return -1; /* thing exists but cannot be parsed */ } -int git_config_get_untracked_cache(void) -{ - int val = -1; - const char *v; - - /* Hack for test programs like test-dump-untracked-cache */ - if (ignore_untracked_cache_config) - return -1; - - if (!git_config_get_maybe_bool("core.untrackedcache", &val)) - return val; - - if (!git_config_get_value("core.untrackedcache", &v)) { - if (!strcasecmp(v, "keep")) - return -1; - - error(_("unknown core.untrackedCache value '%s'; " - "using 'keep' default value"), v); - return -1; - } - - return -1; /* default value */ -} - int git_config_get_split_index(void) { int val; @@ -915,6 +915,10 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, if (protocol == PROTO_LOCAL) path = end; + else if (protocol == PROTO_FILE && *host != '/' && + !has_dos_drive_prefix(host) && + offset_1st_component(host - 2) > 1) + path = host - 2; /* include the leading "//" */ else if (protocol == PROTO_FILE && has_dos_drive_prefix(end)) path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */ else diff --git a/connected.c b/connected.c index cd9b324afa..971db009b3 100644 --- a/connected.c +++ b/connected.c @@ -5,6 +5,7 @@ #include "connected.h" #include "transport.h" #include "packfile.h" +#include "promisor-remote.h" /* * If we feed all the commits we want to verify to this command @@ -73,7 +74,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data, argv_array_push(&rev_list.args,"rev-list"); argv_array_push(&rev_list.args, "--objects"); argv_array_push(&rev_list.args, "--stdin"); - if (repository_format_partial_clone) + if (has_promisor_remote()) argv_array_push(&rev_list.args, "--exclude-promisor-objects"); if (!opt->is_deepening_fetch) { argv_array_push(&rev_list.args, "--not"); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e087c4bf00..59cd3e8e90 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -340,7 +340,7 @@ __gitcomp () c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in - --*=*|*.) ;; + --*=|*.) ;; *) c="$c " ;; esac COMPREPLY[i++]="${2-}$c" @@ -360,7 +360,7 @@ __gitcomp () c="$c${4-}" if [[ $c == "$cur_"* ]]; then case $c in - --*=*|*.) ;; + *=|*.) ;; *) c="$c " ;; esac COMPREPLY[i++]="${2-}$c" @@ -524,7 +524,7 @@ __git_index_files () # Even when a directory name itself does not contain # any special characters, it will still be quoted if # any of its (stripped) trailing path components do. - # Because of this we may have seen the same direcory + # Because of this we may have seen the same directory # both quoted and unquoted. if (p in paths) # We have seen the same directory unquoted, @@ -1361,7 +1361,9 @@ _git_checkout () esac } -__git_cherry_pick_inprogress_options="--continue --quit --abort" +__git_sequencer_inprogress_options="--continue --quit --abort --skip" + +__git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options _git_cherry_pick () { @@ -1399,7 +1401,18 @@ _git_clean () _git_clone () { + case "$prev" in + -c|--config) + __git_complete_config_variable_name_and_value + return + ;; + esac case "$cur" in + --config=*) + __git_complete_config_variable_name_and_value \ + --cur="${cur##--config=}" + return + ;; --*) __gitcomp_builtin clone return @@ -2030,7 +2043,7 @@ _git_rebase () --autosquash --no-autosquash --fork-point --no-fork-point --autostash --no-autostash - --verify --no-verify + --verify --no-verify --keep-base --keep-empty --root --force-rebase --no-ff --rerere-autoupdate --exec @@ -2225,181 +2238,282 @@ __git_config_vars= __git_compute_config_vars () { test -n "$__git_config_vars" || - __git_config_vars="$(git help --config-for-completion | sort | uniq)" + __git_config_vars="$(git help --config-for-completion | sort -u)" } -_git_config () +# Completes possible values of various configuration variables. +# +# Usage: __git_complete_config_variable_value [<option>]... +# --varname=<word>: The name of the configuration variable whose value is +# to be completed. Defaults to the previous word on the +# command line. +# --cur=<word>: The current value to be completed. Defaults to the current +# word to be completed. +__git_complete_config_variable_value () { - local varname + local varname="$prev" cur_="$cur" + + while test $# != 0; do + case "$1" in + --varname=*) varname="${1##--varname=}" ;; + --cur=*) cur_="${1##--cur=}" ;; + *) return 1 ;; + esac + shift + done if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then - varname="${prev,,}" + varname="${varname,,}" else - varname="$(echo "$prev" |tr A-Z a-z)" + varname="$(echo "$varname" |tr A-Z a-z)" fi case "$varname" in branch.*.remote|branch.*.pushremote) - __gitcomp_nl "$(__git_remotes)" + __gitcomp_nl "$(__git_remotes)" "" "$cur_" return ;; branch.*.merge) - __git_complete_refs + __git_complete_refs --cur="$cur_" return ;; branch.*.rebase) - __gitcomp "false true merges preserve interactive" + __gitcomp "false true merges preserve interactive" "" "$cur_" return ;; remote.pushdefault) - __gitcomp_nl "$(__git_remotes)" + __gitcomp_nl "$(__git_remotes)" "" "$cur_" return ;; remote.*.fetch) - local remote="${prev#remote.}" + local remote="${varname#remote.}" remote="${remote%.fetch}" - if [ -z "$cur" ]; then + if [ -z "$cur_" ]; then __gitcomp_nl "refs/heads/" "" "" "" return fi - __gitcomp_nl "$(__git_refs_remotes "$remote")" + __gitcomp_nl "$(__git_refs_remotes "$remote")" "" "$cur_" return ;; remote.*.push) - local remote="${prev#remote.}" + local remote="${varname#remote.}" remote="${remote%.push}" __gitcomp_nl "$(__git for-each-ref \ - --format='%(refname):%(refname)' refs/heads)" + --format='%(refname):%(refname)' refs/heads)" "" "$cur_" return ;; pull.twohead|pull.octopus) __git_compute_merge_strategies - __gitcomp "$__git_merge_strategies" - return - ;; - color.branch|color.diff|color.interactive|\ - color.showbranch|color.status|color.ui) - __gitcomp "always never auto" + __gitcomp "$__git_merge_strategies" "" "$cur_" return ;; color.pager) - __gitcomp "false true" + __gitcomp "false true" "" "$cur_" return ;; color.*.*) __gitcomp " normal black red green yellow blue magenta cyan white bold dim ul blink reverse - " + " "" "$cur_" + return + ;; + color.*) + __gitcomp "false true always never auto" "" "$cur_" return ;; diff.submodule) - __gitcomp "$__git_diff_submodule_formats" + __gitcomp "$__git_diff_submodule_formats" "" "$cur_" return ;; help.format) - __gitcomp "man info web html" + __gitcomp "man info web html" "" "$cur_" return ;; log.date) - __gitcomp "$__git_log_date_formats" + __gitcomp "$__git_log_date_formats" "" "$cur_" return ;; sendemail.aliasfiletype) - __gitcomp "mutt mailrc pine elm gnus" + __gitcomp "mutt mailrc pine elm gnus" "" "$cur_" return ;; sendemail.confirm) - __gitcomp "$__git_send_email_confirm_options" + __gitcomp "$__git_send_email_confirm_options" "" "$cur_" return ;; sendemail.suppresscc) - __gitcomp "$__git_send_email_suppresscc_options" + __gitcomp "$__git_send_email_suppresscc_options" "" "$cur_" return ;; sendemail.transferencoding) - __gitcomp "7bit 8bit quoted-printable base64" - return - ;; - --get|--get-all|--unset|--unset-all) - __gitcomp_nl "$(__git_config_get_set_variables)" + __gitcomp "7bit 8bit quoted-printable base64" "" "$cur_" return ;; *.*) return ;; esac - case "$cur" in - --*) - __gitcomp_builtin config - return - ;; +} + +# Completes configuration sections, subsections, variable names. +# +# Usage: __git_complete_config_variable_name [<option>]... +# --cur=<word>: The current configuration section/variable name to be +# completed. Defaults to the current word to be completed. +# --sfx=<suffix>: A suffix to be appended to each fully completed +# configuration variable name (but not to sections or +# subsections) instead of the default space. +__git_complete_config_variable_name () +{ + local cur_="$cur" sfx + + while test $# != 0; do + case "$1" in + --cur=*) cur_="${1##--cur=}" ;; + --sfx=*) sfx="${1##--sfx=}" ;; + *) return 1 ;; + esac + shift + done + + case "$cur_" in branch.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" "$sfx" return ;; branch.*) - local pfx="${cur%.*}." cur_="${cur#*.}" + local pfx="${cur%.*}." + cur_="${cur#*.}" __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")" - __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" + __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "$sfx" return ;; guitool.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" __gitcomp " argPrompt cmd confirm needsFile noConsole noRescan prompt revPrompt revUnmerged title - " "$pfx" "$cur_" + " "$pfx" "$cur_" "$sfx" return ;; difftool.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "cmd path" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "cmd path" "$pfx" "$cur_" "$sfx" return ;; man.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "cmd path" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "cmd path" "$pfx" "$cur_" "$sfx" return ;; mergetool.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" "$sfx" return ;; pager.*) - local pfx="${cur%.*}." cur_="${cur#*.}" + local pfx="${cur_%.*}." + cur_="${cur_#*.}" __git_compute_all_commands - __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" + __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "$sfx" return ;; remote.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" __gitcomp " url proxy fetch push mirror skipDefaultUpdate receivepack uploadpack tagOpt pushurl - " "$pfx" "$cur_" + " "$pfx" "$cur_" "$sfx" return ;; remote.*) - local pfx="${cur%.*}." cur_="${cur#*.}" + local pfx="${cur_%.*}." + cur_="${cur_#*.}" __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "." - __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" + __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "$sfx" return ;; url.*.*) - local pfx="${cur%.*}." cur_="${cur##*.}" - __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" + local pfx="${cur_%.*}." + cur_="${cur_##*.}" + __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx" return ;; *.*) __git_compute_config_vars - __gitcomp "$__git_config_vars" + __gitcomp "$__git_config_vars" "" "$cur_" "$sfx" ;; *) __git_compute_config_vars - __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')" + __gitcomp "$(echo "$__git_config_vars" | + awk -F . '{ + sections[$1] = 1 + } + END { + for (s in sections) + print s "." + } + ')" "" "$cur_" + ;; + esac +} + +# Completes '='-separated configuration sections/variable names and values +# for 'git -c section.name=value'. +# +# Usage: __git_complete_config_variable_name_and_value [<option>]... +# --cur=<word>: The current configuration section/variable name/value to be +# completed. Defaults to the current word to be completed. +__git_complete_config_variable_name_and_value () +{ + local cur_="$cur" + + while test $# != 0; do + case "$1" in + --cur=*) cur_="${1##--cur=}" ;; + *) return 1 ;; + esac + shift + done + + case "$cur_" in + *=*) + __git_complete_config_variable_value \ + --varname="${cur_%%=*}" --cur="${cur_#*=}" + ;; + *) + __git_complete_config_variable_name --cur="$cur_" --sfx='=' + ;; + esac +} + +_git_config () +{ + case "$prev" in + --get|--get-all|--unset|--unset-all) + __gitcomp_nl "$(__git_config_get_set_variables)" + return + ;; + *.*) + __git_complete_config_variable_value + return + ;; + esac + case "$cur" in + --*) + __gitcomp_builtin config + ;; + *) + __git_complete_config_variable_name + ;; esac } @@ -2512,7 +2626,7 @@ _git_restore () esac } -__git_revert_inprogress_options="--continue --quit --abort" +__git_revert_inprogress_options=$__git_sequencer_inprogress_options _git_revert () { @@ -2956,7 +3070,11 @@ __git_main () # Bash filename completion return ;; - -c|--namespace) + -c) + __git_complete_config_variable_name_and_value + return + ;; + --namespace) # we don't support completing these options' arguments return ;; diff --git a/contrib/svn-fe/svn-fe.txt b/contrib/svn-fe/svn-fe.txt index a3425f4770..19333fc8df 100644 --- a/contrib/svn-fe/svn-fe.txt +++ b/contrib/svn-fe/svn-fe.txt @@ -56,7 +56,7 @@ line. This line has the form `git-svn-id: URL@REVNO UUID`. The resulting repository will generally require further processing to put each project in its own repository and to separate the history -of each branch. The 'git filter-branch --subdirectory-filter' command +of each branch. The 'git filter-repo --subdirectory-filter' command may be useful for this purpose. BUGS @@ -67,5 +67,5 @@ The exit status does not reflect whether an error was detected. SEE ALSO -------- -git-svn(1), svn2git(1), svk(1), git-filter-branch(1), git-fast-import(1), +git-svn(1), svn2git(1), svk(1), git-filter-repo(1), git-fast-import(1), https://svn.apache.org/repos/asf/subversion/trunk/notes/dump-load-format.txt @@ -8,6 +8,7 @@ #include "pkt-line.h" #include "sub-process.h" #include "utf8.h" +#include "ll-merge.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -1293,10 +1294,11 @@ struct conv_attrs { const char *working_tree_encoding; /* Supported encoding or default encoding if NULL */ }; +static struct attr_check *check; + static void convert_attrs(const struct index_state *istate, struct conv_attrs *ca, const char *path) { - static struct attr_check *check; struct attr_check_item *ccheck = NULL; if (!check) { @@ -1339,6 +1341,23 @@ static void convert_attrs(const struct index_state *istate, ca->crlf_action = CRLF_AUTO_INPUT; } +void reset_parsed_attributes(void) +{ + struct convert_driver *drv, *next; + + attr_check_free(check); + check = NULL; + reset_merge_attributes(); + + for (drv = user_convert; drv; drv = next) { + next = drv->next; + free((void *)drv->name); + free(drv); + } + user_convert = NULL; + user_convert_tail = NULL; +} + int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path) { struct conv_attrs ca; @@ -94,6 +94,12 @@ void convert_to_git_filter_fd(const struct index_state *istate, int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path); +/* + * Reset the internal list of attributes used by convert_to_git and + * convert_to_working_tree. + */ +void reset_parsed_attributes(void); + /***************************************************************** * * Streaming conversion support diff --git a/credential-store.c b/credential-store.c index ac295420dd..c010497cb2 100644 --- a/credential-store.c +++ b/credential-store.c @@ -72,15 +72,16 @@ static void store_credential_file(const char *fn, struct credential *c) struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s://", c->protocol); - strbuf_addstr_urlencode(&buf, c->username, 1); + strbuf_addstr_urlencode(&buf, c->username, is_rfc3986_unreserved); strbuf_addch(&buf, ':'); - strbuf_addstr_urlencode(&buf, c->password, 1); + strbuf_addstr_urlencode(&buf, c->password, is_rfc3986_unreserved); strbuf_addch(&buf, '@'); if (c->host) - strbuf_addstr_urlencode(&buf, c->host, 1); + strbuf_addstr_urlencode(&buf, c->host, is_rfc3986_unreserved); if (c->path) { strbuf_addch(&buf, '/'); - strbuf_addstr_urlencode(&buf, c->path, 0); + strbuf_addstr_urlencode(&buf, c->path, + is_rfc3986_reserved_or_unreserved); } rewrite_credential_file(fn, c, &buf); diff --git a/diff-delta.c b/diff-delta.c index e49643353b..77fea08dfb 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -326,6 +326,8 @@ create_delta(const struct delta_index *index, const unsigned char *ref_data, *ref_top, *data, *top; unsigned char *out; + *delta_size = 0; + if (!trg_buf || !trg_size) return NULL; @@ -25,7 +25,7 @@ #include "packfile.h" #include "parse-options.h" #include "help.h" -#include "fetch-object.h" +#include "promisor-remote.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -6512,6 +6512,7 @@ static void add_if_missing(struct repository *r, const struct diff_filespec *filespec) { if (filespec && filespec->oid_valid && + !S_ISGITLINK(filespec->mode) && oid_object_info_extended(r, &filespec->oid, NULL, OBJECT_INFO_FOR_PREFETCH)) oid_array_append(to_fetch, &filespec->oid); @@ -6519,8 +6520,7 @@ static void add_if_missing(struct repository *r, void diffcore_std(struct diff_options *options) { - if (options->repo == the_repository && - repository_format_partial_clone) { + if (options->repo == the_repository && has_promisor_remote()) { /* * Prefetch the diff pairs that are about to be flushed. */ @@ -6537,8 +6537,8 @@ void diffcore_std(struct diff_options *options) /* * NEEDSWORK: Consider deduplicating the OIDs sent. */ - fetch_objects(repository_format_partial_clone, - to_fetch.oid, to_fetch.nr); + promisor_remote_get_direct(options->repo, + to_fetch.oid, to_fetch.nr); oid_array_clear(&to_fetch); } @@ -561,7 +561,7 @@ int no_wildcard(const char *string) return string[simple_length(string)] == '\0'; } -void parse_exclude_pattern(const char **pattern, +void parse_path_pattern(const char **pattern, int *patternlen, unsigned *flags, int *nowildcardlen) @@ -571,20 +571,20 @@ void parse_exclude_pattern(const char **pattern, *flags = 0; if (*p == '!') { - *flags |= EXC_FLAG_NEGATIVE; + *flags |= PATTERN_FLAG_NEGATIVE; p++; } len = strlen(p); if (len && p[len - 1] == '/') { len--; - *flags |= EXC_FLAG_MUSTBEDIR; + *flags |= PATTERN_FLAG_MUSTBEDIR; } for (i = 0; i < len; i++) { if (p[i] == '/') break; } if (i == len) - *flags |= EXC_FLAG_NODIR; + *flags |= PATTERN_FLAG_NODIR; *nowildcardlen = simple_length(p); /* * we should have excluded the trailing slash from 'p' too, @@ -594,35 +594,35 @@ void parse_exclude_pattern(const char **pattern, if (*nowildcardlen > len) *nowildcardlen = len; if (*p == '*' && no_wildcard(p + 1)) - *flags |= EXC_FLAG_ENDSWITH; + *flags |= PATTERN_FLAG_ENDSWITH; *pattern = p; *patternlen = len; } -void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *el, int srcpos) +void add_pattern(const char *string, const char *base, + int baselen, struct pattern_list *pl, int srcpos) { - struct exclude *x; + struct path_pattern *pattern; int patternlen; unsigned flags; int nowildcardlen; - parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen); - if (flags & EXC_FLAG_MUSTBEDIR) { - FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen); + parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen); + if (flags & PATTERN_FLAG_MUSTBEDIR) { + FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen); } else { - x = xmalloc(sizeof(*x)); - x->pattern = string; + pattern = xmalloc(sizeof(*pattern)); + pattern->pattern = string; } - x->patternlen = patternlen; - x->nowildcardlen = nowildcardlen; - x->base = base; - x->baselen = baselen; - x->flags = flags; - x->srcpos = srcpos; - ALLOC_GROW(el->excludes, el->nr + 1, el->alloc); - el->excludes[el->nr++] = x; - x->el = el; + pattern->patternlen = patternlen; + pattern->nowildcardlen = nowildcardlen; + pattern->base = base; + pattern->baselen = baselen; + pattern->flags = flags; + pattern->srcpos = srcpos; + ALLOC_GROW(pl->patterns, pl->nr + 1, pl->alloc); + pl->patterns[pl->nr++] = pattern; + pattern->pl = pl; } static int read_skip_worktree_file_from_index(const struct index_state *istate, @@ -643,19 +643,19 @@ static int read_skip_worktree_file_from_index(const struct index_state *istate, } /* - * Frees memory within el which was allocated for exclude patterns and - * the file buffer. Does not free el itself. + * Frees memory within pl which was allocated for exclude patterns and + * the file buffer. Does not free pl itself. */ -void clear_exclude_list(struct exclude_list *el) +void clear_pattern_list(struct pattern_list *pl) { int i; - for (i = 0; i < el->nr; i++) - free(el->excludes[i]); - free(el->excludes); - free(el->filebuf); + for (i = 0; i < pl->nr; i++) + free(pl->patterns[i]); + free(pl->patterns); + free(pl->filebuf); - memset(el, 0, sizeof(*el)); + memset(pl, 0, sizeof(*pl)); } static void trim_trailing_spaces(char *buf) @@ -762,21 +762,21 @@ static void invalidate_directory(struct untracked_cache *uc, dir->dirs[i]->recurse = 0; } -static int add_excludes_from_buffer(char *buf, size_t size, +static int add_patterns_from_buffer(char *buf, size_t size, const char *base, int baselen, - struct exclude_list *el); + struct pattern_list *pl); /* * Given a file with name "fname", read it (either from disk, or from * an index if 'istate' is non-null), parse it and store the - * exclude rules in "el". + * exclude rules in "pl". * * If "ss" is not NULL, compute SHA-1 of the exclude file and fill - * stat data from disk (only valid if add_excludes returns zero). If + * stat data from disk (only valid if add_patterns returns zero). If * ss_valid is non-zero, "ss" must contain good value as input. */ -static int add_excludes(const char *fname, const char *base, int baselen, - struct exclude_list *el, struct index_state *istate, +static int add_patterns(const char *fname, const char *base, int baselen, + struct pattern_list *pl, struct index_state *istate, struct oid_stat *oid_stat) { struct stat st; @@ -837,21 +837,21 @@ static int add_excludes(const char *fname, const char *base, int baselen, } } - add_excludes_from_buffer(buf, size, base, baselen, el); + add_patterns_from_buffer(buf, size, base, baselen, pl); return 0; } -static int add_excludes_from_buffer(char *buf, size_t size, +static int add_patterns_from_buffer(char *buf, size_t size, const char *base, int baselen, - struct exclude_list *el) + struct pattern_list *pl) { int i, lineno = 1; char *entry; - el->filebuf = buf; + pl->filebuf = buf; if (skip_utf8_bom(&buf, size)) - size -= buf - el->filebuf; + size -= buf - pl->filebuf; entry = buf; @@ -860,7 +860,7 @@ static int add_excludes_from_buffer(char *buf, size_t size, if (entry != buf + i && entry[0] != '#') { buf[i - (i && buf[i-1] == '\r')] = 0; trim_trailing_spaces(entry); - add_exclude(entry, base, baselen, el, lineno); + add_pattern(entry, base, baselen, pl, lineno); } lineno++; entry = buf + i + 1; @@ -869,17 +869,17 @@ static int add_excludes_from_buffer(char *buf, size_t size, return 0; } -int add_excludes_from_file_to_list(const char *fname, const char *base, - int baselen, struct exclude_list *el, +int add_patterns_from_file_to_list(const char *fname, const char *base, + int baselen, struct pattern_list *pl, struct index_state *istate) { - return add_excludes(fname, base, baselen, el, istate, NULL); + return add_patterns(fname, base, baselen, pl, istate, NULL); } -int add_excludes_from_blob_to_list( +int add_patterns_from_blob_to_list( struct object_id *oid, const char *base, int baselen, - struct exclude_list *el) + struct pattern_list *pl) { char *buf; size_t size; @@ -889,31 +889,31 @@ int add_excludes_from_blob_to_list( if (r != 1) return r; - add_excludes_from_buffer(buf, size, base, baselen, el); + add_patterns_from_buffer(buf, size, base, baselen, pl); return 0; } -struct exclude_list *add_exclude_list(struct dir_struct *dir, +struct pattern_list *add_pattern_list(struct dir_struct *dir, int group_type, const char *src) { - struct exclude_list *el; + struct pattern_list *pl; struct exclude_list_group *group; group = &dir->exclude_list_group[group_type]; - ALLOC_GROW(group->el, group->nr + 1, group->alloc); - el = &group->el[group->nr++]; - memset(el, 0, sizeof(*el)); - el->src = src; - return el; + ALLOC_GROW(group->pl, group->nr + 1, group->alloc); + pl = &group->pl[group->nr++]; + memset(pl, 0, sizeof(*pl)); + pl->src = src; + return pl; } /* * Used to set up core.excludesfile and .git/info/exclude lists. */ -static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname, +static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname, struct oid_stat *oid_stat) { - struct exclude_list *el; + struct pattern_list *pl; /* * catch setup_standard_excludes() that's called before * dir->untracked is assigned. That function behaves @@ -921,15 +921,15 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname, */ if (!dir->untracked) dir->unmanaged_exclude_files++; - el = add_exclude_list(dir, EXC_FILE, fname); - if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0) + pl = add_pattern_list(dir, EXC_FILE, fname); + if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0) die(_("cannot use %s as an exclude file"), fname); } -void add_excludes_from_file(struct dir_struct *dir, const char *fname) +void add_patterns_from_file(struct dir_struct *dir, const char *fname) { dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */ - add_excludes_from_file_1(dir, fname, NULL); + add_patterns_from_file_1(dir, fname, NULL); } int match_basename(const char *basename, int basenamelen, @@ -940,7 +940,7 @@ int match_basename(const char *basename, int basenamelen, if (patternlen == basenamelen && !fspathncmp(pattern, basename, basenamelen)) return 1; - } else if (flags & EXC_FLAG_ENDSWITH) { + } else if (flags & PATTERN_FLAG_ENDSWITH) { /* "*literal" matching against "fooliteral" */ if (patternlen - 1 <= basenamelen && !fspathncmp(pattern + 1, @@ -1021,85 +1021,97 @@ int match_pathname(const char *pathname, int pathlen, * any, determines the fate. Returns the exclude_list element which * matched, or NULL for undecided. */ -static struct exclude *last_exclude_matching_from_list(const char *pathname, +static struct path_pattern *last_matching_pattern_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, - struct exclude_list *el, + struct pattern_list *pl, struct index_state *istate) { - struct exclude *exc = NULL; /* undecided */ + struct path_pattern *res = NULL; /* undecided */ int i; - if (!el->nr) + if (!pl->nr) return NULL; /* undefined */ - for (i = el->nr - 1; 0 <= i; i--) { - struct exclude *x = el->excludes[i]; - const char *exclude = x->pattern; - int prefix = x->nowildcardlen; + for (i = pl->nr - 1; 0 <= i; i--) { + struct path_pattern *pattern = pl->patterns[i]; + const char *exclude = pattern->pattern; + int prefix = pattern->nowildcardlen; - if (x->flags & EXC_FLAG_MUSTBEDIR) { + if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) *dtype = get_dtype(NULL, istate, pathname, pathlen); if (*dtype != DT_DIR) continue; } - if (x->flags & EXC_FLAG_NODIR) { + if (pattern->flags & PATTERN_FLAG_NODIR) { if (match_basename(basename, pathlen - (basename - pathname), - exclude, prefix, x->patternlen, - x->flags)) { - exc = x; + exclude, prefix, pattern->patternlen, + pattern->flags)) { + res = pattern; break; } continue; } - assert(x->baselen == 0 || x->base[x->baselen - 1] == '/'); + assert(pattern->baselen == 0 || + pattern->base[pattern->baselen - 1] == '/'); if (match_pathname(pathname, pathlen, - x->base, x->baselen ? x->baselen - 1 : 0, - exclude, prefix, x->patternlen, x->flags)) { - exc = x; + pattern->base, + pattern->baselen ? pattern->baselen - 1 : 0, + exclude, prefix, pattern->patternlen, + pattern->flags)) { + res = pattern; break; } } - return exc; + return res; } /* - * Scan the list and let the last match determine the fate. - * Return 1 for exclude, 0 for include and -1 for undecided. + * Scan the list of patterns to determine if the ordered list + * of patterns matches on 'pathname'. + * + * Return 1 for a match, 0 for not matched and -1 for undecided. */ -int is_excluded_from_list(const char *pathname, - int pathlen, const char *basename, int *dtype, - struct exclude_list *el, struct index_state *istate) -{ - struct exclude *exclude; - exclude = last_exclude_matching_from_list(pathname, pathlen, basename, - dtype, el, istate); - if (exclude) - return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; - return -1; /* undecided */ +enum pattern_match_result path_matches_pattern_list( + const char *pathname, int pathlen, + const char *basename, int *dtype, + struct pattern_list *pl, + struct index_state *istate) +{ + struct path_pattern *pattern; + pattern = last_matching_pattern_from_list(pathname, pathlen, basename, + dtype, pl, istate); + if (pattern) { + if (pattern->flags & PATTERN_FLAG_NEGATIVE) + return NOT_MATCHED; + else + return MATCHED; + } + + return UNDECIDED; } -static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, - struct index_state *istate, - const char *pathname, int pathlen, const char *basename, - int *dtype_p) +static struct path_pattern *last_matching_pattern_from_lists( + struct dir_struct *dir, struct index_state *istate, + const char *pathname, int pathlen, + const char *basename, int *dtype_p) { int i, j; struct exclude_list_group *group; - struct exclude *exclude; + struct path_pattern *pattern; for (i = EXC_CMDL; i <= EXC_FILE; i++) { group = &dir->exclude_list_group[i]; for (j = group->nr - 1; j >= 0; j--) { - exclude = last_exclude_matching_from_list( + pattern = last_matching_pattern_from_list( pathname, pathlen, basename, dtype_p, - &group->el[j], istate); - if (exclude) - return exclude; + &group->pl[j], istate); + if (pattern) + return pattern; } } return NULL; @@ -1114,7 +1126,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { struct exclude_list_group *group; - struct exclude_list *el; + struct pattern_list *pl; struct exclude_stack *stk = NULL; struct untracked_cache_dir *untracked; int current; @@ -1130,17 +1142,17 @@ static void prep_exclude(struct dir_struct *dir, if (stk->baselen <= baselen && !strncmp(dir->basebuf.buf, base, stk->baselen)) break; - el = &group->el[dir->exclude_stack->exclude_ix]; + pl = &group->pl[dir->exclude_stack->exclude_ix]; dir->exclude_stack = stk->prev; - dir->exclude = NULL; - free((char *)el->src); /* see strbuf_detach() below */ - clear_exclude_list(el); + dir->pattern = NULL; + free((char *)pl->src); /* see strbuf_detach() below */ + clear_pattern_list(pl); free(stk); group->nr--; } /* Skip traversing into sub directories if the parent is excluded */ - if (dir->exclude) + if (dir->pattern) return; /* @@ -1181,7 +1193,7 @@ static void prep_exclude(struct dir_struct *dir, stk->baselen = cp - base; stk->exclude_ix = group->nr; stk->ucd = untracked; - el = add_exclude_list(dir, EXC_DIRS, NULL); + pl = add_pattern_list(dir, EXC_DIRS, NULL); strbuf_add(&dir->basebuf, base + current, stk->baselen - current); assert(stk->baselen == dir->basebuf.len); @@ -1189,15 +1201,15 @@ static void prep_exclude(struct dir_struct *dir, if (stk->baselen) { int dt = DT_DIR; dir->basebuf.buf[stk->baselen - 1] = 0; - dir->exclude = last_exclude_matching_from_lists(dir, + dir->pattern = last_matching_pattern_from_lists(dir, istate, dir->basebuf.buf, stk->baselen - 1, dir->basebuf.buf + current, &dt); dir->basebuf.buf[stk->baselen - 1] = '/'; - if (dir->exclude && - dir->exclude->flags & EXC_FLAG_NEGATIVE) - dir->exclude = NULL; - if (dir->exclude) { + if (dir->pattern && + dir->pattern->flags & PATTERN_FLAG_NEGATIVE) + dir->pattern = NULL; + if (dir->pattern) { dir->exclude_stack = stk; return; } @@ -1223,30 +1235,30 @@ static void prep_exclude(struct dir_struct *dir, /* * dir->basebuf gets reused by the traversal, but we * need fname to remain unchanged to ensure the src - * member of each struct exclude correctly + * member of each struct path_pattern correctly * back-references its source file. Other invocations - * of add_exclude_list provide stable strings, so we + * of add_pattern_list provide stable strings, so we * strbuf_detach() and free() here in the caller. */ struct strbuf sb = STRBUF_INIT; strbuf_addbuf(&sb, &dir->basebuf); strbuf_addstr(&sb, dir->exclude_per_dir); - el->src = strbuf_detach(&sb, NULL); - add_excludes(el->src, el->src, stk->baselen, el, istate, + pl->src = strbuf_detach(&sb, NULL); + add_patterns(pl->src, pl->src, stk->baselen, pl, istate, untracked ? &oid_stat : NULL); } /* * NEEDSWORK: when untracked cache is enabled, prep_exclude() * will first be called in valid_cached_dir() then maybe many - * times more in last_exclude_matching(). When the cache is - * used, last_exclude_matching() will not be called and + * times more in last_matching_pattern(). When the cache is + * used, last_matching_pattern() will not be called and * reading .gitignore content will be a waste. * * So when it's called by valid_cached_dir() and we can get * .gitignore SHA-1 from the index (i.e. .gitignore is not * modified on work tree), we could delay reading the * .gitignore content until we absolutely need it in - * last_exclude_matching(). Be careful about ignore rule + * last_matching_pattern(). Be careful about ignore rule * order, though, if you do that. */ if (untracked && @@ -1266,7 +1278,7 @@ static void prep_exclude(struct dir_struct *dir, * Returns the exclude_list element which matched, or NULL for * undecided. */ -struct exclude *last_exclude_matching(struct dir_struct *dir, +struct path_pattern *last_matching_pattern(struct dir_struct *dir, struct index_state *istate, const char *pathname, int *dtype_p) @@ -1277,10 +1289,10 @@ struct exclude *last_exclude_matching(struct dir_struct *dir, prep_exclude(dir, istate, pathname, basename-pathname); - if (dir->exclude) - return dir->exclude; + if (dir->pattern) + return dir->pattern; - return last_exclude_matching_from_lists(dir, istate, pathname, pathlen, + return last_matching_pattern_from_lists(dir, istate, pathname, pathlen, basename, dtype_p); } @@ -1292,10 +1304,10 @@ struct exclude *last_exclude_matching(struct dir_struct *dir, int is_excluded(struct dir_struct *dir, struct index_state *istate, const char *pathname, int *dtype_p) { - struct exclude *exclude = - last_exclude_matching(dir, istate, pathname, dtype_p); - if (exclude) - return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; + struct path_pattern *pattern = + last_matching_pattern(dir, istate, pathname, dtype_p); + if (pattern) + return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1; return 0; } @@ -1808,7 +1820,7 @@ static int valid_cached_dir(struct dir_struct *dir, /* * prep_exclude will be called eventually on this directory, - * but it's called much later in last_exclude_matching(). We + * but it's called much later in last_matching_pattern(). We * need it now to determine the validity of the cache for this * path. The next calls will be nearly no-op, the way * prep_exclude() is designed. @@ -2488,14 +2500,14 @@ void setup_standard_excludes(struct dir_struct *dir) if (!excludes_file) excludes_file = xdg_config_home("ignore"); if (excludes_file && !access_or_warn(excludes_file, R_OK, 0)) - add_excludes_from_file_1(dir, excludes_file, + add_patterns_from_file_1(dir, excludes_file, dir->untracked ? &dir->ss_excludes_file : NULL); /* per repository user preference */ if (startup_info->have_repository) { const char *path = git_path_info_exclude(); if (!access_or_warn(path, R_OK, 0)) - add_excludes_from_file_1(dir, path, + add_patterns_from_file_1(dir, path, dir->untracked ? &dir->ss_info_exclude : NULL); } } @@ -2527,18 +2539,18 @@ void clear_directory(struct dir_struct *dir) { int i, j; struct exclude_list_group *group; - struct exclude_list *el; + struct pattern_list *pl; struct exclude_stack *stk; for (i = EXC_CMDL; i <= EXC_FILE; i++) { group = &dir->exclude_list_group[i]; for (j = 0; j < group->nr; j++) { - el = &group->el[j]; + pl = &group->pl[j]; if (i == EXC_DIRS) - free((char *)el->src); - clear_exclude_list(el); + free((char *)pl->src); + clear_pattern_list(pl); } - free(group->el); + free(group->pl); } stk = dir->exclude_stack; @@ -11,24 +11,24 @@ struct dir_entry { char name[FLEX_ARRAY]; /* more */ }; -#define EXC_FLAG_NODIR 1 -#define EXC_FLAG_ENDSWITH 4 -#define EXC_FLAG_MUSTBEDIR 8 -#define EXC_FLAG_NEGATIVE 16 +#define PATTERN_FLAG_NODIR 1 +#define PATTERN_FLAG_ENDSWITH 4 +#define PATTERN_FLAG_MUSTBEDIR 8 +#define PATTERN_FLAG_NEGATIVE 16 -struct exclude { +struct path_pattern { /* - * This allows callers of last_exclude_matching() etc. + * This allows callers of last_matching_pattern() etc. * to determine the origin of the matching pattern. */ - struct exclude_list *el; + struct pattern_list *pl; const char *pattern; int patternlen; int nowildcardlen; const char *base; int baselen; - unsigned flags; /* EXC_FLAG_* */ + unsigned flags; /* PATTERN_FLAG_* */ /* * Counting starts from 1 for line numbers in ignore files, @@ -44,7 +44,7 @@ struct exclude { * can also be used to represent the list of --exclude values passed * via CLI args. */ -struct exclude_list { +struct pattern_list { int nr; int alloc; @@ -54,7 +54,7 @@ struct exclude_list { /* origin of list, e.g. path to filename, or descriptive string */ const char *src; - struct exclude **excludes; + struct path_pattern **patterns; }; /* @@ -72,7 +72,7 @@ struct exclude_stack { struct exclude_list_group { int nr, alloc; - struct exclude_list *el; + struct pattern_list *pl; }; struct oid_stat { @@ -191,7 +191,7 @@ struct dir_struct { * matching exclude struct if the directory is excluded. */ struct exclude_stack *exclude_stack; - struct exclude *exclude; + struct path_pattern *pattern; struct strbuf basebuf; /* Enable untracked file cache if set */ @@ -230,10 +230,23 @@ int read_directory(struct dir_struct *, struct index_state *istate, const char *path, int len, const struct pathspec *pathspec); -int is_excluded_from_list(const char *pathname, int pathlen, - const char *basename, int *dtype, - struct exclude_list *el, - struct index_state *istate); +enum pattern_match_result { + UNDECIDED = -1, + NOT_MATCHED = 0, + MATCHED = 1, +}; + +/* + * Scan the list of patterns to determine if the ordered list + * of patterns matches on 'pathname'. + * + * Return 1 for a match, 0 for not matched and -1 for undecided. + */ +enum pattern_match_result path_matches_pattern_list(const char *pathname, + int pathlen, + const char *basename, int *dtype, + struct pattern_list *pl, + struct index_state *istate); struct dir_entry *dir_add_ignored(struct dir_struct *dir, struct index_state *istate, const char *pathname, int len); @@ -248,26 +261,26 @@ int match_pathname(const char *, int, const char *, int, const char *, int, int, unsigned); -struct exclude *last_exclude_matching(struct dir_struct *dir, - struct index_state *istate, - const char *name, int *dtype); +struct path_pattern *last_matching_pattern(struct dir_struct *dir, + struct index_state *istate, + const char *name, int *dtype); int is_excluded(struct dir_struct *dir, struct index_state *istate, const char *name, int *dtype); -struct exclude_list *add_exclude_list(struct dir_struct *dir, +struct pattern_list *add_pattern_list(struct dir_struct *dir, int group_type, const char *src); -int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, - struct exclude_list *el, struct index_state *istate); -void add_excludes_from_file(struct dir_struct *, const char *fname); -int add_excludes_from_blob_to_list(struct object_id *oid, +int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen, + struct pattern_list *pl, struct index_state *istate); +void add_patterns_from_file(struct dir_struct *, const char *fname); +int add_patterns_from_blob_to_list(struct object_id *oid, const char *base, int baselen, - struct exclude_list *el); -void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen); -void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *el, int srcpos); -void clear_exclude_list(struct exclude_list *el); + struct pattern_list *pl); +void parse_path_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen); +void add_pattern(const char *string, const char *base, + int baselen, struct pattern_list *pl, int srcpos); +void clear_pattern_list(struct pattern_list *pl); void clear_directory(struct dir_struct *dir); int repo_file_exists(struct repository *repo, const char *path); diff --git a/environment.c b/environment.c index 89af47cb85..efa072680a 100644 --- a/environment.c +++ b/environment.c @@ -31,8 +31,6 @@ int warn_ambiguous_refs = 1; int warn_on_object_refname_ambiguity = 1; int ref_paranoia = -1; int repository_format_precious_objects; -char *repository_format_partial_clone; -const char *core_partial_clone_filter_default; int repository_format_worktree_config; const char *git_commit_encoding; const char *git_log_output_encoding; diff --git a/fast-import.c b/fast-import.c index b44d6a467e..1f9160b645 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1763,7 +1763,6 @@ static int read_next_command(void) } else { struct recent_command *rc; - strbuf_detach(&command_buf, NULL); stdin_eof = strbuf_getline_lf(&command_buf, stdin); if (stdin_eof) return EOF; @@ -1784,7 +1783,7 @@ static int read_next_command(void) free(rc->buf); } - rc->buf = command_buf.buf; + rc->buf = xstrdup(command_buf.buf); rc->prev = cmd_tail; rc->next = cmd_hist.prev; rc->prev->next = rc; @@ -1833,7 +1832,6 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res) char *term = xstrdup(data); size_t term_len = command_buf.len - (data - command_buf.buf); - strbuf_detach(&command_buf, NULL); for (;;) { if (strbuf_getline_lf(&command_buf, stdin) == EOF) die("EOF in data (terminator '%s' not found)", term); @@ -2588,7 +2586,7 @@ static void parse_new_commit(const char *arg) struct branch *b; char *author = NULL; char *committer = NULL; - const char *encoding = NULL; + char *encoding = NULL; struct hash_list *merge_list = NULL; unsigned int merge_count; unsigned char prev_fanout, new_fanout; @@ -2611,8 +2609,10 @@ static void parse_new_commit(const char *arg) } if (!committer) die("Expected committer but didn't get one"); - if (skip_prefix(command_buf.buf, "encoding ", &encoding)) + if (skip_prefix(command_buf.buf, "encoding ", &v)) { + encoding = xstrdup(v); read_next_command(); + } parse_data(&msg, 0, NULL); read_next_command(); parse_from(b); @@ -2686,6 +2686,7 @@ static void parse_new_commit(const char *arg) strbuf_addbuf(&new_data, &msg); free(author); free(committer); + free(encoding); if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark)) b->pack_id = pack_id; diff --git a/fetch-negotiator.c b/fetch-negotiator.c index d6d685cba0..0a1357dc9d 100644 --- a/fetch-negotiator.c +++ b/fetch-negotiator.c @@ -2,19 +2,20 @@ #include "fetch-negotiator.h" #include "negotiator/default.h" #include "negotiator/skipping.h" +#include "repository.h" -void fetch_negotiator_init(struct fetch_negotiator *negotiator, - const char *algorithm) +void fetch_negotiator_init(struct repository *r, + struct fetch_negotiator *negotiator) { - if (algorithm) { - if (!strcmp(algorithm, "skipping")) { - skipping_negotiator_init(negotiator); - return; - } else if (!strcmp(algorithm, "default")) { - /* Fall through to default initialization */ - } else { - die("unknown fetch negotiation algorithm '%s'", algorithm); - } + prepare_repo_settings(r); + switch(r->settings.fetch_negotiation_algorithm) { + case FETCH_NEGOTIATION_SKIPPING: + skipping_negotiator_init(negotiator); + return; + + case FETCH_NEGOTIATION_DEFAULT: + default: + default_negotiator_init(negotiator); + return; } - default_negotiator_init(negotiator); } diff --git a/fetch-negotiator.h b/fetch-negotiator.h index 9e3967ce66..ea78868504 100644 --- a/fetch-negotiator.h +++ b/fetch-negotiator.h @@ -2,6 +2,7 @@ #define FETCH_NEGOTIATOR_H struct commit; +struct repository; /* * An object that supplies the information needed to negotiate the contents of @@ -52,7 +53,7 @@ struct fetch_negotiator { void *data; }; -void fetch_negotiator_init(struct fetch_negotiator *negotiator, - const char *algorithm); +void fetch_negotiator_init(struct repository *r, + struct fetch_negotiator *negotiator); #endif diff --git a/fetch-object.c b/fetch-object.c deleted file mode 100644 index 4266548800..0000000000 --- a/fetch-object.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "cache.h" -#include "packfile.h" -#include "pkt-line.h" -#include "strbuf.h" -#include "transport.h" -#include "fetch-object.h" - -static void fetch_refs(const char *remote_name, struct ref *ref) -{ - struct remote *remote; - struct transport *transport; - int original_fetch_if_missing = fetch_if_missing; - - fetch_if_missing = 0; - remote = remote_get(remote_name); - if (!remote->url[0]) - die(_("Remote with no URL")); - transport = transport_get(remote, remote->url[0]); - - transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); - transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1"); - transport_fetch_refs(transport, ref); - fetch_if_missing = original_fetch_if_missing; -} - -void fetch_objects(const char *remote_name, const struct object_id *oids, - int oid_nr) -{ - struct ref *ref = NULL; - int i; - - for (i = 0; i < oid_nr; i++) { - struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i])); - oidcpy(&new_ref->old_oid, &oids[i]); - new_ref->exact_oid = 1; - new_ref->next = ref; - ref = new_ref; - } - fetch_refs(remote_name, ref); -} diff --git a/fetch-object.h b/fetch-object.h deleted file mode 100644 index d6444caa5a..0000000000 --- a/fetch-object.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef FETCH_OBJECT_H -#define FETCH_OBJECT_H - -struct object_id; - -void fetch_objects(const char *remote_name, const struct object_id *oids, - int oid_nr); - -#endif diff --git a/fetch-pack.c b/fetch-pack.c index 65be043f2a..6ccc6294ea 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -36,7 +36,6 @@ static int agent_supported; static int server_supports_filtering; static struct lock_file shallow_lock; static const char *alternate_shallow_file; -static char *negotiation_algorithm; static struct strbuf fsck_msg_types = STRBUF_INIT; /* Remember to update object flag allocation in object.h */ @@ -339,12 +338,9 @@ static int find_common(struct fetch_negotiator *negotiator, } } if (server_supports_filtering && args->filter_options.choice) { - struct strbuf expanded_filter_spec = STRBUF_INIT; - expand_list_objects_filter_spec(&args->filter_options, - &expanded_filter_spec); - packet_buf_write(&req_buf, "filter %s", - expanded_filter_spec.buf); - strbuf_release(&expanded_filter_spec); + const char *spec = + expand_list_objects_filter_spec(&args->filter_options); + packet_buf_write(&req_buf, "filter %s", spec); } packet_buf_flush(&req_buf); state_len = req_buf.len; @@ -892,12 +888,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository; struct ref *ref = copy_ref_list(orig_ref); struct object_id oid; const char *agent_feature; int agent_len; struct fetch_negotiator negotiator; - fetch_negotiator_init(&negotiator, negotiation_algorithm); + fetch_negotiator_init(r, &negotiator); sort_ref_list(&ref, ref_compare_name); QSORT(sought, nr_sought, cmp_ref_by_name); @@ -911,7 +908,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (server_supports("shallow")) print_verbose(args, _("Server supports %s"), "shallow"); - else if (args->depth > 0 || is_repository_shallow(the_repository)) + else if (args->depth > 0 || is_repository_shallow(r)) die(_("Server does not support shallow clients")); if (args->depth > 0 || args->deepen_since || args->deepen_not) args->deepen = 1; @@ -1112,7 +1109,7 @@ static int add_haves(struct fetch_negotiator *negotiator, } static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, - const struct fetch_pack_args *args, + struct fetch_pack_args *args, const struct ref *wants, struct oidset *common, int *haves_to_send, int *in_vain, int sideband_all) @@ -1153,13 +1150,10 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, /* Add filter */ if (server_supports_feature("fetch", "filter", 0) && args->filter_options.choice) { - struct strbuf expanded_filter_spec = STRBUF_INIT; + const char *spec = + expand_list_objects_filter_spec(&args->filter_options); print_verbose(args, _("Server supports filter")); - expand_list_objects_filter_spec(&args->filter_options, - &expanded_filter_spec); - packet_buf_write(&req_buf, "filter %s", - expanded_filter_spec.buf); - strbuf_release(&expanded_filter_spec); + packet_buf_write(&req_buf, "filter %s", spec); } else if (args->filter_options.choice) { warning("filtering not recognized by server, ignoring"); } @@ -1379,6 +1373,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository; struct ref *ref = copy_ref_list(orig_ref); enum fetch_state state = FETCH_CHECK_LOCAL; struct oidset common = OIDSET_INIT; @@ -1386,7 +1381,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, int in_vain = 0; int haves_to_send = INITIAL_FLUSH; struct fetch_negotiator negotiator; - fetch_negotiator_init(&negotiator, negotiation_algorithm); + fetch_negotiator_init(r, &negotiator); packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE | PACKET_READ_DIE_ON_ERR_PACKET); @@ -1505,8 +1500,6 @@ static void fetch_pack_config(void) git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta); git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects); git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects); - git_config_get_string("fetch.negotiationalgorithm", - &negotiation_algorithm); git_config(fetch_pack_config_cb, NULL); } diff --git a/git-compat-util.h b/git-compat-util.h index 83be89de0a..f0d13e4e28 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -818,9 +818,6 @@ const char *inet_ntop(int af, const void *src, char *dst, size_t size); int git_atexit(void (*handler)(void)); #endif -typedef void (*try_to_free_t)(size_t); -try_to_free_t set_try_to_free_routine(try_to_free_t); - static inline size_t st_add(size_t a, size_t b) { if (unsigned_add_overflows(a, b)) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 5c5afa2b98..fea7964617 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -83,6 +83,20 @@ set_ident () { finish_ident COMMITTER } +if test -z "$FILTER_BRANCH_SQUELCH_WARNING$GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS" +then + cat <<EOF +WARNING: git-filter-branch has a glut of gotchas generating mangled history + rewrites. Hit Ctrl-C before proceeding to abort, then use an + alternative filtering tool such as 'git filter-repo' + (https://github.com/newren/git-filter-repo/) instead. See the + filter-branch manual page for more details; to squelch this warning, + set FILTER_BRANCH_SQUELCH_WARNING=1. +EOF + sleep 10 + printf "Proceeding with filter-branch...\n\n" +fi + USAGE="[--setup <command>] [--subdirectory-filter <directory>] [--env-filter <command>] [--tree-filter <command>] [--index-filter <command>] [--parent-filter <command>] [--msg-filter <command>] diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 6de74ce639..fd476b6999 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -1340,6 +1340,7 @@ set HEAD {} set PARENT {} set MERGE_HEAD [list] set commit_type {} +set commit_type_is_amend 0 set empty_tree {} set current_branch {} set is_detached 0 @@ -1347,8 +1348,9 @@ set current_diff_path {} set is_3way_diff 0 set is_submodule_diff 0 set is_conflict_diff 0 -set selected_commit_type new set diff_empty_count 0 +set last_revert {} +set last_revert_enc {} set nullid "0000000000000000000000000000000000000000" set nullid2 "0000000000000000000000000000000000000001" @@ -1434,7 +1436,7 @@ proc PARENT {} { } proc force_amend {} { - global selected_commit_type + global commit_type_is_amend global HEAD PARENT MERGE_HEAD commit_type repository_state newType newHEAD newMERGE_HEAD @@ -1443,7 +1445,7 @@ proc force_amend {} { set MERGE_HEAD $newMERGE_HEAD set commit_type $newType - set selected_commit_type amend + set commit_type_is_amend 1 do_select_commit_type } @@ -2494,7 +2496,7 @@ proc force_first_diff {after} { proc toggle_or_diff {mode w args} { global file_states file_lists current_diff_path ui_index ui_workdir - global last_clicked selected_paths + global last_clicked selected_paths file_lists_last_clicked if {$mode eq "click"} { foreach {x y} $args break @@ -2551,6 +2553,8 @@ proc toggle_or_diff {mode w args} { $ui_index tag remove in_sel 0.0 end $ui_workdir tag remove in_sel 0.0 end + set file_lists_last_clicked($w) $path + # Determine the state of the file if {[info exists file_states($path)]} { set state [lindex $file_states($path) 0] @@ -2664,6 +2668,32 @@ proc show_less_context {} { } } +proc focus_widget {widget} { + global file_lists last_clicked selected_paths + global file_lists_last_clicked + + if {[llength $file_lists($widget)] > 0} { + set path $file_lists_last_clicked($widget) + set index [lsearch -sorted -exact $file_lists($widget) $path] + if {$index < 0} { + set index 0 + set path [lindex $file_lists($widget) $index] + } + + focus $widget + set last_clicked [list $widget [expr $index + 1]] + array unset selected_paths + set selected_paths($path) 1 + show_diff $path $widget + } +} + +proc toggle_commit_type {} { + global commit_type_is_amend + set commit_type_is_amend [expr !$commit_type_is_amend] + do_select_commit_type +} + ###################################################################### ## ## ui construction @@ -2852,19 +2882,11 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { menu .mbar.commit if {![is_enabled nocommit]} { - .mbar.commit add radiobutton \ - -label [mc "New Commit"] \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value new - lappend disable_on_lock \ - [list .mbar.commit entryconf [.mbar.commit index last] -state] - - .mbar.commit add radiobutton \ + .mbar.commit add checkbutton \ -label [mc "Amend Last Commit"] \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value amend + -accelerator $M1T-E \ + -variable commit_type_is_amend \ + -command do_select_commit_type lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] @@ -3030,8 +3052,23 @@ unset doc_path doc_url wm protocol . WM_DELETE_WINDOW do_quit bind all <$M1B-Key-q> do_quit bind all <$M1B-Key-Q> do_quit -bind all <$M1B-Key-w> {destroy [winfo toplevel %W]} -bind all <$M1B-Key-W> {destroy [winfo toplevel %W]} + +set m1b_w_script { + set toplvl_win [winfo toplevel %W] + + # If we are destroying the main window, we should call do_quit to take + # care of cleanup before exiting the program. + if {$toplvl_win eq "."} { + do_quit + } else { + destroy $toplvl_win + } +} + +bind all <$M1B-Key-w> $m1b_w_script +bind all <$M1B-Key-W> $m1b_w_script + +unset m1b_w_script set subcommand_args {} proc usage {} { @@ -3337,18 +3374,10 @@ set ui_comm .vpane.lower.commarea.buffer.frame.t set ui_coml .vpane.lower.commarea.buffer.header.l if {![is_enabled nocommit]} { - ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \ - -text [mc "New Commit"] \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value new - lappend disable_on_lock \ - [list .vpane.lower.commarea.buffer.header.new conf -state] - ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \ + ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \ -text [mc "Amend Last Commit"] \ - -command do_select_commit_type \ - -variable selected_commit_type \ - -value amend + -variable commit_type_is_amend \ + -command do_select_commit_type lappend disable_on_lock \ [list .vpane.lower.commarea.buffer.header.amend conf -state] } @@ -3373,7 +3402,6 @@ pack $ui_coml -side left -fill x if {![is_enabled nocommit]} { pack .vpane.lower.commarea.buffer.header.amend -side right - pack .vpane.lower.commarea.buffer.header.new -side right } textframe .vpane.lower.commarea.buffer.frame @@ -3387,10 +3415,16 @@ ttext $ui_comm -background white -foreground black \ -relief sunken \ -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \ -font font_diff \ + -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx set} \ -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set} +${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \ + -orient horizontal \ + -command [list $ui_comm xview] ${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \ + -orient vertical \ -command [list $ui_comm yview] +pack .vpane.lower.commarea.buffer.frame.sbx -side bottom -fill x pack .vpane.lower.commarea.buffer.frame.sby -side right -fill y pack $ui_comm -side left -fill y pack .vpane.lower.commarea.buffer.header -side top -fill x @@ -3606,16 +3640,32 @@ set ctxm .vpane.lower.diff.body.ctxm menu $ctxm -tearoff 0 $ctxm add command \ -label [mc "Apply/Reverse Hunk"] \ - -command {apply_hunk $cursorX $cursorY} + -command {apply_or_revert_hunk $cursorX $cursorY 0} set ui_diff_applyhunk [$ctxm index last] lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state] $ctxm add command \ -label [mc "Apply/Reverse Line"] \ - -command {apply_range_or_line $cursorX $cursorY; do_rescan} + -command {apply_or_revert_range_or_line $cursorX $cursorY 0; do_rescan} set ui_diff_applyline [$ctxm index last] lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state] $ctxm add separator $ctxm add command \ + -label [mc "Revert Hunk"] \ + -command {apply_or_revert_hunk $cursorX $cursorY 1} +set ui_diff_reverthunk [$ctxm index last] +lappend diff_actions [list $ctxm entryconf $ui_diff_reverthunk -state] +$ctxm add command \ + -label [mc "Revert Line"] \ + -command {apply_or_revert_range_or_line $cursorX $cursorY 1; do_rescan} +set ui_diff_revertline [$ctxm index last] +lappend diff_actions [list $ctxm entryconf $ui_diff_revertline -state] +$ctxm add command \ + -label [mc "Undo Last Revert"] \ + -command {undo_last_revert; do_rescan} +set ui_diff_undorevert [$ctxm index last] +lappend diff_actions [list $ctxm entryconf $ui_diff_undorevert -state] +$ctxm add separator +$ctxm add command \ -label [mc "Show Less Context"] \ -command show_less_context lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] @@ -3693,7 +3743,7 @@ proc has_textconv {path} { } proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} { - global current_diff_path file_states + global current_diff_path file_states last_revert set ::cursorX $x set ::cursorY $y if {[info exists file_states($current_diff_path)]} { @@ -3707,19 +3757,28 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} { tk_popup $ctxmsm $X $Y } else { set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}] + set u [mc "Undo Last Revert"] if {$::ui_index eq $::current_diff_side} { set l [mc "Unstage Hunk From Commit"] + set h [mc "Revert Hunk"] + if {$has_range} { set t [mc "Unstage Lines From Commit"] + set r [mc "Revert Lines"] } else { set t [mc "Unstage Line From Commit"] + set r [mc "Revert Line"] } } else { set l [mc "Stage Hunk For Commit"] + set h [mc "Revert Hunk"] + if {$has_range} { set t [mc "Stage Lines For Commit"] + set r [mc "Revert Lines"] } else { set t [mc "Stage Line For Commit"] + set r [mc "Revert Line"] } } if {$::is_3way_diff @@ -3730,11 +3789,35 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} { || [string match {T?} $state] || [has_textconv $current_diff_path]} { set s disabled + set revert_state disabled } else { set s normal + + # Only allow reverting changes in the working tree. If + # the user wants to revert changes in the index, they + # need to unstage those first. + if {$::ui_workdir eq $::current_diff_side} { + set revert_state normal + } else { + set revert_state disabled + } + } + + if {$last_revert eq {}} { + set undo_state disabled + } else { + set undo_state normal } + $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l $ctxm entryconf $::ui_diff_applyline -state $s -label $t + $ctxm entryconf $::ui_diff_revertline -state $revert_state \ + -label $r + $ctxm entryconf $::ui_diff_reverthunk -state $revert_state \ + -label $h + $ctxm entryconf $::ui_diff_undorevert -state $undo_state \ + -label $u + tk_popup $ctxm $X $Y } } @@ -3861,6 +3944,8 @@ bind . <$M1B-Key-j> do_revert_selection bind . <$M1B-Key-J> do_revert_selection bind . <$M1B-Key-i> do_add_all bind . <$M1B-Key-I> do_add_all +bind . <$M1B-Key-e> toggle_commit_type +bind . <$M1B-Key-E> toggle_commit_type bind . <$M1B-Key-minus> {show_less_context;break} bind . <$M1B-Key-KP_Subtract> {show_less_context;break} bind . <$M1B-Key-equal> {show_more_context;break} @@ -3877,6 +3962,14 @@ foreach i [list $ui_index $ui_workdir] { } unset i +bind . <Alt-Key-1> {focus_widget $::ui_workdir} +bind . <Alt-Key-2> {focus_widget $::ui_index} +bind . <Alt-Key-3> {focus $::ui_diff} +bind . <Alt-Key-4> {focus $::ui_comm} + +set file_lists_last_clicked($ui_index) {} +set file_lists_last_clicked($ui_workdir) {} + set file_lists($ui_index) [list] set file_lists($ui_workdir) [list] diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl index 9e7412c446..a5228297db 100644 --- a/git-gui/lib/checkout_op.tcl +++ b/git-gui/lib/checkout_op.tcl @@ -389,7 +389,7 @@ $err } method _after_readtree {} { - global selected_commit_type commit_type HEAD MERGE_HEAD PARENT + global commit_type HEAD MERGE_HEAD PARENT global current_branch is_detached global ui_comm @@ -490,12 +490,12 @@ method _update_repo_state {} { # amend mode our file lists are accurate and we can avoid # the rescan. # - global selected_commit_type commit_type HEAD MERGE_HEAD PARENT + global commit_type_is_amend commit_type HEAD MERGE_HEAD PARENT global ui_comm unlock_index set name [_name $this] - set selected_commit_type new + set commit_type_is_amend 0 if {[string match amend* $commit_type]} { $ui_comm delete 0.0 end $ui_comm edit reset diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 75ea965dac..b516aa2990 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -333,7 +333,7 @@ proc commit_writetree {curHEAD msg_p} { proc commit_committree {fd_wt curHEAD msg_p} { global HEAD PARENT MERGE_HEAD commit_type commit_author global current_branch - global ui_comm selected_commit_type + global ui_comm commit_type_is_amend global file_states selected_paths rescan_active global repo_config global env @@ -467,8 +467,8 @@ A rescan will be automatically started now. # -- Update in memory status # - set selected_commit_type new set commit_type normal + set commit_type_is_amend 0 set HEAD $cmt_id set PARENT $cmt_id set MERGE_HEAD [list] diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl index 68c4a6c736..958a0fa219 100644 --- a/git-gui/lib/diff.tcl +++ b/git-gui/lib/diff.tcl @@ -55,7 +55,7 @@ proc reshow_diff {{after {}}} { proc force_diff_encoding {enc} { global current_diff_path - + if {$current_diff_path ne {}} { force_path_encoding $current_diff_path $enc reshow_diff @@ -567,24 +567,31 @@ proc read_diff {fd conflict_size cont_info} { } } -proc apply_hunk {x y} { +proc apply_or_revert_hunk {x y revert} { global current_diff_path current_diff_header current_diff_side - global ui_diff ui_index file_states + global ui_diff ui_index file_states last_revert last_revert_enc if {$current_diff_path eq {} || $current_diff_header eq {}} return if {![lock_index apply_hunk]} return - set apply_cmd {apply --cached --whitespace=nowarn} + set apply_cmd {apply --whitespace=nowarn} set mi [lindex $file_states($current_diff_path) 0] if {$current_diff_side eq $ui_index} { set failed_msg [mc "Failed to unstage selected hunk."] - lappend apply_cmd --reverse + lappend apply_cmd --reverse --cached if {[string index $mi 0] ne {M}} { unlock_index return } } else { - set failed_msg [mc "Failed to stage selected hunk."] + if {$revert} { + set failed_msg [mc "Failed to revert selected hunk."] + lappend apply_cmd --reverse + } else { + set failed_msg [mc "Failed to stage selected hunk."] + lappend apply_cmd --cached + } + if {[string index $mi 1] ne {M}} { unlock_index return @@ -603,29 +610,40 @@ proc apply_hunk {x y} { set e_lno end } + set wholepatch "$current_diff_header[$ui_diff get $s_lno $e_lno]" + if {[catch { set enc [get_path_encoding $current_diff_path] set p [eval git_write $apply_cmd] fconfigure $p -translation binary -encoding $enc - puts -nonewline $p $current_diff_header - puts -nonewline $p [$ui_diff get $s_lno $e_lno] + puts -nonewline $p $wholepatch close $p} err]} { error_popup "$failed_msg\n\n$err" unlock_index return } + if {$revert} { + # Save a copy of this patch for undoing reverts. + set last_revert $wholepatch + set last_revert_enc $enc + } + $ui_diff conf -state normal $ui_diff delete $s_lno $e_lno $ui_diff conf -state disabled + # Check if the hunk was the last one in the file. if {[$ui_diff get 1.0 end] eq "\n"} { set o _ } else { set o ? } - if {$current_diff_side eq $ui_index} { + # Update the status flags. + if {$revert} { + set mi [string index $mi 0]$o + } elseif {$current_diff_side eq $ui_index} { set mi ${o}M } elseif {[string index $mi 0] eq {_}} { set mi M$o @@ -640,9 +658,9 @@ proc apply_hunk {x y} { } } -proc apply_range_or_line {x y} { +proc apply_or_revert_range_or_line {x y revert} { global current_diff_path current_diff_header current_diff_side - global ui_diff ui_index file_states + global ui_diff ui_index file_states last_revert set selected [$ui_diff tag nextrange sel 0.0] @@ -660,19 +678,27 @@ proc apply_range_or_line {x y} { if {$current_diff_path eq {} || $current_diff_header eq {}} return if {![lock_index apply_hunk]} return - set apply_cmd {apply --cached --whitespace=nowarn} + set apply_cmd {apply --whitespace=nowarn} set mi [lindex $file_states($current_diff_path) 0] if {$current_diff_side eq $ui_index} { set failed_msg [mc "Failed to unstage selected line."] set to_context {+} - lappend apply_cmd --reverse + lappend apply_cmd --reverse --cached if {[string index $mi 0] ne {M}} { unlock_index return } } else { - set failed_msg [mc "Failed to stage selected line."] - set to_context {-} + if {$revert} { + set failed_msg [mc "Failed to revert selected line."] + set to_context {+} + lappend apply_cmd --reverse + } else { + set failed_msg [mc "Failed to stage selected line."] + set to_context {-} + lappend apply_cmd --cached + } + if {[string index $mi 1] ne {M}} { unlock_index return @@ -830,7 +856,47 @@ proc apply_range_or_line {x y} { puts -nonewline $p $wholepatch close $p} err]} { error_popup "$failed_msg\n\n$err" + unlock_index + return + } + + if {$revert} { + # Save a copy of this patch for undoing reverts. + set last_revert $current_diff_header$wholepatch + set last_revert_enc $enc } unlock_index } + +# Undo the last line/hunk reverted. When hunks and lines are reverted, a copy +# of the diff applied is saved. Re-apply that diff to undo the revert. +# +# Right now, we only use a single variable to hold the copy, and not a +# stack/deque for simplicity, so multiple undos are not possible. Maybe this +# can be added if the need for something like this is felt in the future. +proc undo_last_revert {} { + global last_revert current_diff_path current_diff_header + global last_revert_enc + + if {$last_revert eq {}} return + if {![lock_index apply_hunk]} return + + set apply_cmd {apply --whitespace=nowarn} + set failed_msg [mc "Failed to undo last revert."] + + if {[catch { + set enc $last_revert_enc + set p [eval git_write $apply_cmd] + fconfigure $p -translation binary -encoding $enc + puts -nonewline $p $last_revert + close $p} err]} { + error_popup "$failed_msg\n\n$err" + unlock_index + return + } + + set last_revert {} + + unlock_index +} diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index b588db11d9..e07b7a3762 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -466,19 +466,19 @@ proc do_revert_selection {} { } proc do_select_commit_type {} { - global commit_type selected_commit_type + global commit_type commit_type_is_amend - if {$selected_commit_type eq {new} + if {$commit_type_is_amend == 0 && [string match amend* $commit_type]} { create_new_commit - } elseif {$selected_commit_type eq {amend} + } elseif {$commit_type_is_amend == 1 && ![string match amend* $commit_type]} { load_last_commit # The amend request was rejected... # if {![string match amend* $commit_type]} { - set selected_commit_type new + set commit_type_is_amend 0 } } } diff --git a/gitk-git/gitk b/gitk-git/gitk index a14d7a16b2..abe4805ade 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -3404,6 +3404,8 @@ set rectmask { } image create bitmap reficon-H -background black -foreground "#00ff00" \ -data $rectdata -maskdata $rectmask +image create bitmap reficon-R -background black -foreground "#ffddaa" \ + -data $rectdata -maskdata $rectmask image create bitmap reficon-o -background black -foreground "#ddddff" \ -data $rectdata -maskdata $rectmask @@ -7016,6 +7018,7 @@ proc commit_descriptor {p} { # append some text to the ctext widget, and make any SHA1 ID # that we know about be a clickable link. +# Also look for URLs of the form "http[s]://..." and make them web links. proc appendwithlinks {text tags} { global ctext linknum curview @@ -7032,6 +7035,18 @@ proc appendwithlinks {text tags} { setlink $linkid link$linknum incr linknum } + set wlinks [regexp -indices -all -inline -line \ + {https?://[^[:space:]]+} $text] + foreach l $wlinks { + set s2 [lindex $l 0] + set e2 [lindex $l 1] + set url [string range $text $s2 $e2] + incr e2 + $ctext tag delete link$linknum + $ctext tag add link$linknum "$start + $s2 c" "$start + $e2 c" + setwlink $url link$linknum + incr linknum + } } proc setlink {id lk} { @@ -7064,6 +7079,18 @@ proc setlink {id lk} { } } +proc setwlink {url lk} { + global ctext + global linkfgcolor + global web_browser + + if {$web_browser eq {}} return + $ctext tag conf $lk -foreground $linkfgcolor -underline 1 + $ctext tag bind $lk <1> [list browseweb $url] + $ctext tag bind $lk <Enter> {linkcursor %W 1} + $ctext tag bind $lk <Leave> {linkcursor %W -1} +} + proc appendshortlink {id {pre {}} {post {}}} { global ctext linknum @@ -7098,6 +7125,16 @@ proc linkcursor {w inc} { } } +proc browseweb {url} { + global web_browser + + if {$web_browser eq {}} return + # Use eval here in case $web_browser is a command plus some arguments + if {[catch {eval exec $web_browser [list $url] &} err]} { + error_popup "[mc "Error starting web browser:"] $err" + } +} + proc viewnextline {dir} { global canv linespc @@ -8191,11 +8228,11 @@ proc parseblobdiffline {ids line} { } else { $ctext insert end "$line\n" filesep } - } elseif {![string compare -length 3 " >" $line]} { + } elseif {$currdiffsubmod != "" && ![string compare -length 3 " >" $line]} { set $currdiffsubmod "" set line [encoding convertfrom $diffencoding $line] $ctext insert end "$line\n" dresult - } elseif {![string compare -length 3 " <" $line]} { + } elseif {$currdiffsubmod != "" && ![string compare -length 3 " <" $line]} { set $currdiffsubmod "" set line [encoding convertfrom $diffencoding $line] $ctext insert end "$line\n" d0 @@ -10022,6 +10059,7 @@ proc sel_reflist {w x y} { set n [lindex $ref 0] switch -- [lindex $ref 1] { "H" {selbyid $headids($n)} + "R" {selbyid $headids($n)} "T" {selbyid $tagids($n)} "o" {selbyid $otherrefids($n)} } @@ -10051,7 +10089,11 @@ proc refill_reflist {} { foreach n [array names headids] { if {[string match $reflistfilter $n]} { if {[commitinview $headids($n) $curview]} { - lappend refs [list $n H] + if {[string match "remotes/*" $n]} { + lappend refs [list $n R] + } else { + lappend refs [list $n H] + } } else { interestedin $headids($n) {run refill_reflist} } @@ -11488,7 +11530,7 @@ proc create_prefs_page {w} { proc prefspage_general {notebook} { global NS maxwidth maxgraphpct showneartags showlocalchanges global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs - global hideremotes want_ttk have_ttk maxrefs + global hideremotes want_ttk have_ttk maxrefs web_browser set page [create_prefs_page $notebook.general] @@ -11539,6 +11581,13 @@ proc prefspage_general {notebook} { pack configure $page.extdifff.l -padx 10 grid x $page.extdifff $page.extdifft -sticky ew + ${NS}::entry $page.webbrowser -textvariable web_browser + ${NS}::frame $page.webbrowserf + ${NS}::label $page.webbrowserf.l -text [mc "Web browser" ] + pack $page.webbrowserf.l -side left + pack configure $page.webbrowserf.l -padx 10 + grid x $page.webbrowserf $page.webbrowser -sticky ew + ${NS}::label $page.lgen -text [mc "General options"] grid $page.lgen - -sticky w -pady 10 ${NS}::checkbutton $page.want_ttk -variable want_ttk \ @@ -12310,6 +12359,7 @@ if {[tk windowingsystem] eq "win32"} { set bgcolor SystemWindow set fgcolor SystemWindowText set selectbgcolor SystemHighlight + set web_browser "cmd /c start" } else { set uicolor grey85 set uifgcolor black @@ -12317,6 +12367,11 @@ if {[tk windowingsystem] eq "win32"} { set bgcolor white set fgcolor black set selectbgcolor gray85 + if {[tk windowingsystem] eq "aqua"} { + set web_browser "open" + } else { + set web_browser "xdg-open" + } } set diffcolors {red "#00a000" blue} set diffcontext 3 @@ -12390,6 +12445,7 @@ set config_variables { filesepbgcolor filesepfgcolor linehoverbgcolor linehoverfgcolor linehoveroutlinecolor mainheadcirclecolor workingfilescirclecolor indexcirclecolor circlecolors linkfgcolor circleoutlinecolor + web_browser } foreach var $config_variables { config_init_trace $var diff --git a/gitk-git/po/zh_cn.po b/gitk-git/po/zh_cn.po new file mode 100644 index 0000000000..17b7f899da --- /dev/null +++ b/gitk-git/po/zh_cn.po @@ -0,0 +1,1367 @@ +# Translation of gitk to Simplified Chinese. +# +# Translators: +# YanKe <imyanke@163.com>, 2017 + +msgid "" +msgstr "" +"Project-Id-Version: Git Chinese Localization Project\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-28 23:11+0800\n" +"PO-Revision-Date: 2017-03-11 02:27+0800\n" +"Last-Translator: YanKe <imyanke@163.com>\n" +"Language-Team: Chinese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" + +#: gitk:140 +msgid "Couldn't get list of unmerged files:" +msgstr "不能获取未合并文件列表:" + +#: gitk:212 gitk:2403 +msgid "Color words" +msgstr "着色显示差异" + +#: gitk:217 gitk:2403 gitk:8249 gitk:8282 +msgid "Markup words" +msgstr "标记显示差异" + +#: gitk:324 +msgid "Error parsing revisions:" +msgstr "解析版本错误:" + +#: gitk:380 +msgid "Error executing --argscmd command:" +msgstr "运行 --argscmd命令出错" + +#: gitk:393 +msgid "No files selected: --merge specified but no files are unmerged." +msgstr "没有选中文件:--指定merge参数但没有未合并的文件。" + +#: gitk:396 +msgid "" +"No files selected: --merge specified but no unmerged files are within file " +"limit." +msgstr "没有选中文件:--指定merge参数但没有未合并的文件在文件中" + +#: gitk:418 gitk:566 +msgid "Error executing git log:" +msgstr "执行git log命令出错:" + +#: gitk:436 gitk:582 +msgid "Reading" +msgstr "读取中" + +#: gitk:496 gitk:4549 +msgid "Reading commits..." +msgstr "提交记录读取中..." + +#: gitk:499 gitk:1641 gitk:4552 +msgid "No commits selected" +msgstr "未选中任何提交" + +#: gitk:1449 gitk:4069 gitk:12583 +msgid "Command line" +msgstr "命令行" + +#: gitk:1515 +msgid "Can't parse git log output:" +msgstr "不能解析git log输出:" + +#: gitk:1744 +msgid "No commit information available" +msgstr "无可用提交信息" + +#: gitk:1907 gitk:1936 gitk:4339 gitk:9789 gitk:11388 gitk:11668 +msgid "OK" +msgstr "确定" + +#: gitk:1938 gitk:4341 gitk:9225 gitk:9304 gitk:9434 gitk:9520 gitk:9791 +#: gitk:11389 gitk:11669 +msgid "Cancel" +msgstr "取消" + +#: gitk:2087 +msgid "&Update" +msgstr "更新" + +#: gitk:2088 +msgid "&Reload" +msgstr "重新加载" + +#: gitk:2089 +msgid "Reread re&ferences" +msgstr "重新读取引用" + +#: gitk:2090 +msgid "&List references" +msgstr "列出引用(分支以及tag)" + +#: gitk:2092 +msgid "Start git &gui" +msgstr "启动git gui客户端" + +#: gitk:2094 +msgid "&Quit" +msgstr "退出" + +#: gitk:2086 +msgid "&File" +msgstr "文件" + +#: gitk:2098 +msgid "&Preferences" +msgstr "偏好设置" + +#: gitk:2097 +msgid "&Edit" +msgstr "编辑" + +#: gitk:2102 +msgid "&New view..." +msgstr "新视图..." + +#: gitk:2103 +msgid "&Edit view..." +msgstr "编辑视图..." + +#: gitk:2104 +msgid "&Delete view" +msgstr "删除视图" + +#: gitk:2106 +msgid "&All files" +msgstr "所有文件" + +#: gitk:2101 +msgid "&View" +msgstr "视图" + +#: gitk:2111 gitk:2121 +msgid "&About gitk" +msgstr "关于gitk" + +#: gitk:2112 gitk:2126 +msgid "&Key bindings" +msgstr "快捷键" + +#: gitk:2110 gitk:2125 +msgid "&Help" +msgstr "帮助" + +#: gitk:2203 gitk:8681 +msgid "SHA1 ID:" +msgstr "SHA1 ID:" + +#: gitk:2247 +msgid "Row" +msgstr "行" + +#: gitk:2285 +msgid "Find" +msgstr "查找" + +#: gitk:2313 +msgid "commit" +msgstr "提交" + +#: gitk:2317 gitk:2319 gitk:4711 gitk:4734 gitk:4758 gitk:6779 gitk:6851 +#: gitk:6936 +msgid "containing:" +msgstr "包含:" + +#: gitk:2320 gitk:3550 gitk:3555 gitk:4787 +msgid "touching paths:" +msgstr "影响路径:" + +#: gitk:2321 gitk:4801 +msgid "adding/removing string:" +msgstr "增加/删除字符串:" + +#: gitk:2322 gitk:4803 +msgid "changing lines matching:" +msgstr "改变行匹配:" + +#: gitk:2331 gitk:2333 gitk:4790 +msgid "Exact" +msgstr "精确匹配" + +#: gitk:2333 gitk:4878 gitk:6747 +msgid "IgnCase" +msgstr "忽略大小写" + +#: gitk:2333 gitk:4760 gitk:4876 gitk:6743 +msgid "Regexp" +msgstr "正则" + +#: gitk:2335 gitk:2336 gitk:4898 gitk:4928 gitk:4935 gitk:6872 gitk:6940 +msgid "All fields" +msgstr "所有字段" + +#: gitk:2336 gitk:4895 gitk:4928 gitk:6810 +msgid "Headline" +msgstr "标题" + +#: gitk:2337 gitk:4895 gitk:6810 gitk:6940 gitk:7413 +msgid "Comments" +msgstr "提交注释" + +#: gitk:2337 gitk:4895 gitk:4900 gitk:4935 gitk:6810 gitk:7348 gitk:8859 +#: gitk:8874 +msgid "Author" +msgstr "作者" + +#: gitk:2337 gitk:4895 gitk:6810 gitk:7350 +msgid "Committer" +msgstr "提交者" + +#: gitk:2371 +msgid "Search" +msgstr "搜索" + +#: gitk:2379 +msgid "Diff" +msgstr "差异" + +#: gitk:2381 +msgid "Old version" +msgstr "老版本" + +#: gitk:2383 +msgid "New version" +msgstr "新版本" + +#: gitk:2386 +msgid "Lines of context" +msgstr "Diff上下文显示行数" + +#: gitk:2396 +msgid "Ignore space change" +msgstr "忽略空格修改" + +#: gitk:2400 gitk:2402 gitk:7983 gitk:8235 +msgid "Line diff" +msgstr "按行显示差异" + +#: gitk:2467 +msgid "Patch" +msgstr "补丁" + +#: gitk:2469 +msgid "Tree" +msgstr "树" + +#: gitk:2639 gitk:2660 +msgid "Diff this -> selected" +msgstr "比较从当前提交到选中提交的差异" + +#: gitk:2640 gitk:2661 +msgid "Diff selected -> this" +msgstr "比较从选中提交到当前提交的差异" + +#: gitk:2641 gitk:2662 +msgid "Make patch" +msgstr "制作补丁" + +#: gitk:2642 gitk:9283 +msgid "Create tag" +msgstr "创建tag" + +#: gitk:2643 +msgid "Copy commit summary" +msgstr "复制提交摘要" + +#: gitk:2644 gitk:9414 +msgid "Write commit to file" +msgstr "写入提交到文件" + +#: gitk:2645 +msgid "Create new branch" +msgstr "创建新分支" + +#: gitk:2646 +msgid "Cherry-pick this commit" +msgstr "在此提交运用补丁(cherry-pick)命令" + +#: gitk:2647 +msgid "Reset HEAD branch to here" +msgstr "将分支头(HEAD)重置到此处" + +#: gitk:2648 +msgid "Mark this commit" +msgstr "标记此提交" + +#: gitk:2649 +msgid "Return to mark" +msgstr "返回到标记" + +#: gitk:2650 +msgid "Find descendant of this and mark" +msgstr "查找本次提交的子提交并标记" + +#: gitk:2651 +msgid "Compare with marked commit" +msgstr "和已标记的提交作比较" + +#: gitk:2652 gitk:2663 +msgid "Diff this -> marked commit" +msgstr "比较从当前提交到已标记提交的差异" + +#: gitk:2653 gitk:2664 +msgid "Diff marked commit -> this" +msgstr "比较从已标记提交到当前提交的差异" + +#: gitk:2654 +msgid "Revert this commit" +msgstr "撤销(revert)此提交" + +#: gitk:2670 +msgid "Check out this branch" +msgstr "检出(checkout)此分支" + +#: gitk:2671 +msgid "Rename this branch" +msgstr "重命名(Rename)此分支" + +#: gitk:2672 +msgid "Remove this branch" +msgstr "删除(Remove)此分支" + +#: gitk:2673 +msgid "Copy branch name" +msgstr "复制分支名称" + +#: gitk:2680 +msgid "Highlight this too" +msgstr "高亮此处" + +#: gitk:2681 +msgid "Highlight this only" +msgstr "只高亮此处" + +#: gitk:2682 +msgid "External diff" +msgstr "外部diff" + +#: gitk:2683 +msgid "Blame parent commit" +msgstr "Blame父提交" + +#: gitk:2684 +msgid "Copy path" +msgstr "复制路径" + +#: gitk:2691 +msgid "Show origin of this line" +msgstr "显示此行原始提交" + +#: gitk:2692 +msgid "Run git gui blame on this line" +msgstr "在此行运行git gui客户端的blame" + +#: gitk:3036 +msgid "About gitk" +msgstr "关于gitk" + +#: gitk:3038 +msgid "" +"\n" +"Gitk - a commit viewer for git\n" +"\n" +"Copyright © 2005-2016 Paul Mackerras\n" +"\n" +"Use and redistribute under the terms of the GNU General Public License" +msgstr "\nGitk — 一个git的提交查看器\n\n© 2005-2016 Paul Mackerras\n\n在GNU许可证下使用以及分发" + +#: gitk:3046 gitk:3113 gitk:10004 +msgid "Close" +msgstr "关闭" + +#: gitk:3067 +msgid "Gitk key bindings" +msgstr "Gitk快捷键" + +#: gitk:3070 +msgid "Gitk key bindings:" +msgstr "Gitk快捷键:" + +#: gitk:3072 +#, tcl-format +msgid "<%s-Q>\t\tQuit" +msgstr "<%s-Q>\t\t退出" + +#: gitk:3073 +#, tcl-format +msgid "<%s-W>\t\tClose window" +msgstr "<%s-W>\t\t关闭窗口" + +#: gitk:3074 +msgid "<Home>\t\tMove to first commit" +msgstr "<Home>\t\t移动到第一次提交" + +#: gitk:3075 +msgid "<End>\t\tMove to last commit" +msgstr "<End>\t\t移动到最后一次提交" + +#: gitk:3076 +msgid "<Up>, p, k\tMove up one commit" +msgstr "<Up>, p, k\t移动到上一次提交" + +#: gitk:3077 +msgid "<Down>, n, j\tMove down one commit" +msgstr "<Down>, n, j\t移动到下一次提交" + +#: gitk:3078 +msgid "<Left>, z, h\tGo back in history list" +msgstr "<Left>, z, h\t历史列表的上一项" + +#: gitk:3079 +msgid "<Right>, x, l\tGo forward in history list" +msgstr "<Right>, x, l\t历史列表的下一项" + +#: gitk:3080 +#, tcl-format +msgid "<%s-n>\tGo to n-th parent of current commit in history list" +msgstr "<%s-n>\t在历史列表中前往本次提交的第n个父提交" + +#: gitk:3081 +msgid "<PageUp>\tMove up one page in commit list" +msgstr "<PageUp>\t上一页提交列表" + +#: gitk:3082 +msgid "<PageDown>\tMove down one page in commit list" +msgstr "<PageDown>\t下一页提交列表" + +#: gitk:3083 +#, tcl-format +msgid "<%s-Home>\tScroll to top of commit list" +msgstr "<%s-Home>\t滚动到提交列表顶部" + +#: gitk:3084 +#, tcl-format +msgid "<%s-End>\tScroll to bottom of commit list" +msgstr "<%s-End>\t滚动到提交列表底部" + +#: gitk:3085 +#, tcl-format +msgid "<%s-Up>\tScroll commit list up one line" +msgstr "<%s-Up>\t向上滚动一行提交列表" + +#: gitk:3086 +#, tcl-format +msgid "<%s-Down>\tScroll commit list down one line" +msgstr "<%s-Down>\t向下滚动一行提交列表" + +#: gitk:3087 +#, tcl-format +msgid "<%s-PageUp>\tScroll commit list up one page" +msgstr "<%s-PageUp>\t向上滚动一页提交列表" + +#: gitk:3088 +#, tcl-format +msgid "<%s-PageDown>\tScroll commit list down one page" +msgstr "<%s-PageDown>\t向下滚动一页提交列表" + +#: gitk:3089 +msgid "<Shift-Up>\tFind backwards (upwards, later commits)" +msgstr "<Shift-Up>\t向后查找(向上的,更晚的提交)" + +#: gitk:3090 +msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" +msgstr "<Shift-Down>\t向前查找(向下的,更早的提交)" + +#: gitk:3091 +msgid "<Delete>, b\tScroll diff view up one page" +msgstr "<Delete>, b\t向上滚动diff视图一页" + +#: gitk:3092 +msgid "<Backspace>\tScroll diff view up one page" +msgstr "<Backspace>\t向上滚动diff视图一页" + +#: gitk:3093 +msgid "<Space>\t\tScroll diff view down one page" +msgstr "<Space>\t\t向下滚动diff视图一页" + +#: gitk:3094 +msgid "u\t\tScroll diff view up 18 lines" +msgstr "u\t\t向上滚动diff视图18行" + +#: gitk:3095 +msgid "d\t\tScroll diff view down 18 lines" +msgstr "d\t\t向下滚动diff视图18行" + +#: gitk:3096 +#, tcl-format +msgid "<%s-F>\t\tFind" +msgstr "<%s-F>\t\t查找" + +#: gitk:3097 +#, tcl-format +msgid "<%s-G>\t\tMove to next find hit" +msgstr "<%s-G>\t\t移动到下一次查找命中" + +#: gitk:3098 +msgid "<Return>\tMove to next find hit" +msgstr "<Return>\t\t移动到下一次查找命中" + +#: gitk:3099 +msgid "g\t\tGo to commit" +msgstr "g\t\t转到提交" + +#: gitk:3100 +msgid "/\t\tFocus the search box" +msgstr "/\t\t选中搜索框" + +#: gitk:3101 +msgid "?\t\tMove to previous find hit" +msgstr "?\t\t移动到上一次查找命中" + +#: gitk:3102 +msgid "f\t\tScroll diff view to next file" +msgstr "f\t\t滚动diff视图到下一个文件" + +#: gitk:3103 +#, tcl-format +msgid "<%s-S>\t\tSearch for next hit in diff view" +msgstr "<%s-S>\t\t在diff视图中查找下一此命中" + +#: gitk:3104 +#, tcl-format +msgid "<%s-R>\t\tSearch for previous hit in diff view" +msgstr "<%s-R>\t\t在diff视图中查找上一次命中" + +#: gitk:3105 +#, tcl-format +msgid "<%s-KP+>\tIncrease font size" +msgstr "<%s-KP+>\t增大字体大小" + +#: gitk:3106 +#, tcl-format +msgid "<%s-plus>\tIncrease font size" +msgstr "<%s-plus>\t增大字体大小" + +#: gitk:3107 +#, tcl-format +msgid "<%s-KP->\tDecrease font size" +msgstr "<%s-KP->\t减小字体大小" + +#: gitk:3108 +#, tcl-format +msgid "<%s-minus>\tDecrease font size" +msgstr "<%s-minus>\t减小字体大小" + +#: gitk:3109 +msgid "<F5>\t\tUpdate" +msgstr "<F5>\t\t更新" + +#: gitk:3574 gitk:3583 +#, tcl-format +msgid "Error creating temporary directory %s:" +msgstr "创建临时目录出错%s:" + +#: gitk:3596 +#, tcl-format +msgid "Error getting \"%s\" from %s:" +msgstr "从%s获取\"%s\"出错:" + +#: gitk:3659 +msgid "command failed:" +msgstr "执行命令失败:" + +#: gitk:3808 +msgid "No such commit" +msgstr "无此提交" + +#: gitk:3822 +msgid "git gui blame: command failed:" +msgstr "git gui blame:执行命令失败:" + +#: gitk:3853 +#, tcl-format +msgid "Couldn't read merge head: %s" +msgstr "不能读取合并头(merge head):%s" + +#: gitk:3861 +#, tcl-format +msgid "Error reading index: %s" +msgstr "读取索引出错:%s" + +#: gitk:3886 +#, tcl-format +msgid "Couldn't start git blame: %s" +msgstr "不能执行git blame:%s" + +#: gitk:3889 gitk:6778 +msgid "Searching" +msgstr "搜索中" + +#: gitk:3921 +#, tcl-format +msgid "Error running git blame: %s" +msgstr "运行git blame出错:%s" + +#: gitk:3949 +#, tcl-format +msgid "That line comes from commit %s, which is not in this view" +msgstr "此行来自提交%s,不在此视图中" + +#: gitk:3963 +msgid "External diff viewer failed:" +msgstr "外部diff查看器失败:" + +#: gitk:4067 +msgid "All files" +msgstr "所有文件" + +#: gitk:4091 +msgid "View" +msgstr "视图" + +#: gitk:4094 +msgid "Gitk view definition" +msgstr "Gitk视图定义" + +#: gitk:4098 +msgid "Remember this view" +msgstr "记住此视图" + +#: gitk:4099 +msgid "References (space separated list):" +msgstr "引用(空格切分的列表):" + +#: gitk:4100 +msgid "Branches & tags:" +msgstr "分支和tags" + +#: gitk:4101 +msgid "All refs" +msgstr "所有引用" + +#: gitk:4102 +msgid "All (local) branches" +msgstr "所有(本地)分支" + +#: gitk:4103 +msgid "All tags" +msgstr "所有tag" + +#: gitk:4104 +msgid "All remote-tracking branches" +msgstr "所有远程跟踪分支" + +#: gitk:4105 +msgid "Commit Info (regular expressions):" +msgstr "提交信息 (正则表达式):" + +#: gitk:4106 +msgid "Author:" +msgstr "作者:" + +#: gitk:4107 +msgid "Committer:" +msgstr "提交者:" + +#: gitk:4108 +msgid "Commit Message:" +msgstr "提交信息:" + +#: gitk:4109 +msgid "Matches all Commit Info criteria" +msgstr "匹配所有提交信息标准" + +#: gitk:4110 +msgid "Matches no Commit Info criteria" +msgstr "匹配无提交信息标准" + +#: gitk:4111 +msgid "Changes to Files:" +msgstr "文件修改列表:" + +#: gitk:4112 +msgid "Fixed String" +msgstr "固定字符串" + +#: gitk:4113 +msgid "Regular Expression" +msgstr "正则表达式:" + +#: gitk:4114 +msgid "Search string:" +msgstr "搜索字符串:" + +#: gitk:4115 +msgid "" +"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " +"15:27:38\"):" +msgstr "提交日期 (\"2星期之前\", \"2009-03-17 15:27:38\", \"5月 17, 2009 15:27:38\"):" + +#: gitk:4116 +msgid "Since:" +msgstr "自:" + +#: gitk:4117 +msgid "Until:" +msgstr "到:" + +#: gitk:4118 +msgid "Limit and/or skip a number of revisions (positive integer):" +msgstr "限制 且/或 跳过一定数量的版本(正整数):" + +#: gitk:4119 +msgid "Number to show:" +msgstr "显示数量:" + +#: gitk:4120 +msgid "Number to skip:" +msgstr "跳过数量:" + +#: gitk:4121 +msgid "Miscellaneous options:" +msgstr "其他选项:" + +#: gitk:4122 +msgid "Strictly sort by date" +msgstr "严格按日期整理" + +#: gitk:4123 +msgid "Mark branch sides" +msgstr "标记分支边界" + +#: gitk:4124 +msgid "Limit to first parent" +msgstr "限制到第一个父提交" + +#: gitk:4125 +msgid "Simple history" +msgstr "简易历史" + +#: gitk:4126 +msgid "Additional arguments to git log:" +msgstr "git log命令的额外参数:" + +#: gitk:4127 +msgid "Enter files and directories to include, one per line:" +msgstr "输入文件和文件夹来引用,每行一个:" + +#: gitk:4128 +msgid "Command to generate more commits to include:" +msgstr "命令产生更多的提交来引用:" + +#: gitk:4252 +msgid "Gitk: edit view" +msgstr "Gitk: 编辑视图" + +#: gitk:4260 +msgid "-- criteria for selecting revisions" +msgstr "-- 用来选择版本的规则" + +#: gitk:4265 +msgid "View Name" +msgstr "视图名称" + +#: gitk:4340 +msgid "Apply (F5)" +msgstr "应用(F5)" + +#: gitk:4378 +msgid "Error in commit selection arguments:" +msgstr "提交选择参数错误:" + +#: gitk:4433 gitk:4486 gitk:4948 gitk:4962 gitk:6232 gitk:12524 gitk:12525 +msgid "None" +msgstr "无" + +#: gitk:5045 gitk:5050 +msgid "Descendant" +msgstr "子提交" + +#: gitk:5046 +msgid "Not descendant" +msgstr "非子提交" + +#: gitk:5053 gitk:5058 +msgid "Ancestor" +msgstr "父提交" + +#: gitk:5054 +msgid "Not ancestor" +msgstr "非父提交" + +#: gitk:5348 +msgid "Local changes checked in to index but not committed" +msgstr "已添加到索引但未提交的修改" + +#: gitk:5384 +msgid "Local uncommitted changes, not checked in to index" +msgstr "未添加到索引且未提交的修改" + +#: gitk:7158 +msgid "and many more" +msgstr "更多" + +#: gitk:7161 +msgid "many" +msgstr "很多" + +#: gitk:7352 +msgid "Tags:" +msgstr "Tags:" + +#: gitk:7369 gitk:7375 gitk:8854 +msgid "Parent" +msgstr "父节点" + +#: gitk:7380 +msgid "Child" +msgstr "子节点" + +#: gitk:7389 +msgid "Branch" +msgstr "分支" + +#: gitk:7392 +msgid "Follows" +msgstr "之后的tag" + +#: gitk:7395 +msgid "Precedes" +msgstr "之前的tag" + +#: gitk:7990 +#, tcl-format +msgid "Error getting diffs: %s" +msgstr "获取差异错误:%s" + +#: gitk:8679 +msgid "Goto:" +msgstr "转到:" + +#: gitk:8700 +#, tcl-format +msgid "Short SHA1 id %s is ambiguous" +msgstr "短格式的SHA1提交号%s不明确、有歧义" + +#: gitk:8707 +#, tcl-format +msgid "Revision %s is not known" +msgstr "版本%s未知" + +#: gitk:8717 +#, tcl-format +msgid "SHA1 id %s is not known" +msgstr "提交号(SHA1 id)%s未知" + +#: gitk:8719 +#, tcl-format +msgid "Revision %s is not in the current view" +msgstr "版本%s不在当前视图中" + +#: gitk:8861 gitk:8876 +msgid "Date" +msgstr "日期" + +#: gitk:8864 +msgid "Children" +msgstr "子节点" + +#: gitk:8927 +#, tcl-format +msgid "Reset %s branch to here" +msgstr "重置分支%s到此处" + +#: gitk:8929 +msgid "Detached head: can't reset" +msgstr "分离的头(head):不能重置(reset)" + +#: gitk:9034 gitk:9040 +msgid "Skipping merge commit " +msgstr "跳过合并提交" + +#: gitk:9049 gitk:9054 +msgid "Error getting patch ID for " +msgstr "获取补丁ID出错" + +#: gitk:9050 gitk:9055 +msgid " - stopping\n" +msgstr " — 停止中\n" + +#: gitk:9060 gitk:9063 gitk:9071 gitk:9085 gitk:9094 +msgid "Commit " +msgstr "提交" + +#: gitk:9064 +msgid "" +" is the same patch as\n" +" " +msgstr " 是相同的补丁(patch)\n " + +#: gitk:9072 +msgid "" +" differs from\n" +" " +msgstr " 差异来自\n " + +#: gitk:9074 +msgid "" +"Diff of commits:\n" +"\n" +msgstr "提交的差异(Diff):\n\n" + +#: gitk:9086 gitk:9095 +#, tcl-format +msgid " has %s children - stopping\n" +msgstr "有%s子节点 — 停止中\n" + +#: gitk:9114 +#, tcl-format +msgid "Error writing commit to file: %s" +msgstr "写入提交到文件出错:%s" + +#: gitk:9120 +#, tcl-format +msgid "Error diffing commits: %s" +msgstr "比较提交差异出错:%s" + +#: gitk:9166 +msgid "Top" +msgstr "顶部" + +#: gitk:9167 +msgid "From" +msgstr "从" + +#: gitk:9172 +msgid "To" +msgstr "到" + +#: gitk:9196 +msgid "Generate patch" +msgstr "生成补丁(patch)" + +#: gitk:9198 +msgid "From:" +msgstr "从:" + +#: gitk:9207 +msgid "To:" +msgstr "到:" + +#: gitk:9216 +msgid "Reverse" +msgstr "反向(Reverse)" + +#: gitk:9218 gitk:9428 +msgid "Output file:" +msgstr "输出文件:" + +#: gitk:9224 +msgid "Generate" +msgstr "生成" + +#: gitk:9262 +msgid "Error creating patch:" +msgstr "创建补丁(patch)出错:" + +#: gitk:9285 gitk:9416 gitk:9504 +msgid "ID:" +msgstr "ID:" + +#: gitk:9294 +msgid "Tag name:" +msgstr "Tag名称:" + +#: gitk:9297 +msgid "Tag message is optional" +msgstr "Tag信息是可选的" + +#: gitk:9299 +msgid "Tag message:" +msgstr "Tag信息:" + +#: gitk:9303 gitk:9474 +msgid "Create" +msgstr "创建" + +#: gitk:9321 +msgid "No tag name specified" +msgstr "未指定tag名称" + +#: gitk:9325 +#, tcl-format +msgid "Tag \"%s\" already exists" +msgstr "Tag\"%s\"已经存在" + +#: gitk:9335 +msgid "Error creating tag:" +msgstr "创建tag出错:" + +#: gitk:9425 +msgid "Command:" +msgstr "命令:" + +#: gitk:9433 +msgid "Write" +msgstr "写入" + +#: gitk:9451 +msgid "Error writing commit:" +msgstr "写入提交出错:" + +#: gitk:9473 +msgid "Create branch" +msgstr "创建分支" + +#: gitk:9489 +#, tcl-format +msgid "Rename branch %s" +msgstr "重命名分支%s" + +#: gitk:9490 +msgid "Rename" +msgstr "重命名" + +#: gitk:9514 +msgid "Name:" +msgstr "名称:" + +#: gitk:9538 +msgid "Please specify a name for the new branch" +msgstr "请指定新分支的名称" + +#: gitk:9543 +#, tcl-format +msgid "Branch '%s' already exists. Overwrite?" +msgstr "分支\"%s\"已经存在。覆盖它?" + +#: gitk:9587 +msgid "Please specify a new name for the branch" +msgstr "请重新指定新分支的名称" + +#: gitk:9650 +#, tcl-format +msgid "Commit %s is already included in branch %s -- really re-apply it?" +msgstr "提交%s已经存在于分支%s。确定重新应用它?" + +#: gitk:9655 +msgid "Cherry-picking" +msgstr "打补丁中(Cherry-picking)" + +#: gitk:9664 +#, tcl-format +msgid "" +"Cherry-pick failed because of local changes to file '%s'.\n" +"Please commit, reset or stash your changes and try again." +msgstr "打补丁(Cherry-pick)失败,因为本地修改了文件\"%s\"。\n请提交(commit)、重置(reset)或暂存(stash)修改后重试。" + +#: gitk:9670 +msgid "" +"Cherry-pick failed because of merge conflict.\n" +"Do you wish to run git citool to resolve it?" +msgstr "打补丁(Cherry-pick)失败因为合并冲突。\n你是否希望运行git citool 来解决冲突?" + +#: gitk:9686 gitk:9744 +msgid "No changes committed" +msgstr "无已经提交的修改" + +#: gitk:9713 +#, tcl-format +msgid "Commit %s is not included in branch %s -- really revert it?" +msgstr "提交%s不包含在分支%s中,确认回滚(revert)它?" + +#: gitk:9718 +msgid "Reverting" +msgstr "回滚中(Reverting)" + +#: gitk:9726 +#, tcl-format +msgid "" +"Revert failed because of local changes to the following files:%s Please " +"commit, reset or stash your changes and try again." +msgstr "回滚(revert)失败,因为如下的本地文件修改:%s\n请提交(commit)、重置(reset)或者暂存(stash)改变后重试。" + +#: gitk:9730 +msgid "" +"Revert failed because of merge conflict.\n" +" Do you wish to run git citool to resolve it?" +msgstr "回滚(revert)失败,因为合并冲突。\n你是否希望运行git citool来解决冲突?" + +#: gitk:9773 +msgid "Confirm reset" +msgstr "确认重置(reset)" + +#: gitk:9775 +#, tcl-format +msgid "Reset branch %s to %s?" +msgstr "重置(reset)分支%s到%s?" + +#: gitk:9777 +msgid "Reset type:" +msgstr "重置(reset)类型:" + +#: gitk:9780 +msgid "Soft: Leave working tree and index untouched" +msgstr "软性:离开工作树,索引未改变" + +#: gitk:9783 +msgid "Mixed: Leave working tree untouched, reset index" +msgstr "混合:离开工作树(未改变),索引重置" + +#: gitk:9786 +msgid "" +"Hard: Reset working tree and index\n" +"(discard ALL local changes)" +msgstr "硬性:重置工作树和索引\n(丢弃所有的本地修改)" + +#: gitk:9803 +msgid "Resetting" +msgstr "重置中(Resetting)" + +#: gitk:9876 +#, tcl-format +msgid "A local branch named %s exists already" +msgstr "本地分支%s已经存在" + +#: gitk:9884 +msgid "Checking out" +msgstr "检出中(Checking out)" + +#: gitk:9943 +msgid "Cannot delete the currently checked-out branch" +msgstr "不能删除当前检出(checkout)分支" + +#: gitk:9949 +#, tcl-format +msgid "" +"The commits on branch %s aren't on any other branch.\n" +"Really delete branch %s?" +msgstr "在分支%s上的提交不在其他任何分支上。\n确认删除分支%s?" + +#: gitk:9980 +#, tcl-format +msgid "Tags and heads: %s" +msgstr "Tags和头指针(heads):%s" + +#: gitk:9997 +msgid "Filter" +msgstr "过滤器" + +#: gitk:10293 +msgid "" +"Error reading commit topology information; branch and preceding/following " +"tag information will be incomplete." +msgstr "读取提交拓扑信息出错;分支和之前/之后的tag信息将不能完成。" + +#: gitk:11270 +msgid "Tag" +msgstr "标签(Tag)" + +#: gitk:11274 +msgid "Id" +msgstr "Id" + +#: gitk:11357 +msgid "Gitk font chooser" +msgstr "Gitk字体选择" + +#: gitk:11374 +msgid "B" +msgstr "粗体" + +#: gitk:11377 +msgid "I" +msgstr "斜体" + +#: gitk:11495 +msgid "Commit list display options" +msgstr "提交列表展示选项" + +#: gitk:11498 +msgid "Maximum graph width (lines)" +msgstr "最大图宽度(行数)" + +#: gitk:11502 +#, no-tcl-format +msgid "Maximum graph width (% of pane)" +msgstr "最大图宽度(%窗口百分比)" + +#: gitk:11505 +msgid "Show local changes" +msgstr "显示本地修改" + +#: gitk:11508 +msgid "Auto-select SHA1 (length)" +msgstr "自动选择SHA1(长度)" + +#: gitk:11512 +msgid "Hide remote refs" +msgstr "隐藏远程引用" + +#: gitk:11516 +msgid "Diff display options" +msgstr "差异(Diff)展示选项" + +#: gitk:11518 +msgid "Tab spacing" +msgstr "制表符宽度" + +#: gitk:11521 +msgid "Display nearby tags/heads" +msgstr "显示临近的tags/heads" + +#: gitk:11524 +msgid "Maximum # tags/heads to show" +msgstr "最大tags/heads展示数量" + +#: gitk:11527 +msgid "Limit diffs to listed paths" +msgstr "diff中列出文件限制" + +#: gitk:11530 +msgid "Support per-file encodings" +msgstr "单独文件编码支持" + +#: gitk:11536 gitk:11683 +msgid "External diff tool" +msgstr "外部差异(diff)工具" + +#: gitk:11537 +msgid "Choose..." +msgstr "选择..." + +#: gitk:11542 +msgid "General options" +msgstr "常规选项" + +#: gitk:11545 +msgid "Use themed widgets" +msgstr "使用主题小部件" + +#: gitk:11547 +msgid "(change requires restart)" +msgstr "(需重启生效)" + +#: gitk:11549 +msgid "(currently unavailable)" +msgstr "(当前不可用)" + +#: gitk:11560 +msgid "Colors: press to choose" +msgstr "颜色:点击来选择" + +#: gitk:11563 +msgid "Interface" +msgstr "界面" + +#: gitk:11564 +msgid "interface" +msgstr "界面" + +#: gitk:11567 +msgid "Background" +msgstr "背景" + +#: gitk:11568 gitk:11598 +msgid "background" +msgstr "背景" + +#: gitk:11571 +msgid "Foreground" +msgstr "前景" + +#: gitk:11572 +msgid "foreground" +msgstr "前景" + +#: gitk:11575 +msgid "Diff: old lines" +msgstr "差异(Diff):老代码行" + +#: gitk:11576 +msgid "diff old lines" +msgstr "差异(diff)老代码行" + +#: gitk:11580 +msgid "Diff: new lines" +msgstr "差异(Diff):新代码行" + +#: gitk:11581 +msgid "diff new lines" +msgstr "差异(diff)新代码行" + +#: gitk:11585 +msgid "Diff: hunk header" +msgstr "差异(Diff):补丁片段头信息" + +#: gitk:11587 +msgid "diff hunk header" +msgstr "差异(diff)补丁片段头信息" + +#: gitk:11591 +msgid "Marked line bg" +msgstr "已标记代码行背景" + +#: gitk:11593 +msgid "marked line background" +msgstr "已标记代码行背景" + +#: gitk:11597 +msgid "Select bg" +msgstr "选择背景" + +#: gitk:11606 +msgid "Fonts: press to choose" +msgstr "字体:点击来选择" + +#: gitk:11608 +msgid "Main font" +msgstr "主字体" + +#: gitk:11609 +msgid "Diff display font" +msgstr "差异(Diff)显示字体" + +#: gitk:11610 +msgid "User interface font" +msgstr "用户界面字体" + +#: gitk:11632 +msgid "Gitk preferences" +msgstr "Gitk偏好设置" + +#: gitk:11641 +msgid "General" +msgstr "常规" + +#: gitk:11642 +msgid "Colors" +msgstr "颜色" + +#: gitk:11643 +msgid "Fonts" +msgstr "字体" + +#: gitk:11693 +#, tcl-format +msgid "Gitk: choose color for %s" +msgstr "Gitk:选择颜色用于%s" + +#: gitk:12206 +msgid "" +"Sorry, gitk cannot run with this version of Tcl/Tk.\n" +" Gitk requires at least Tcl/Tk 8.4." +msgstr "对不起,gitk不能运行在当前版本的Tcl/Tk中。\nGitk运行需要最低版本为Tcl/Tk8.4。" + +#: gitk:12416 +msgid "Cannot find a git repository here." +msgstr "在此位置未发现git仓库。" + +#: gitk:12463 +#, tcl-format +msgid "Ambiguous argument '%s': both revision and filename" +msgstr "不明确有歧义的参数\"%s\":版本和文件名称" + +#: gitk:12475 +msgid "Bad arguments to gitk:" +msgstr "运行gitk参数错误:" @@ -774,7 +774,8 @@ static struct string_list guess_refs(const char *ref) return similar_refs; } -void help_unknown_ref(const char *ref, const char *cmd, const char *error) +NORETURN void help_unknown_ref(const char *ref, const char *cmd, + const char *error) { int i; struct string_list suggested_refs = guess_refs(ref); @@ -43,7 +43,7 @@ void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdn * call this to die(), when it is suspected that the user mistyped a * ref to the command, to give suggested "correct" refs. */ -void help_unknown_ref(const char *ref, const char *cmd, const char *error); +NORETURN void help_unknown_ref(const char *ref, const char *cmd, const char *error); static inline void list_config_item(struct string_list *list, const char *prefix, @@ -513,9 +513,11 @@ static void set_proxyauth_name_password(CURL *result) #else struct strbuf s = STRBUF_INIT; - strbuf_addstr_urlencode(&s, proxy_auth.username, 1); + strbuf_addstr_urlencode(&s, proxy_auth.username, + is_rfc3986_unreserved); strbuf_addch(&s, ':'); - strbuf_addstr_urlencode(&s, proxy_auth.password, 1); + strbuf_addstr_urlencode(&s, proxy_auth.password, + is_rfc3986_unreserved); curl_proxyuserpwd = strbuf_detach(&s, NULL); curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd); #endif @@ -1073,6 +1075,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) git_config(urlmatch_config_entry, &config); free(normalized_url); + string_list_clear(&config.vars, 1); #if LIBCURL_VERSION_NUM >= 0x073800 if (http_ssl_backend) { @@ -25,8 +25,12 @@ #if LIBCURL_VERSION_NUM < 0x070704 #define curl_global_cleanup() do { /* nothing */ } while (0) #endif + #if LIBCURL_VERSION_NUM < 0x070800 #define curl_global_init(a) do { /* nothing */ } while (0) +#elif LIBCURL_VERSION_NUM >= 0x070c00 +#define curl_global_init(a) curl_global_init_mem(a, xmalloc, free, \ + xrealloc, xstrdup, xcalloc) #endif #if (LIBCURL_VERSION_NUM < 0x070c04) || (LIBCURL_VERSION_NUM == 0x071000) diff --git a/line-log.c b/line-log.c index 3aff1849e7..9010e00950 100644 --- a/line-log.c +++ b/line-log.c @@ -737,6 +737,38 @@ static struct line_log_data *lookup_line_range(struct rev_info *revs, return ret; } +static int same_paths_in_pathspec_and_range(struct pathspec *pathspec, + struct line_log_data *range) +{ + int i; + struct line_log_data *r; + + for (i = 0, r = range; i < pathspec->nr && r; i++, r = r->next) + if (strcmp(pathspec->items[i].match, r->path)) + return 0; + if (i < pathspec->nr || r) + /* different number of pathspec items and ranges */ + return 0; + + return 1; +} + +static void parse_pathspec_from_ranges(struct pathspec *pathspec, + struct line_log_data *range) +{ + struct line_log_data *r; + struct argv_array array = ARGV_ARRAY_INIT; + const char **paths; + + for (r = range; r; r = r->next) + argv_array_push(&array, r->path); + paths = argv_array_detach(&array); + + parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", paths); + /* strings are now owned by pathspec */ + free(paths); +} + void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args) { struct commit *commit = NULL; @@ -746,20 +778,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list range = parse_lines(rev->diffopt.repo, commit, prefix, args); add_line_range(rev, commit, range); - if (!rev->diffopt.detect_rename) { - struct line_log_data *r; - struct argv_array array = ARGV_ARRAY_INIT; - const char **paths; - - for (r = range; r; r = r->next) - argv_array_push(&array, r->path); - paths = argv_array_detach(&array); - - parse_pathspec(&rev->diffopt.pathspec, 0, - PATHSPEC_PREFER_FULL, "", paths); - /* strings are now owned by pathspec */ - free(paths); - } + parse_pathspec_from_ranges(&rev->diffopt.pathspec, range); } static void move_diff_queue(struct diff_queue_struct *dst, @@ -817,15 +836,29 @@ static void queue_diffs(struct line_log_data *range, struct diff_queue_struct *queue, struct commit *commit, struct commit *parent) { + struct object_id *tree_oid, *parent_tree_oid; + assert(commit); + tree_oid = get_commit_tree_oid(commit); + parent_tree_oid = parent ? get_commit_tree_oid(parent) : NULL; + + if (opt->detect_rename && + !same_paths_in_pathspec_and_range(&opt->pathspec, range)) { + clear_pathspec(&opt->pathspec); + parse_pathspec_from_ranges(&opt->pathspec, range); + } DIFF_QUEUE_CLEAR(&diff_queued_diff); - diff_tree_oid(parent ? get_commit_tree_oid(parent) : NULL, - get_commit_tree_oid(commit), "", opt); - if (opt->detect_rename) { + diff_tree_oid(parent_tree_oid, tree_oid, "", opt); + if (opt->detect_rename && diff_might_be_rename()) { + /* must look at the full tree diff to detect renames */ + clear_pathspec(&opt->pathspec); + DIFF_QUEUE_CLEAR(&diff_queued_diff); + + diff_tree_oid(parent_tree_oid, tree_oid, "", opt); + filter_diffs_for_paths(range, 1); - if (diff_might_be_rename()) - diffcore_std(opt); + diffcore_std(opt); filter_diffs_for_paths(range, 0); } move_diff_queue(queue, &diff_queued_diff); diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index 1cb20c659c..4d88bfe64a 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -6,6 +6,14 @@ #include "list-objects.h" #include "list-objects-filter.h" #include "list-objects-filter-options.h" +#include "promisor-remote.h" +#include "trace.h" +#include "url.h" + +static int parse_combine_filter( + struct list_objects_filter_options *filter_options, + const char *arg, + struct strbuf *errbuf); /* * Parse value of the argument to the "filter" keyword. @@ -29,16 +37,11 @@ static int gently_parse_list_objects_filter( { const char *v0; - if (filter_options->choice) { - if (errbuf) { - strbuf_addstr( - errbuf, - _("multiple filter-specs cannot be combined")); - } - return 1; - } + if (!arg) + return 0; - filter_options->filter_spec = strdup(arg); + if (filter_options->choice) + BUG("filter_options already populated"); if (!strcmp(arg, "blob:none")) { filter_options->choice = LOFC_BLOB_NONE; @@ -52,11 +55,7 @@ static int gently_parse_list_objects_filter( } else if (skip_prefix(arg, "tree:", &v0)) { if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) { - if (errbuf) { - strbuf_addstr( - errbuf, - _("expected 'tree:<depth>'")); - } + strbuf_addstr(errbuf, _("expected 'tree:<depth>'")); return 1; } filter_options->choice = LOFC_TREE_DEPTH; @@ -84,103 +83,298 @@ static int gently_parse_list_objects_filter( _("sparse:path filters support has been dropped")); } return 1; + + } else if (skip_prefix(arg, "combine:", &v0)) { + return parse_combine_filter(filter_options, v0, errbuf); + } /* * Please update _git_fetch() in git-completion.bash when you * add new filters */ - if (errbuf) - strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg); + strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg); memset(filter_options, 0, sizeof(*filter_options)); return 1; } -int parse_list_objects_filter(struct list_objects_filter_options *filter_options, - const char *arg) +static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?"; + +static int has_reserved_character( + struct strbuf *sub_spec, struct strbuf *errbuf) { - struct strbuf buf = STRBUF_INIT; - if (gently_parse_list_objects_filter(filter_options, arg, &buf)) - die("%s", buf.buf); + const char *c = sub_spec->buf; + while (*c) { + if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) { + strbuf_addf( + errbuf, + _("must escape char in sub-filter-spec: '%c'"), + *c); + return 1; + } + c++; + } + return 0; } +static int parse_combine_subfilter( + struct list_objects_filter_options *filter_options, + struct strbuf *subspec, + struct strbuf *errbuf) +{ + size_t new_index = filter_options->sub_nr; + char *decoded; + int result; + + ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1, + filter_options->sub_alloc); + + decoded = url_percent_decode(subspec->buf); + + result = has_reserved_character(subspec, errbuf) || + gently_parse_list_objects_filter( + &filter_options->sub[new_index], decoded, errbuf); + + free(decoded); + return result; +} + +static int parse_combine_filter( + struct list_objects_filter_options *filter_options, + const char *arg, + struct strbuf *errbuf) +{ + struct strbuf **subspecs = strbuf_split_str(arg, '+', 0); + size_t sub; + int result = 0; + + if (!subspecs[0]) { + strbuf_addstr(errbuf, _("expected something after combine:")); + result = 1; + goto cleanup; + } + + for (sub = 0; subspecs[sub] && !result; sub++) { + if (subspecs[sub + 1]) { + /* + * This is not the last subspec. Remove trailing "+" so + * we can parse it. + */ + size_t last = subspecs[sub]->len - 1; + assert(subspecs[sub]->buf[last] == '+'); + strbuf_remove(subspecs[sub], last, 1); + } + result = parse_combine_subfilter( + filter_options, subspecs[sub], errbuf); + } + + filter_options->choice = LOFC_COMBINE; + +cleanup: + strbuf_list_free(subspecs); + if (result) { + list_objects_filter_release(filter_options); + memset(filter_options, 0, sizeof(*filter_options)); + } + return result; +} + +static int allow_unencoded(char ch) +{ + if (ch <= ' ' || ch == '%' || ch == '+') + return 0; + return !strchr(RESERVED_NON_WS, ch); +} + +static void filter_spec_append_urlencode( + struct list_objects_filter_options *filter, const char *raw) +{ + struct strbuf buf = STRBUF_INIT; + strbuf_addstr_urlencode(&buf, raw, allow_unencoded); + trace_printf("Add to combine filter-spec: %s\n", buf.buf); + string_list_append(&filter->filter_spec, strbuf_detach(&buf, NULL)); +} + +/* + * Changes filter_options into an equivalent LOFC_COMBINE filter options + * instance. Does not do anything if filter_options is already LOFC_COMBINE. + */ +static void transform_to_combine_type( + struct list_objects_filter_options *filter_options) +{ + assert(filter_options->choice); + if (filter_options->choice == LOFC_COMBINE) + return; + { + const int initial_sub_alloc = 2; + struct list_objects_filter_options *sub_array = + xcalloc(initial_sub_alloc, sizeof(*sub_array)); + sub_array[0] = *filter_options; + memset(filter_options, 0, sizeof(*filter_options)); + filter_options->sub = sub_array; + filter_options->sub_alloc = initial_sub_alloc; + } + filter_options->sub_nr = 1; + filter_options->choice = LOFC_COMBINE; + string_list_append(&filter_options->filter_spec, xstrdup("combine:")); + filter_spec_append_urlencode( + filter_options, + list_objects_filter_spec(&filter_options->sub[0])); + /* + * We don't need the filter_spec strings for subfilter specs, only the + * top level. + */ + string_list_clear(&filter_options->sub[0].filter_spec, /*free_util=*/0); +} + +void list_objects_filter_die_if_populated( + struct list_objects_filter_options *filter_options) +{ + if (filter_options->choice) + die(_("multiple filter-specs cannot be combined")); +} + +void parse_list_objects_filter( + struct list_objects_filter_options *filter_options, + const char *arg) +{ + struct strbuf errbuf = STRBUF_INIT; + int parse_error; + + if (!filter_options->choice) { + string_list_append(&filter_options->filter_spec, xstrdup(arg)); + + parse_error = gently_parse_list_objects_filter( + filter_options, arg, &errbuf); + } else { + /* + * Make filter_options an LOFC_COMBINE spec so we can trivially + * add subspecs to it. + */ + transform_to_combine_type(filter_options); + + string_list_append(&filter_options->filter_spec, xstrdup("+")); + filter_spec_append_urlencode(filter_options, arg); + ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1, + filter_options->sub_alloc); + + parse_error = gently_parse_list_objects_filter( + &filter_options->sub[filter_options->sub_nr - 1], arg, + &errbuf); + } + if (parse_error) + die("%s", errbuf.buf); +} + int opt_parse_list_objects_filter(const struct option *opt, const char *arg, int unset) { struct list_objects_filter_options *filter_options = opt->value; - if (unset || !arg) { + if (unset || !arg) list_objects_filter_set_no_filter(filter_options); - return 0; + else + parse_list_objects_filter(filter_options, arg); + return 0; +} + +const char *list_objects_filter_spec(struct list_objects_filter_options *filter) +{ + if (!filter->filter_spec.nr) + BUG("no filter_spec available for this filter"); + if (filter->filter_spec.nr != 1) { + struct strbuf concatted = STRBUF_INIT; + strbuf_add_separated_string_list( + &concatted, "", &filter->filter_spec); + string_list_clear(&filter->filter_spec, /*free_util=*/0); + string_list_append( + &filter->filter_spec, strbuf_detach(&concatted, NULL)); } - return parse_list_objects_filter(filter_options, arg); + return filter->filter_spec.items[0].string; } -void expand_list_objects_filter_spec( - const struct list_objects_filter_options *filter, - struct strbuf *expanded_spec) +const char *expand_list_objects_filter_spec( + struct list_objects_filter_options *filter) { - strbuf_init(expanded_spec, strlen(filter->filter_spec)); - if (filter->choice == LOFC_BLOB_LIMIT) - strbuf_addf(expanded_spec, "blob:limit=%lu", + if (filter->choice == LOFC_BLOB_LIMIT) { + struct strbuf expanded_spec = STRBUF_INIT; + strbuf_addf(&expanded_spec, "blob:limit=%lu", filter->blob_limit_value); - else if (filter->choice == LOFC_TREE_DEPTH) - strbuf_addf(expanded_spec, "tree:%lu", - filter->tree_exclude_depth); - else - strbuf_addstr(expanded_spec, filter->filter_spec); + string_list_clear(&filter->filter_spec, /*free_util=*/0); + string_list_append( + &filter->filter_spec, + strbuf_detach(&expanded_spec, NULL)); + } + + return list_objects_filter_spec(filter); } void list_objects_filter_release( struct list_objects_filter_options *filter_options) { - free(filter_options->filter_spec); + size_t sub; + + if (!filter_options) + return; + string_list_clear(&filter_options->filter_spec, /*free_util=*/0); free(filter_options->sparse_oid_value); + for (sub = 0; sub < filter_options->sub_nr; sub++) + list_objects_filter_release(&filter_options->sub[sub]); + free(filter_options->sub); memset(filter_options, 0, sizeof(*filter_options)); } void partial_clone_register( const char *remote, - const struct list_objects_filter_options *filter_options) + struct list_objects_filter_options *filter_options) { - /* - * Record the name of the partial clone remote in the - * config and in the global variable -- the latter is - * used throughout to indicate that partial clone is - * enabled and to expect missing objects. - */ - if (repository_format_partial_clone && - *repository_format_partial_clone && - strcmp(remote, repository_format_partial_clone)) - die(_("cannot change partial clone promisor remote")); + char *cfg_name; + char *filter_name; - git_config_set("core.repositoryformatversion", "1"); - git_config_set("extensions.partialclone", remote); + /* Check if it is already registered */ + if (!promisor_remote_find(remote)) { + git_config_set("core.repositoryformatversion", "1"); - repository_format_partial_clone = xstrdup(remote); + /* Add promisor config for the remote */ + cfg_name = xstrfmt("remote.%s.promisor", remote); + git_config_set(cfg_name, "true"); + free(cfg_name); + } /* * Record the initial filter-spec in the config as * the default for subsequent fetches from this remote. */ - core_partial_clone_filter_default = - xstrdup(filter_options->filter_spec); - git_config_set("core.partialclonefilter", - core_partial_clone_filter_default); + filter_name = xstrfmt("remote.%s.partialclonefilter", remote); + /* NEEDSWORK: 'expand' result leaking??? */ + git_config_set(filter_name, + expand_list_objects_filter_spec(filter_options)); + free(filter_name); + + /* Make sure the config info are reset */ + promisor_remote_reinit(); } void partial_clone_get_default_filter_spec( - struct list_objects_filter_options *filter_options) + struct list_objects_filter_options *filter_options, + const char *remote) { + struct promisor_remote *promisor = promisor_remote_find(remote); + struct strbuf errbuf = STRBUF_INIT; + /* * Parse default value, but silently ignore it if it is invalid. */ - if (!core_partial_clone_filter_default) + if (!promisor) return; + + string_list_append(&filter_options->filter_spec, + promisor->partial_clone_filter); gently_parse_list_objects_filter(filter_options, - core_partial_clone_filter_default, - NULL); + promisor->partial_clone_filter, + &errbuf); + strbuf_release(&errbuf); } diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h index c54f0000fb..b63c5ee1a3 100644 --- a/list-objects-filter-options.h +++ b/list-objects-filter-options.h @@ -2,7 +2,7 @@ #define LIST_OBJECTS_FILTER_OPTIONS_H #include "parse-options.h" -#include "strbuf.h" +#include "string-list.h" /* * The list of defined filters for list-objects. @@ -13,6 +13,7 @@ enum list_objects_filter_choice { LOFC_BLOB_LIMIT, LOFC_TREE_DEPTH, LOFC_SPARSE_OID, + LOFC_COMBINE, LOFC__COUNT /* must be last */ }; @@ -23,8 +24,10 @@ struct list_objects_filter_options { * commands that launch filtering sub-processes, or for communication * over the network, don't use this value; use the result of * expand_list_objects_filter_spec() instead. + * To get the raw filter spec given by the user, use the result of + * list_objects_filter_spec(). */ - char *filter_spec; + struct string_list filter_spec; /* * 'choice' is determined by parsing the filter-spec. This indicates @@ -38,19 +41,40 @@ struct list_objects_filter_options { unsigned int no_filter : 1; /* - * Parsed values (fields) from within the filter-spec. These are - * choice-specific; not all values will be defined for any given - * choice. + * BEGIN choice-specific parsed values from within the filter-spec. Only + * some values will be defined for any given choice. */ + struct object_id *sparse_oid_value; unsigned long blob_limit_value; unsigned long tree_exclude_depth; + + /* LOFC_COMBINE values */ + + /* This array contains all the subfilters which this filter combines. */ + size_t sub_nr, sub_alloc; + struct list_objects_filter_options *sub; + + /* + * END choice-specific parsed values. + */ }; /* Normalized command line arguments */ #define CL_ARG__FILTER "filter" -int parse_list_objects_filter( +void list_objects_filter_die_if_populated( + struct list_objects_filter_options *filter_options); + +/* + * Parses the filter spec string given by arg and either (1) simply places the + * result in filter_options if it is not yet populated or (2) combines it with + * the filter already in filter_options if it is already populated. In the case + * of (2), the filter specs are combined as if specified with 'combine:'. + * + * Dies and prints a user-facing message if an error occurs. + */ +void parse_list_objects_filter( struct list_objects_filter_options *filter_options, const char *arg); @@ -65,13 +89,22 @@ int opt_parse_list_objects_filter(const struct option *opt, /* * Translates abbreviated numbers in the filter's filter_spec into their * fully-expanded forms (e.g., "limit:blob=1k" becomes "limit:blob=1024"). + * Returns a string owned by the list_objects_filter_options object. * - * This form should be used instead of the raw filter_spec field when - * communicating with a remote process or subprocess. + * This form should be used instead of the raw list_objects_filter_spec() + * value when communicating with a remote process or subprocess. + */ +const char *expand_list_objects_filter_spec( + struct list_objects_filter_options *filter); + +/* + * Returns the filter spec string more or less in the form as the user + * entered it. This form of the filter_spec can be used in user-facing + * messages. Returns a string owned by the list_objects_filter_options + * object. */ -void expand_list_objects_filter_spec( - const struct list_objects_filter_options *filter, - struct strbuf *expanded_spec); +const char *list_objects_filter_spec( + struct list_objects_filter_options *filter); void list_objects_filter_release( struct list_objects_filter_options *filter_options); @@ -85,8 +118,9 @@ static inline void list_objects_filter_set_no_filter( void partial_clone_register( const char *remote, - const struct list_objects_filter_options *filter_options); -void partial_clone_get_default_filter_spec( struct list_objects_filter_options *filter_options); +void partial_clone_get_default_filter_spec( + struct list_objects_filter_options *filter_options, + const char *remote); #endif /* LIST_OBJECTS_FILTER_OPTIONS_H */ diff --git a/list-objects-filter.c b/list-objects-filter.c index 36e1f774bc..d624f1c898 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -26,11 +26,46 @@ */ #define FILTER_SHOWN_BUT_REVISIT (1<<21) -/* - * A filter for list-objects to omit ALL blobs from the traversal. - * And to OPTIONALLY collect a list of the omitted OIDs. - */ -struct filter_blobs_none_data { +struct subfilter { + struct filter *filter; + struct oidset seen; + struct oidset omits; + struct object_id skip_tree; + unsigned is_skipping_tree : 1; +}; + +struct filter { + enum list_objects_filter_result (*filter_object_fn)( + struct repository *r, + enum list_objects_filter_situation filter_situation, + struct object *obj, + const char *pathname, + const char *filename, + struct oidset *omits, + void *filter_data); + + /* + * Optional. If this function is supplied and the filter needs + * to collect omits, then this function is called once before + * free_fn is called. + * + * This is required because the following two conditions hold: + * + * a. A tree filter can add and remove objects as an object + * graph is traversed. + * b. A combine filter's omit set is the union of all its + * subfilters, which may include tree: filters. + * + * As such, the omits sets must be separate sets, and can only + * be unioned after the traversal is completed. + */ + void (*finalize_omits_fn)(struct oidset *omits, void *filter_data); + + void (*free_fn)(void *filter_data); + + void *filter_data; + + /* If non-NULL, the filter collects a list of the omitted OIDs here. */ struct oidset *omits; }; @@ -40,10 +75,9 @@ static enum list_objects_filter_result filter_blobs_none( struct object *obj, const char *pathname, const char *filename, + struct oidset *omits, void *filter_data_) { - struct filter_blobs_none_data *filter_data = filter_data_; - switch (filter_situation) { default: BUG("unknown filter_situation: %d", filter_situation); @@ -61,24 +95,18 @@ static enum list_objects_filter_result filter_blobs_none( assert(obj->type == OBJ_BLOB); assert((obj->flags & SEEN) == 0); - if (filter_data->omits) - oidset_insert(filter_data->omits, &obj->oid); + if (omits) + oidset_insert(omits, &obj->oid); return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */ } } -static void *filter_blobs_none__init( - struct oidset *omitted, +static void filter_blobs_none__init( struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn) + struct filter *filter) { - struct filter_blobs_none_data *d = xcalloc(1, sizeof(*d)); - d->omits = omitted; - - *filter_fn = filter_blobs_none; - *filter_free_fn = free; - return d; + filter->filter_object_fn = filter_blobs_none; + filter->free_fn = free; } /* @@ -86,8 +114,6 @@ static void *filter_blobs_none__init( * Can OPTIONALLY collect a list of the omitted OIDs. */ struct filter_trees_depth_data { - struct oidset *omits; - /* * Maps trees to the minimum depth at which they were seen. It is not * necessary to re-traverse a tree at deeper or equal depths than it has @@ -110,16 +136,16 @@ struct seen_map_entry { /* Returns 1 if the oid was in the omits set before it was invoked. */ static int filter_trees_update_omits( struct object *obj, - struct filter_trees_depth_data *filter_data, + struct oidset *omits, int include_it) { - if (!filter_data->omits) + if (!omits) return 0; if (include_it) - return oidset_remove(filter_data->omits, &obj->oid); + return oidset_remove(omits, &obj->oid); else - return oidset_insert(filter_data->omits, &obj->oid); + return oidset_insert(omits, &obj->oid); } static enum list_objects_filter_result filter_trees_depth( @@ -128,6 +154,7 @@ static enum list_objects_filter_result filter_trees_depth( struct object *obj, const char *pathname, const char *filename, + struct oidset *omits, void *filter_data_) { struct filter_trees_depth_data *filter_data = filter_data_; @@ -152,7 +179,7 @@ static enum list_objects_filter_result filter_trees_depth( return LOFR_ZERO; case LOFS_BLOB: - filter_trees_update_omits(obj, filter_data, include_it); + filter_trees_update_omits(obj, omits, include_it); return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO; case LOFS_BEGIN_TREE: @@ -173,12 +200,12 @@ static enum list_objects_filter_result filter_trees_depth( filter_res = LOFR_SKIP_TREE; } else { int been_omitted = filter_trees_update_omits( - obj, filter_data, include_it); + obj, omits, include_it); seen_info->depth = filter_data->current_depth; if (include_it) filter_res = LOFR_DO_SHOW; - else if (filter_data->omits && !been_omitted) + else if (omits && !been_omitted) /* * Must update omit information of children * recursively; they have not been omitted yet. @@ -201,21 +228,18 @@ static void filter_trees_free(void *filter_data) { free(d); } -static void *filter_trees_depth__init( - struct oidset *omitted, +static void filter_trees_depth__init( struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn) + struct filter *filter) { struct filter_trees_depth_data *d = xcalloc(1, sizeof(*d)); - d->omits = omitted; oidmap_init(&d->seen_at_depth, 0); d->exclude_depth = filter_options->tree_exclude_depth; d->current_depth = 0; - *filter_fn = filter_trees_depth; - *filter_free_fn = filter_trees_free; - return d; + filter->filter_data = d; + filter->filter_object_fn = filter_trees_depth; + filter->free_fn = filter_trees_free; } /* @@ -223,7 +247,6 @@ static void *filter_trees_depth__init( * And to OPTIONALLY collect a list of the omitted OIDs. */ struct filter_blobs_limit_data { - struct oidset *omits; unsigned long max_bytes; }; @@ -233,6 +256,7 @@ static enum list_objects_filter_result filter_blobs_limit( struct object *obj, const char *pathname, const char *filename, + struct oidset *omits, void *filter_data_) { struct filter_blobs_limit_data *filter_data = filter_data_; @@ -270,30 +294,27 @@ static enum list_objects_filter_result filter_blobs_limit( if (object_length < filter_data->max_bytes) goto include_it; - if (filter_data->omits) - oidset_insert(filter_data->omits, &obj->oid); + if (omits) + oidset_insert(omits, &obj->oid); return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */ } include_it: - if (filter_data->omits) - oidset_remove(filter_data->omits, &obj->oid); + if (omits) + oidset_remove(omits, &obj->oid); return LOFR_MARK_SEEN | LOFR_DO_SHOW; } -static void *filter_blobs_limit__init( - struct oidset *omitted, +static void filter_blobs_limit__init( struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn) + struct filter *filter) { struct filter_blobs_limit_data *d = xcalloc(1, sizeof(*d)); - d->omits = omitted; d->max_bytes = filter_options->blob_limit_value; - *filter_fn = filter_blobs_limit; - *filter_free_fn = free; - return d; + filter->filter_data = d; + filter->filter_object_fn = filter_blobs_limit; + filter->free_fn = free; } /* @@ -307,12 +328,12 @@ static void *filter_blobs_limit__init( */ struct frame { /* - * defval is the usual default include/exclude value that + * default_match is the usual default include/exclude value that * should be inherited as we recurse into directories based * upon pattern matching of the directory itself or of a * containing directory. */ - int defval; + enum pattern_match_result default_match; /* * 1 if the directory (recursively) contains any provisionally @@ -326,8 +347,7 @@ struct frame { }; struct filter_sparse_data { - struct oidset *omits; - struct exclude_list el; + struct pattern_list pl; size_t nr, alloc; struct frame *array_frame; @@ -339,11 +359,13 @@ static enum list_objects_filter_result filter_sparse( struct object *obj, const char *pathname, const char *filename, + struct oidset *omits, void *filter_data_) { struct filter_sparse_data *filter_data = filter_data_; - int val, dtype; + int dtype; struct frame *frame; + enum pattern_match_result match; switch (filter_situation) { default: @@ -352,15 +374,15 @@ static enum list_objects_filter_result filter_sparse( case LOFS_BEGIN_TREE: assert(obj->type == OBJ_TREE); dtype = DT_DIR; - val = is_excluded_from_list(pathname, strlen(pathname), - filename, &dtype, &filter_data->el, - r->index); - if (val < 0) - val = filter_data->array_frame[filter_data->nr - 1].defval; + match = path_matches_pattern_list(pathname, strlen(pathname), + filename, &dtype, &filter_data->pl, + r->index); + if (match == UNDECIDED) + match = filter_data->array_frame[filter_data->nr - 1].default_match; ALLOC_GROW(filter_data->array_frame, filter_data->nr + 1, filter_data->alloc); - filter_data->array_frame[filter_data->nr].defval = val; + filter_data->array_frame[filter_data->nr].default_match = match; filter_data->array_frame[filter_data->nr].child_prov_omit = 0; filter_data->nr++; @@ -414,14 +436,14 @@ static enum list_objects_filter_result filter_sparse( frame = &filter_data->array_frame[filter_data->nr - 1]; dtype = DT_REG; - val = is_excluded_from_list(pathname, strlen(pathname), - filename, &dtype, &filter_data->el, + match = path_matches_pattern_list(pathname, strlen(pathname), + filename, &dtype, &filter_data->pl, r->index); - if (val < 0) - val = frame->defval; - if (val > 0) { - if (filter_data->omits) - oidset_remove(filter_data->omits, &obj->oid); + if (match == UNDECIDED) + match = frame->default_match; + if (match == MATCHED) { + if (omits) + oidset_remove(omits, &obj->oid); return LOFR_MARK_SEEN | LOFR_DO_SHOW; } @@ -435,8 +457,8 @@ static enum list_objects_filter_result filter_sparse( * Leave the LOFR_ bits unset so that if the blob appears * again in the traversal, we will be asked again. */ - if (filter_data->omits) - oidset_insert(filter_data->omits, &obj->oid); + if (omits) + oidset_insert(omits, &obj->oid); /* * Remember that at least 1 blob in this tree was @@ -456,33 +478,161 @@ static void filter_sparse_free(void *filter_data) free(d); } -static void *filter_sparse_oid__init( - struct oidset *omitted, +static void filter_sparse_oid__init( struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn) + struct filter *filter) { struct filter_sparse_data *d = xcalloc(1, sizeof(*d)); - d->omits = omitted; - if (add_excludes_from_blob_to_list(filter_options->sparse_oid_value, - NULL, 0, &d->el) < 0) + if (add_patterns_from_blob_to_list(filter_options->sparse_oid_value, + NULL, 0, &d->pl) < 0) die("could not load filter specification"); ALLOC_GROW(d->array_frame, d->nr + 1, d->alloc); - d->array_frame[d->nr].defval = 0; /* default to include */ + d->array_frame[d->nr].default_match = 0; /* default to include */ d->array_frame[d->nr].child_prov_omit = 0; d->nr++; - *filter_fn = filter_sparse; - *filter_free_fn = filter_sparse_free; - return d; + filter->filter_data = d; + filter->filter_object_fn = filter_sparse; + filter->free_fn = filter_sparse_free; } -typedef void *(*filter_init_fn)( - struct oidset *omitted, +/* A filter which only shows objects shown by all sub-filters. */ +struct combine_filter_data { + struct subfilter *sub; + size_t nr; +}; + +static enum list_objects_filter_result process_subfilter( + struct repository *r, + enum list_objects_filter_situation filter_situation, + struct object *obj, + const char *pathname, + const char *filename, + struct subfilter *sub) +{ + enum list_objects_filter_result result; + + /* + * Check and update is_skipping_tree before oidset_contains so + * that is_skipping_tree gets unset even when the object is + * marked as seen. As of this writing, no filter uses + * LOFR_MARK_SEEN on trees that also uses LOFR_SKIP_TREE, so the + * ordering is only theoretically important. Be cautious if you + * change the order of the below checks and more filters have + * been added! + */ + if (sub->is_skipping_tree) { + if (filter_situation == LOFS_END_TREE && + oideq(&obj->oid, &sub->skip_tree)) + sub->is_skipping_tree = 0; + else + return LOFR_ZERO; + } + if (oidset_contains(&sub->seen, &obj->oid)) + return LOFR_ZERO; + + result = list_objects_filter__filter_object( + r, filter_situation, obj, pathname, filename, sub->filter); + + if (result & LOFR_MARK_SEEN) + oidset_insert(&sub->seen, &obj->oid); + + if (result & LOFR_SKIP_TREE) { + sub->is_skipping_tree = 1; + sub->skip_tree = obj->oid; + } + + return result; +} + +static enum list_objects_filter_result filter_combine( + struct repository *r, + enum list_objects_filter_situation filter_situation, + struct object *obj, + const char *pathname, + const char *filename, + struct oidset *omits, + void *filter_data) +{ + struct combine_filter_data *d = filter_data; + enum list_objects_filter_result combined_result = + LOFR_DO_SHOW | LOFR_MARK_SEEN | LOFR_SKIP_TREE; + size_t sub; + + for (sub = 0; sub < d->nr; sub++) { + enum list_objects_filter_result sub_result = process_subfilter( + r, filter_situation, obj, pathname, filename, + &d->sub[sub]); + if (!(sub_result & LOFR_DO_SHOW)) + combined_result &= ~LOFR_DO_SHOW; + if (!(sub_result & LOFR_MARK_SEEN)) + combined_result &= ~LOFR_MARK_SEEN; + if (!d->sub[sub].is_skipping_tree) + combined_result &= ~LOFR_SKIP_TREE; + } + + return combined_result; +} + +static void filter_combine__free(void *filter_data) +{ + struct combine_filter_data *d = filter_data; + size_t sub; + for (sub = 0; sub < d->nr; sub++) { + list_objects_filter__free(d->sub[sub].filter); + oidset_clear(&d->sub[sub].seen); + if (d->sub[sub].omits.set.size) + BUG("expected oidset to be cleared already"); + } + free(d->sub); +} + +static void add_all(struct oidset *dest, struct oidset *src) { + struct oidset_iter iter; + struct object_id *src_oid; + + oidset_iter_init(src, &iter); + while ((src_oid = oidset_iter_next(&iter)) != NULL) + oidset_insert(dest, src_oid); +} + +static void filter_combine__finalize_omits( + struct oidset *omits, + void *filter_data) +{ + struct combine_filter_data *d = filter_data; + size_t sub; + + for (sub = 0; sub < d->nr; sub++) { + add_all(omits, &d->sub[sub].omits); + oidset_clear(&d->sub[sub].omits); + } +} + +static void filter_combine__init( struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn); + struct filter* filter) +{ + struct combine_filter_data *d = xcalloc(1, sizeof(*d)); + size_t sub; + + d->nr = filter_options->sub_nr; + d->sub = xcalloc(d->nr, sizeof(*d->sub)); + for (sub = 0; sub < d->nr; sub++) + d->sub[sub].filter = list_objects_filter__init( + filter->omits ? &d->sub[sub].omits : NULL, + &filter_options->sub[sub]); + + filter->filter_data = d; + filter->filter_object_fn = filter_combine; + filter->free_fn = filter_combine__free; + filter->finalize_omits_fn = filter_combine__finalize_omits; +} + +typedef void (*filter_init_fn)( + struct list_objects_filter_options *filter_options, + struct filter *filter); /* * Must match "enum list_objects_filter_choice". @@ -493,14 +643,14 @@ static filter_init_fn s_filters[] = { filter_blobs_limit__init, filter_trees_depth__init, filter_sparse_oid__init, + filter_combine__init, }; -void *list_objects_filter__init( +struct filter *list_objects_filter__init( struct oidset *omitted, - struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn) + struct list_objects_filter_options *filter_options) { + struct filter *filter; filter_init_fn init_fn; assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT); @@ -510,10 +660,44 @@ void *list_objects_filter__init( filter_options->choice); init_fn = s_filters[filter_options->choice]; - if (init_fn) - return init_fn(omitted, filter_options, - filter_fn, filter_free_fn); - *filter_fn = NULL; - *filter_free_fn = NULL; - return NULL; + if (!init_fn) + return NULL; + + filter = xcalloc(1, sizeof(*filter)); + filter->omits = omitted; + init_fn(filter_options, filter); + return filter; +} + +enum list_objects_filter_result list_objects_filter__filter_object( + struct repository *r, + enum list_objects_filter_situation filter_situation, + struct object *obj, + const char *pathname, + const char *filename, + struct filter *filter) +{ + if (filter && (obj->flags & NOT_USER_GIVEN)) + return filter->filter_object_fn(r, filter_situation, obj, + pathname, filename, + filter->omits, + filter->filter_data); + /* + * No filter is active or user gave object explicitly. In this case, + * always show the object (except when LOFS_END_TREE, since this tree + * had already been shown when LOFS_BEGIN_TREE). + */ + if (filter_situation == LOFS_END_TREE) + return 0; + return LOFR_MARK_SEEN | LOFR_DO_SHOW; +} + +void list_objects_filter__free(struct filter *filter) +{ + if (!filter) + return; + if (filter->finalize_omits_fn && filter->omits) + filter->finalize_omits_fn(filter->omits, filter->filter_data); + filter->free_fn(filter->filter_data); + free(filter); } diff --git a/list-objects-filter.h b/list-objects-filter.h index 1d45a4ad57..cfd784e203 100644 --- a/list-objects-filter.h +++ b/list-objects-filter.h @@ -60,30 +60,36 @@ enum list_objects_filter_situation { LOFS_BLOB }; -typedef enum list_objects_filter_result (*filter_object_fn)( +struct filter; + +/* + * Constructor for the set of defined list-objects filters. + * The `omitted` set is optional. It is populated with objects that the + * filter excludes. This set should not be considered finalized until + * after list_objects_filter__free is called on the returned `struct + * filter *`. + */ +struct filter *list_objects_filter__init( + struct oidset *omitted, + struct list_objects_filter_options *filter_options); + +/* + * Lets `filter` decide how to handle the `obj`. If `filter` is NULL, this + * function behaves as expected if no filter is configured: all objects are + * included. + */ +enum list_objects_filter_result list_objects_filter__filter_object( struct repository *r, enum list_objects_filter_situation filter_situation, struct object *obj, const char *pathname, const char *filename, - void *filter_data); - -typedef void (*filter_free_fn)(void *filter_data); + struct filter *filter); /* - * Constructor for the set of defined list-objects filters. - * Returns a generic "void *filter_data". - * - * The returned "filter_fn" will be used by traverse_commit_list() - * to filter the results. - * - * The returned "filter_free_fn" is a destructor for the - * filter_data. + * Destroys `filter` and finalizes the `omitted` set, if present. Does + * nothing if `filter` is null. */ -void *list_objects_filter__init( - struct oidset *omitted, - struct list_objects_filter_options *filter_options, - filter_object_fn *filter_fn, - filter_free_fn *filter_free_fn); +void list_objects_filter__free(struct filter *filter); #endif /* LIST_OBJECTS_FILTER_H */ diff --git a/list-objects.c b/list-objects.c index b5651ddd5b..9307d91fb3 100644 --- a/list-objects.c +++ b/list-objects.c @@ -18,8 +18,7 @@ struct traversal_context { show_object_fn show_object; show_commit_fn show_commit; void *show_data; - filter_object_fn filter_fn; - void *filter_data; + struct filter *filter; }; static void process_blob(struct traversal_context *ctx, @@ -29,7 +28,7 @@ static void process_blob(struct traversal_context *ctx, { struct object *obj = &blob->object; size_t pathlen; - enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW; + enum list_objects_filter_result r; if (!ctx->revs->blob_objects) return; @@ -54,11 +53,10 @@ static void process_blob(struct traversal_context *ctx, pathlen = path->len; strbuf_addstr(path, name); - if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) - r = ctx->filter_fn(ctx->revs->repo, - LOFS_BLOB, obj, - path->buf, &path->buf[pathlen], - ctx->filter_data); + r = list_objects_filter__filter_object(ctx->revs->repo, + LOFS_BLOB, obj, + path->buf, &path->buf[pathlen], + ctx->filter); if (r & LOFR_MARK_SEEN) obj->flags |= SEEN; if (r & LOFR_DO_SHOW) @@ -157,7 +155,7 @@ static void process_tree(struct traversal_context *ctx, struct object *obj = &tree->object; struct rev_info *revs = ctx->revs; int baselen = base->len; - enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW; + enum list_objects_filter_result r; int failed_parse; if (!revs->tree_objects) @@ -186,11 +184,10 @@ static void process_tree(struct traversal_context *ctx, } strbuf_addstr(base, name); - if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) - r = ctx->filter_fn(ctx->revs->repo, - LOFS_BEGIN_TREE, obj, - base->buf, &base->buf[baselen], - ctx->filter_data); + r = list_objects_filter__filter_object(ctx->revs->repo, + LOFS_BEGIN_TREE, obj, + base->buf, &base->buf[baselen], + ctx->filter); if (r & LOFR_MARK_SEEN) obj->flags |= SEEN; if (r & LOFR_DO_SHOW) @@ -203,16 +200,14 @@ static void process_tree(struct traversal_context *ctx, else if (!failed_parse) process_tree_contents(ctx, tree, base); - if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) { - r = ctx->filter_fn(ctx->revs->repo, - LOFS_END_TREE, obj, - base->buf, &base->buf[baselen], - ctx->filter_data); - if (r & LOFR_MARK_SEEN) - obj->flags |= SEEN; - if (r & LOFR_DO_SHOW) - ctx->show_object(obj, base->buf, ctx->show_data); - } + r = list_objects_filter__filter_object(ctx->revs->repo, + LOFS_END_TREE, obj, + base->buf, &base->buf[baselen], + ctx->filter); + if (r & LOFR_MARK_SEEN) + obj->flags |= SEEN; + if (r & LOFR_DO_SHOW) + ctx->show_object(obj, base->buf, ctx->show_data); strbuf_setlen(base, baselen); free_tree_buffer(tree); @@ -402,8 +397,7 @@ void traverse_commit_list(struct rev_info *revs, ctx.show_commit = show_commit; ctx.show_object = show_object; ctx.show_data = show_data; - ctx.filter_fn = NULL; - ctx.filter_data = NULL; + ctx.filter = NULL; do_traverse(&ctx); } @@ -416,17 +410,12 @@ void traverse_commit_list_filtered( struct oidset *omitted) { struct traversal_context ctx; - filter_free_fn filter_free_fn = NULL; ctx.revs = revs; ctx.show_object = show_object; ctx.show_commit = show_commit; ctx.show_data = show_data; - ctx.filter_fn = NULL; - - ctx.filter_data = list_objects_filter__init(omitted, filter_options, - &ctx.filter_fn, &filter_free_fn); + ctx.filter = list_objects_filter__init(omitted, filter_options); do_traverse(&ctx); - if (ctx.filter_data && filter_free_fn) - filter_free_fn(ctx.filter_data); + list_objects_filter__free(ctx.filter); } diff --git a/ll-merge.c b/ll-merge.c index 5b8d46aede..d65a8971db 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -32,6 +32,20 @@ struct ll_merge_driver { char *cmdline; }; +static struct attr_check *merge_attributes; +static struct attr_check *load_merge_attributes(void) +{ + if (!merge_attributes) + merge_attributes = attr_check_initl("merge", "conflict-marker-size", NULL); + return merge_attributes; +} + +void reset_merge_attributes(void) +{ + attr_check_free(merge_attributes); + merge_attributes = NULL; +} + /* * Built-in low-levels */ @@ -354,7 +368,7 @@ int ll_merge(mmbuffer_t *result_buf, struct index_state *istate, const struct ll_merge_options *opts) { - static struct attr_check *check; + struct attr_check *check = load_merge_attributes(); static const struct ll_merge_options default_opts; const char *ll_driver_name = NULL; int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; @@ -369,9 +383,6 @@ int ll_merge(mmbuffer_t *result_buf, normalize_file(theirs, path, istate); } - if (!check) - check = attr_check_initl("merge", "conflict-marker-size", NULL); - git_check_attr(istate, path, check); ll_driver_name = check->items[0].value; if (check->items[1].value) { diff --git a/ll-merge.h b/ll-merge.h index b9e2af1c88..e78973dd55 100644 --- a/ll-merge.h +++ b/ll-merge.h @@ -26,5 +26,6 @@ int ll_merge(mmbuffer_t *result_buf, const struct ll_merge_options *opts); int ll_merge_marker_size(struct index_state *istate, const char *path); +void reset_merge_attributes(void); #endif diff --git a/log-tree.c b/log-tree.c index 1e56df62a7..109c212224 100644 --- a/log-tree.c +++ b/log-tree.c @@ -677,9 +677,7 @@ void show_log(struct rev_info *opt) raw = (opt->commit_format == CMIT_FMT_USERFORMAT); format_display_notes(&commit->object.oid, ¬ebuf, get_log_output_encoding(), raw); - ctx.notes_message = notebuf.len - ? strbuf_detach(¬ebuf, NULL) - : xcalloc(1, 1); + ctx.notes_message = strbuf_detach(¬ebuf, NULL); } /* @@ -269,8 +269,10 @@ static int note_tree_insert(struct notes_tree *t, struct int_node *tree, case PTR_TYPE_NOTE: if (oideq(&l->key_oid, &entry->key_oid)) { /* skip concatenation if l == entry */ - if (oideq(&l->val_oid, &entry->val_oid)) + if (oideq(&l->val_oid, &entry->val_oid)) { + free(entry); return 0; + } ret = combine_notes(&l->val_oid, &entry->val_oid); @@ -458,7 +460,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, die("Failed to load %s %s into notes tree " "from %s", type == PTR_TYPE_NOTE ? "note" : "subtree", - oid_to_hex(&l->key_oid), t->ref); + oid_to_hex(&object_oid), t->ref); continue; diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index fa78a460c9..a7a4964b50 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -144,7 +144,7 @@ static inline void reset_all_seen(void) static uint32_t find_object_pos(const struct object_id *oid) { - struct object_entry *entry = packlist_find(writer.to_pack, oid, NULL); + struct object_entry *entry = packlist_find(writer.to_pack, oid); if (!entry) { die("Failed to write bitmap index. Packfile doesn't have full closure " diff --git a/pack-bitmap.c b/pack-bitmap.c index ed2befaac6..e07c798879 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -709,9 +709,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs) else object_list_insert(object, &wants); - if (!tag->tagged) - die("bad tag"); - object = parse_object_or_die(&tag->tagged->oid, NULL); + object = parse_object_or_die(get_tagged_oid(tag), NULL); } if (object->flags & UNINTERESTING) @@ -1063,7 +1061,7 @@ int rebuild_existing_bitmaps(struct bitmap_index *bitmap_git, entry = &bitmap_git->pack->revindex[i]; nth_packed_object_oid(&oid, bitmap_git->pack, entry->nr); - oe = packlist_find(mapping, &oid, NULL); + oe = packlist_find(mapping, &oid); if (oe) reposition[i] = oe_in_pack_pos(mapping, oe) + 1; diff --git a/pack-objects.c b/pack-objects.c index 52560293b6..c6250d77f4 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -68,8 +68,7 @@ static void rehash_objects(struct packing_data *pdata) } struct object_entry *packlist_find(struct packing_data *pdata, - const struct object_id *oid, - uint32_t *index_pos) + const struct object_id *oid) { uint32_t i; int found; @@ -79,9 +78,6 @@ struct object_entry *packlist_find(struct packing_data *pdata, i = locate_object_entry_hash(pdata, oid, &found); - if (index_pos) - *index_pos = i; - if (!found) return NULL; @@ -153,8 +149,7 @@ void prepare_packing_data(struct repository *r, struct packing_data *pdata) } struct object_entry *packlist_alloc(struct packing_data *pdata, - const unsigned char *sha1, - uint32_t index_pos) + const struct object_id *oid) { struct object_entry *new_entry; @@ -177,12 +172,19 @@ struct object_entry *packlist_alloc(struct packing_data *pdata, new_entry = pdata->objects + pdata->nr_objects++; memset(new_entry, 0, sizeof(*new_entry)); - hashcpy(new_entry->idx.oid.hash, sha1); + oidcpy(&new_entry->idx.oid, oid); if (pdata->index_size * 3 <= pdata->nr_objects * 4) rehash_objects(pdata); - else - pdata->index[index_pos] = pdata->nr_objects; + else { + int found; + uint32_t pos = locate_object_entry_hash(pdata, + &new_entry->idx.oid, + &found); + if (found) + BUG("duplicate object inserted into hash"); + pdata->index[pos] = pdata->nr_objects; + } if (pdata->in_pack) pdata->in_pack[pdata->nr_objects - 1] = NULL; diff --git a/pack-objects.h b/pack-objects.h index 857d43850b..6fe6ae5ee8 100644 --- a/pack-objects.h +++ b/pack-objects.h @@ -183,12 +183,10 @@ static inline void packing_data_unlock(struct packing_data *pdata) } struct object_entry *packlist_alloc(struct packing_data *pdata, - const unsigned char *sha1, - uint32_t index_pos); + const struct object_id *oid); struct object_entry *packlist_find(struct packing_data *pdata, - const struct object_id *oid, - uint32_t *index_pos); + const struct object_id *oid); static inline uint32_t pack_name_hash(const char *name) { diff --git a/packfile.c b/packfile.c index fc43a6c52c..f3f962af4c 100644 --- a/packfile.c +++ b/packfile.c @@ -17,6 +17,7 @@ #include "object-store.h" #include "midx.h" #include "commit-graph.h" +#include "promisor-remote.h" char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, @@ -287,13 +288,6 @@ static int unuse_one_window(struct packed_git *current) return 0; } -void release_pack_memory(size_t need) -{ - size_t cur = pack_mapped; - while (need >= (cur - pack_mapped) && unuse_one_window(NULL)) - ; /* nothing */ -} - void close_pack_windows(struct packed_git *p) { while (p->windows) { @@ -710,23 +704,12 @@ void unuse_pack(struct pack_window **w_cursor) } } -static void try_to_free_pack_memory(size_t size) -{ - release_pack_memory(size); -} - struct packed_git *add_packed_git(const char *path, size_t path_len, int local) { - static int have_set_try_to_free_routine; struct stat st; size_t alloc; struct packed_git *p; - if (!have_set_try_to_free_routine) { - have_set_try_to_free_routine = 1; - set_try_to_free_routine(try_to_free_pack_memory); - } - /* * Make sure a corresponding .pack file exists and that * the index looks sane. @@ -2139,7 +2122,7 @@ static int add_promisor_object(const struct object_id *oid, oidset_insert(set, &parents->item->object.oid); } else if (obj->type == OBJ_TAG) { struct tag *tag = (struct tag *) obj; - oidset_insert(set, &tag->tagged->oid); + oidset_insert(set, get_tagged_oid(tag)); } return 0; } @@ -2150,7 +2133,7 @@ int is_promisor_object(const struct object_id *oid) static int promisor_objects_prepared; if (!promisor_objects_prepared) { - if (repository_format_partial_clone) { + if (has_promisor_remote()) { for_each_packed_object(add_promisor_object, &promisor_objects, FOR_EACH_OBJECT_PROMISOR_ONLY); diff --git a/packfile.h b/packfile.h index 3e98910bdd..fc7904ec81 100644 --- a/packfile.h +++ b/packfile.h @@ -100,7 +100,7 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local); * Does not unlink if 'force_delete' is false and the pack-file is * marked as ".keep". */ -extern void unlink_pack_path(const char *pack_name, int force_delete); +void unlink_pack_path(const char *pack_name, int force_delete); /* * Make sure that a pointer access into an mmap'd index file is within bounds, diff --git a/parse-options.c b/parse-options.c index 87b26a1d92..b42f54d48b 100644 --- a/parse-options.c +++ b/parse-options.c @@ -780,7 +780,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, continue; } - if (!arg[2]) { /* "--" */ + if (!arg[2] /* "--" */ || + !strcmp(arg + 2, "end-of-options")) { if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { ctx->argc--; ctx->argv++; diff --git a/parse-options.h b/parse-options.h index a4bd40bb6a..38a33a087e 100644 --- a/parse-options.h +++ b/parse-options.h @@ -46,6 +46,15 @@ enum parse_opt_option_flags { PARSE_OPT_COMP_ARG = 1024 }; +enum parse_opt_result { + PARSE_OPT_COMPLETE = -3, + PARSE_OPT_HELP = -2, + PARSE_OPT_ERROR = -1, /* must be the same as error() */ + PARSE_OPT_DONE = 0, /* fixed so that "return 0" works */ + PARSE_OPT_NON_OPTION, + PARSE_OPT_UNKNOWN +}; + struct option; typedef int parse_opt_cb(const struct option *, const char *arg, int unset); @@ -241,15 +250,6 @@ const char *optname(const struct option *opt, int flags); /*----- incremental advanced APIs -----*/ -enum parse_opt_result { - PARSE_OPT_COMPLETE = -3, - PARSE_OPT_HELP = -2, - PARSE_OPT_ERROR = -1, /* must be the same as error() */ - PARSE_OPT_DONE = 0, /* fixed so that "return 0" works */ - PARSE_OPT_NON_OPTION, - PARSE_OPT_UNKNOWN -}; - /* * It's okay for the caller to consume argv/argc in the usual way. * Other fields of that structure are private to parse-options and should not @@ -1221,31 +1221,52 @@ static inline int chomp_trailing_dir_sep(const char *path, int len) } /* - * If path ends with suffix (complete path components), returns the - * part before suffix (sans trailing directory separators). - * Otherwise returns NULL. + * If path ends with suffix (complete path components), returns the offset of + * the last character in the path before the suffix (sans trailing directory + * separators), and -1 otherwise. */ -char *strip_path_suffix(const char *path, const char *suffix) +static ssize_t stripped_path_suffix_offset(const char *path, const char *suffix) { int path_len = strlen(path), suffix_len = strlen(suffix); while (suffix_len) { if (!path_len) - return NULL; + return -1; if (is_dir_sep(path[path_len - 1])) { if (!is_dir_sep(suffix[suffix_len - 1])) - return NULL; + return -1; path_len = chomp_trailing_dir_sep(path, path_len); suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); } else if (path[--path_len] != suffix[--suffix_len]) - return NULL; + return -1; } if (path_len && !is_dir_sep(path[path_len - 1])) - return NULL; - return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); + return -1; + return chomp_trailing_dir_sep(path, path_len); +} + +/* + * Returns true if the path ends with components, considering only complete path + * components, and false otherwise. + */ +int ends_with_path_components(const char *path, const char *components) +{ + return stripped_path_suffix_offset(path, components) != -1; +} + +/* + * If path ends with suffix (complete path components), returns the + * part before suffix (sans trailing directory separators). + * Otherwise returns NULL. + */ +char *strip_path_suffix(const char *path, const char *suffix) +{ + ssize_t offset = stripped_path_suffix_offset(path, suffix); + + return offset == -1 ? NULL : xstrndup(path, offset); } int daemon_avoid_alias(const char *p) @@ -193,4 +193,7 @@ const char *git_path_merge_head(struct repository *r); const char *git_path_fetch_head(struct repository *r); const char *git_path_shallow(struct repository *r); + +int ends_with_path_components(const char *path, const char *components); + #endif /* PATH_H */ diff --git a/promisor-remote.c b/promisor-remote.c new file mode 100644 index 0000000000..9bc296cdde --- /dev/null +++ b/promisor-remote.c @@ -0,0 +1,265 @@ +#include "cache.h" +#include "object-store.h" +#include "promisor-remote.h" +#include "config.h" +#include "transport.h" + +static char *repository_format_partial_clone; +static const char *core_partial_clone_filter_default; + +void set_repository_format_partial_clone(char *partial_clone) +{ + repository_format_partial_clone = xstrdup_or_null(partial_clone); +} + +static int fetch_refs(const char *remote_name, struct ref *ref) +{ + struct remote *remote; + struct transport *transport; + int original_fetch_if_missing = fetch_if_missing; + int res; + + fetch_if_missing = 0; + remote = remote_get(remote_name); + if (!remote->url[0]) + die(_("Remote with no URL")); + transport = transport_get(remote, remote->url[0]); + + transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); + transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1"); + res = transport_fetch_refs(transport, ref); + fetch_if_missing = original_fetch_if_missing; + + return res; +} + +static int fetch_objects(const char *remote_name, + const struct object_id *oids, + int oid_nr) +{ + struct ref *ref = NULL; + int i; + + for (i = 0; i < oid_nr; i++) { + struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i])); + oidcpy(&new_ref->old_oid, &oids[i]); + new_ref->exact_oid = 1; + new_ref->next = ref; + ref = new_ref; + } + return fetch_refs(remote_name, ref); +} + +static struct promisor_remote *promisors; +static struct promisor_remote **promisors_tail = &promisors; + +static struct promisor_remote *promisor_remote_new(const char *remote_name) +{ + struct promisor_remote *r; + + if (*remote_name == '/') { + warning(_("promisor remote name cannot begin with '/': %s"), + remote_name); + return NULL; + } + + FLEX_ALLOC_STR(r, name, remote_name); + + *promisors_tail = r; + promisors_tail = &r->next; + + return r; +} + +static struct promisor_remote *promisor_remote_lookup(const char *remote_name, + struct promisor_remote **previous) +{ + struct promisor_remote *r, *p; + + for (p = NULL, r = promisors; r; p = r, r = r->next) + if (!strcmp(r->name, remote_name)) { + if (previous) + *previous = p; + return r; + } + + return NULL; +} + +static void promisor_remote_move_to_tail(struct promisor_remote *r, + struct promisor_remote *previous) +{ + if (previous) + previous->next = r->next; + else + promisors = r->next ? r->next : r; + r->next = NULL; + *promisors_tail = r; + promisors_tail = &r->next; +} + +static int promisor_remote_config(const char *var, const char *value, void *data) +{ + const char *name; + int namelen; + const char *subkey; + + if (!strcmp(var, "core.partialclonefilter")) + return git_config_string(&core_partial_clone_filter_default, + var, value); + + if (parse_config_key(var, "remote", &name, &namelen, &subkey) < 0) + return 0; + + if (!strcmp(subkey, "promisor")) { + char *remote_name; + + if (!git_config_bool(var, value)) + return 0; + + remote_name = xmemdupz(name, namelen); + + if (!promisor_remote_lookup(remote_name, NULL)) + promisor_remote_new(remote_name); + + free(remote_name); + return 0; + } + if (!strcmp(subkey, "partialclonefilter")) { + struct promisor_remote *r; + char *remote_name = xmemdupz(name, namelen); + + r = promisor_remote_lookup(remote_name, NULL); + if (!r) + r = promisor_remote_new(remote_name); + + free(remote_name); + + if (!r) + return 0; + + return git_config_string(&r->partial_clone_filter, var, value); + } + + return 0; +} + +static int initialized; + +static void promisor_remote_init(void) +{ + if (initialized) + return; + initialized = 1; + + git_config(promisor_remote_config, NULL); + + if (repository_format_partial_clone) { + struct promisor_remote *o, *previous; + + o = promisor_remote_lookup(repository_format_partial_clone, + &previous); + if (o) + promisor_remote_move_to_tail(o, previous); + else + promisor_remote_new(repository_format_partial_clone); + } +} + +static void promisor_remote_clear(void) +{ + while (promisors) { + struct promisor_remote *r = promisors; + promisors = promisors->next; + free(r); + } + + promisors_tail = &promisors; +} + +void promisor_remote_reinit(void) +{ + initialized = 0; + promisor_remote_clear(); + promisor_remote_init(); +} + +struct promisor_remote *promisor_remote_find(const char *remote_name) +{ + promisor_remote_init(); + + if (!remote_name) + return promisors; + + return promisor_remote_lookup(remote_name, NULL); +} + +int has_promisor_remote(void) +{ + return !!promisor_remote_find(NULL); +} + +static int remove_fetched_oids(struct repository *repo, + struct object_id **oids, + int oid_nr, int to_free) +{ + int i, remaining_nr = 0; + int *remaining = xcalloc(oid_nr, sizeof(*remaining)); + struct object_id *old_oids = *oids; + struct object_id *new_oids; + + for (i = 0; i < oid_nr; i++) + if (oid_object_info_extended(repo, &old_oids[i], NULL, + OBJECT_INFO_SKIP_FETCH_OBJECT)) { + remaining[i] = 1; + remaining_nr++; + } + + if (remaining_nr) { + int j = 0; + new_oids = xcalloc(remaining_nr, sizeof(*new_oids)); + for (i = 0; i < oid_nr; i++) + if (remaining[i]) + oidcpy(&new_oids[j++], &old_oids[i]); + *oids = new_oids; + if (to_free) + free(old_oids); + } + + free(remaining); + + return remaining_nr; +} + +int promisor_remote_get_direct(struct repository *repo, + const struct object_id *oids, + int oid_nr) +{ + struct promisor_remote *r; + struct object_id *remaining_oids = (struct object_id *)oids; + int remaining_nr = oid_nr; + int to_free = 0; + int res = -1; + + promisor_remote_init(); + + for (r = promisors; r; r = r->next) { + if (fetch_objects(r->name, remaining_oids, remaining_nr) < 0) { + if (remaining_nr == 1) + continue; + remaining_nr = remove_fetched_oids(repo, &remaining_oids, + remaining_nr, to_free); + if (remaining_nr) { + to_free = 1; + continue; + } + } + res = 0; + break; + } + + if (to_free) + free(remaining_oids); + + return res; +} diff --git a/promisor-remote.h b/promisor-remote.h new file mode 100644 index 0000000000..8200dfc940 --- /dev/null +++ b/promisor-remote.h @@ -0,0 +1,31 @@ +#ifndef PROMISOR_REMOTE_H +#define PROMISOR_REMOTE_H + +struct object_id; + +/* + * Promisor remote linked list + * + * Information in its fields come from remote.XXX config entries or + * from extensions.partialclone or core.partialclonefilter. + */ +struct promisor_remote { + struct promisor_remote *next; + const char *partial_clone_filter; + const char name[FLEX_ARRAY]; +}; + +extern void promisor_remote_reinit(void); +extern struct promisor_remote *promisor_remote_find(const char *remote_name); +extern int has_promisor_remote(void); +extern int promisor_remote_get_direct(struct repository *repo, + const struct object_id *oids, + int oid_nr); + +/* + * This should be used only once from setup.c to set the value we got + * from the extensions.partialclone config option. + */ +extern void set_repository_format_partial_clone(char *partial_clone); + +#endif /* PROMISOR_REMOTE_H */ @@ -84,12 +84,28 @@ void sq_quote_argv(struct strbuf *dst, const char **argv) } } +/* + * Legacy function to append each argv value, quoted as necessasry, + * with whitespace before each value. This results in a leading + * space in the result. + */ void sq_quote_argv_pretty(struct strbuf *dst, const char **argv) { + if (argv[0]) + strbuf_addch(dst, ' '); + sq_append_quote_argv_pretty(dst, argv); +} + +/* + * Append each argv value, quoted as necessary, with whitespace between them. + */ +void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv) +{ int i; for (i = 0; argv[i]; i++) { - strbuf_addch(dst, ' '); + if (i > 0) + strbuf_addch(dst, ' '); sq_quote_buf_pretty(dst, argv[i]); } } @@ -40,6 +40,7 @@ void sq_quotef(struct strbuf *, const char *fmt, ...); */ void sq_quote_buf_pretty(struct strbuf *, const char *src); void sq_quote_argv_pretty(struct strbuf *, const char **argv); +void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv); /* This unwraps what sq_quote() produces in place, but returns * NULL if the input does not look like what sq_quote would have diff --git a/read-cache.c b/read-cache.c index 52ffa8a313..cff1280975 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1599,16 +1599,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate, #define INDEX_FORMAT_DEFAULT 3 -static unsigned int get_index_format_default(void) +static unsigned int get_index_format_default(struct repository *r) { char *envversion = getenv("GIT_INDEX_VERSION"); char *endp; - int value; unsigned int version = INDEX_FORMAT_DEFAULT; if (!envversion) { - if (!git_config_get_int("index.version", &value)) - version = value; + prepare_repo_settings(r); + + if (r->settings.index_version >= 0) + version = r->settings.index_version; if (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) { warning(_("index.version set, but the value is invalid.\n" "Using version %i"), INDEX_FORMAT_DEFAULT); @@ -1844,18 +1845,17 @@ static void check_ce_order(struct index_state *istate) static void tweak_untracked_cache(struct index_state *istate) { - switch (git_config_get_untracked_cache()) { - case -1: /* keep: do nothing */ - break; - case 0: /* false */ + struct repository *r = the_repository; + + prepare_repo_settings(r); + + if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE) { remove_untracked_cache(istate); - break; - case 1: /* true */ - add_untracked_cache(istate); - break; - default: /* unknown value: do nothing */ - break; + return; } + + if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE) + add_untracked_cache(istate); } static void tweak_split_index(struct index_state *istate) @@ -2765,7 +2765,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, } if (!istate->version) { - istate->version = get_index_format_default(); + istate->version = get_index_format_default(the_repository); if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0)) init_split_index(istate); } diff --git a/ref-filter.c b/ref-filter.c index f27cfc8c3e..220e9bd74a 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1028,7 +1028,7 @@ static const char *copy_name(const char *buf) if (!strncmp(cp, " <", 2)) return xmemdupz(buf, cp - buf); } - return ""; + return xstrdup(""); } static const char *copy_email(const char *buf) @@ -1036,10 +1036,10 @@ static const char *copy_email(const char *buf) const char *email = strchr(buf, '<'); const char *eoemail; if (!email) - return ""; + return xstrdup(""); eoemail = strchr(email, '>'); if (!eoemail) - return ""; + return xstrdup(""); return xmemdupz(email, eoemail + 1 - email); } @@ -1766,7 +1766,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) * If it is a tag object, see if we use a value that derefs * the object, and if we do grab the object it refers to. */ - oi_deref.oid = ((struct tag *)obj)->tagged->oid; + oi_deref.oid = *get_tagged_oid((struct tag *)obj); /* * NEEDSWORK: This derefs tag only once, which @@ -1997,7 +1997,7 @@ static const struct object_id *match_points_at(struct oid_array *points_at, if (!obj) die(_("malformed object at '%s'"), refname); if (obj->type == OBJ_TAG) - tagged_oid = &((struct tag *)obj)->tagged->oid; + tagged_oid = get_tagged_oid((struct tag *)obj); if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0) return tagged_oid; return NULL; diff --git a/refs/packed-backend.c b/refs/packed-backend.c index c01c7f5901..4458a0f69c 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -1012,14 +1012,23 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err) } /* - * Now that we hold the `packed-refs` lock, make sure that our - * snapshot matches the current version of the file. Normally - * `get_snapshot()` does that for us, but that function - * assumes that when the file is locked, any existing snapshot - * is still valid. We've just locked the file, but it might - * have changed the moment *before* we locked it. + * There is a stat-validity problem might cause `update-ref -d` + * lost the newly commit of a ref, because a new `packed-refs` + * file might has the same on-disk file attributes such as + * timestamp, file size and inode value, but has a changed + * ref value. + * + * This could happen with a very small chance when + * `update-ref -d` is called and at the same time another + * `pack-refs --all` process is running. + * + * Now that we hold the `packed-refs` lock, it is important + * to make sure we could read the latest version of + * `packed-refs` file no matter we have just mmap it or not. + * So what need to do is clear the snapshot if we hold it + * already. */ - validate_snapshot(refs); + clear_snapshot(refs); /* * Now make sure that the packed-refs file as it exists in the diff --git a/repo-settings.c b/repo-settings.c new file mode 100644 index 0000000000..05546db98e --- /dev/null +++ b/repo-settings.c @@ -0,0 +1,68 @@ +#include "cache.h" +#include "config.h" +#include "repository.h" + +#define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } } while(0) + +void prepare_repo_settings(struct repository *r) +{ + int value; + char *strval; + + if (r->settings.initialized) + return; + + /* Defaults */ + memset(&r->settings, -1, sizeof(r->settings)); + + if (!repo_config_get_bool(r, "core.commitgraph", &value)) + r->settings.core_commit_graph = value; + if (!repo_config_get_bool(r, "gc.writecommitgraph", &value)) + r->settings.gc_write_commit_graph = value; + UPDATE_DEFAULT_BOOL(r->settings.core_commit_graph, 1); + UPDATE_DEFAULT_BOOL(r->settings.gc_write_commit_graph, 1); + + if (!repo_config_get_bool(r, "index.version", &value)) + r->settings.index_version = value; + if (!repo_config_get_maybe_bool(r, "core.untrackedcache", &value)) { + if (value == 0) + r->settings.core_untracked_cache = UNTRACKED_CACHE_REMOVE; + else + r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE; + } else if (!repo_config_get_string(r, "core.untrackedcache", &strval)) { + if (!strcasecmp(strval, "keep")) + r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP; + + free(strval); + } + + if (!repo_config_get_string(r, "fetch.negotiationalgorithm", &strval)) { + if (!strcasecmp(strval, "skipping")) + r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING; + else + r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT; + } + + if (!repo_config_get_bool(r, "pack.usesparse", &value)) + r->settings.pack_use_sparse = value; + if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) { + UPDATE_DEFAULT_BOOL(r->settings.index_version, 4); + UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE); + } + if (!repo_config_get_bool(r, "fetch.writecommitgraph", &value)) + r->settings.fetch_write_commit_graph = value; + if (!repo_config_get_bool(r, "feature.experimental", &value) && value) { + UPDATE_DEFAULT_BOOL(r->settings.pack_use_sparse, 1); + UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING); + UPDATE_DEFAULT_BOOL(r->settings.fetch_write_commit_graph, 1); + } + UPDATE_DEFAULT_BOOL(r->settings.fetch_write_commit_graph, 0); + + /* Hack for test programs like test-dump-untracked-cache */ + if (ignore_untracked_cache_config) + r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP; + else + UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP); + + UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT); +} diff --git a/repository.h b/repository.h index 4fb6a5885f..fe0b5f5dc6 100644 --- a/repository.h +++ b/repository.h @@ -11,6 +11,34 @@ struct pathspec; struct raw_object_store; struct submodule_cache; +enum untracked_cache_setting { + UNTRACKED_CACHE_UNSET = -1, + UNTRACKED_CACHE_REMOVE = 0, + UNTRACKED_CACHE_KEEP = 1, + UNTRACKED_CACHE_WRITE = 2 +}; + +enum fetch_negotiation_setting { + FETCH_NEGOTIATION_UNSET = -1, + FETCH_NEGOTIATION_NONE = 0, + FETCH_NEGOTIATION_DEFAULT = 1, + FETCH_NEGOTIATION_SKIPPING = 2, +}; + +struct repo_settings { + int initialized; + + int core_commit_graph; + int gc_write_commit_graph; + int fetch_write_commit_graph; + + int index_version; + enum untracked_cache_setting core_untracked_cache; + + int pack_use_sparse; + enum fetch_negotiation_setting fetch_negotiation_algorithm; +}; + struct repository { /* Environment */ /* @@ -72,6 +100,8 @@ struct repository { */ char *submodule_prefix; + struct repo_settings settings; + /* Subsystems */ /* * Repository's config which contains key-value pairs from the usual @@ -157,5 +187,6 @@ int repo_read_index_unmerged(struct repository *); */ void repo_update_index_if_able(struct repository *, struct lock_file *); +void prepare_repo_settings(struct repository *r); #endif /* REPOSITORY_H */ diff --git a/revision.c b/revision.c index 07412297f0..a2406c4519 100644 --- a/revision.c +++ b/revision.c @@ -404,9 +404,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct tag *tag = (struct tag *) object; if (revs->tag_objects && !(flags & UNINTERESTING)) add_pending_object(revs, object, tag->tag); - if (!tag->tagged) - die("bad tag"); - object = parse_object(revs->repo, &tag->tagged->oid); + object = parse_object(revs->repo, get_tagged_oid(tag)); if (!object) { if (revs->ignore_missing_links || (flags & UNINTERESTING)) return NULL; @@ -2523,6 +2521,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt; struct argv_array prune_data = ARGV_ARRAY_INIT; const char *submodule = NULL; + int seen_end_of_options = 0; if (opt) submodule = opt->submodule; @@ -2552,7 +2551,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s revarg_opt |= REVARG_CANNOT_BE_FILENAME; for (left = i = 1; i < argc; i++) { const char *arg = argv[i]; - if (*arg == '-') { + if (!seen_end_of_options && *arg == '-') { int opts; opts = handle_revision_pseudo_opt(submodule, @@ -2574,6 +2573,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s continue; } + if (!strcmp(arg, "--end-of-options")) { + seen_end_of_options = 1; + continue; + } + opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv, opt); if (opts > 0) { diff --git a/sequencer.c b/sequencer.c index 34ebf8ed94..d648aaf416 100644 --- a/sequencer.c +++ b/sequencer.c @@ -3364,6 +3364,9 @@ static int do_merge(struct repository *r, struct commit *head_commit, *merge_commit, *i; struct commit_list *bases, *j, *reversed = NULL; struct commit_list *to_merge = NULL, **tail = &to_merge; + const char *strategy = !opts->xopts_nr && + (!opts->strategy || !strcmp(opts->strategy, "recursive")) ? + NULL : opts->strategy; struct merge_options o; int merge_arg_len, oneline_offset, can_fast_forward, ret, k; static struct lock_file lock; @@ -3516,7 +3519,7 @@ static int do_merge(struct repository *r, goto leave_merge; } - if (to_merge->next) { + if (strategy || to_merge->next) { /* Octopus merge */ struct child_process cmd = CHILD_PROCESS_INIT; @@ -3530,7 +3533,14 @@ static int do_merge(struct repository *r, cmd.git_cmd = 1; argv_array_push(&cmd.args, "merge"); argv_array_push(&cmd.args, "-s"); - argv_array_push(&cmd.args, "octopus"); + if (!strategy) + argv_array_push(&cmd.args, "octopus"); + else { + argv_array_push(&cmd.args, strategy); + for (k = 0; k < opts->xopts_nr; k++) + argv_array_pushf(&cmd.args, + "-X%s", opts->xopts[k]); + } argv_array_push(&cmd.args, "--no-edit"); argv_array_push(&cmd.args, "--no-ff"); argv_array_push(&cmd.args, "--no-log"); @@ -4554,6 +4564,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, { int keep_empty = flags & TODO_LIST_KEEP_EMPTY; int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS; + int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO; struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT; struct strbuf label = STRBUF_INIT; struct commit_list *commits = NULL, **tail = &commits, *iter; @@ -4720,7 +4731,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, if (!commit) strbuf_addf(out, "%s %s\n", cmd_reset, - rebase_cousins ? "onto" : "[new root]"); + rebase_cousins || root_with_onto ? + "onto" : "[new root]"); else { const char *to = NULL; diff --git a/sequencer.h b/sequencer.h index 6704acbb9c..574260f621 100644 --- a/sequencer.h +++ b/sequencer.h @@ -143,6 +143,12 @@ int sequencer_remove_state(struct replay_opts *opts); */ #define TODO_LIST_REBASE_COUSINS (1U << 4) #define TODO_LIST_APPEND_TODO_HELP (1U << 5) +/* + * When generating a script that rebases merges with `--root` *and* with + * `--onto`, we do not want to re-generate the root commits. + */ +#define TODO_LIST_ROOT_WITH_ONTO (1U << 6) + int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, const char **argv, unsigned flags); @@ -4,6 +4,7 @@ #include "dir.h" #include "string-list.h" #include "chdir-notify.h" +#include "promisor-remote.h" static int inside_git_dir = -1; static int inside_work_tree = -1; @@ -478,7 +479,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_ } repository_format_precious_objects = candidate->precious_objects; - repository_format_partial_clone = xstrdup_or_null(candidate->partial_clone); + set_repository_format_partial_clone(candidate->partial_clone); repository_format_worktree_config = candidate->worktree_config; string_list_clear(&candidate->unknown_extensions, 0); @@ -797,7 +798,7 @@ static const char *setup_discovered_git_dir(const char *gitdir, set_git_dir(gitdir); inside_git_dir = 0; inside_work_tree = 1; - if (offset == cwd->len) + if (offset >= cwd->len) return NULL; /* Make "offset" point past the '/' (already the case for root dirs) */ @@ -919,7 +920,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; const char *gitdirenv; - int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1; + int ceil_offset = -1, min_offset = offset_1st_component(dir->buf); dev_t current_device = 0; int one_filesystem = 1; @@ -947,6 +948,12 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (ceil_offset < 0) ceil_offset = min_offset - 2; + if (min_offset && min_offset == dir->len && + !is_dir_sep(dir->buf[min_offset - 1])) { + strbuf_addch(dir, '/'); + min_offset++; + } + /* * Test in the following order (relative to the dir): * - .git (file containing "gitdir: <path>") diff --git a/sha1-file.c b/sha1-file.c index 487ea35d2d..e85f249a5d 100644 --- a/sha1-file.c +++ b/sha1-file.c @@ -30,8 +30,8 @@ #include "mergesort.h" #include "quote.h" #include "packfile.h" -#include "fetch-object.h" #include "object-store.h" +#include "promisor-remote.h" /* The maximum size for an object header. */ #define MAX_HEADER_LEN 32 @@ -952,12 +952,8 @@ void *xmmap_gently(void *start, size_t length, mmap_limit_check(length); ret = mmap(start, length, prot, flags, fd, offset); - if (ret == MAP_FAILED) { - if (!length) - return NULL; - release_pack_memory(length); - ret = mmap(start, length, prot, flags, fd, offset); - } + if (ret == MAP_FAILED && !length) + ret = NULL; return ret; } @@ -1475,16 +1471,17 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid, } /* Check if it is a missing object */ - if (fetch_if_missing && repository_format_partial_clone && + if (fetch_if_missing && has_promisor_remote() && !already_retried && r == the_repository && !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) { /* - * TODO Investigate having fetch_object() return - * TODO error/success and stopping the music here. - * TODO Pass a repository struct through fetch_object, - * such that arbitrary repositories work. + * TODO Investigate checking promisor_remote_get_direct() + * TODO return value and stopping on error here. + * TODO Pass a repository struct through + * promisor_remote_get_direct(), such that arbitrary + * repositories work. */ - fetch_objects(repository_format_partial_clone, real, 1); + promisor_remote_get_direct(r, real, 1); already_retried = 1; continue; } diff --git a/sha1-name.c b/sha1-name.c index 2989e27b71..c665e3f96d 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -403,9 +403,9 @@ static int repo_collect_ambiguous(struct repository *r, return collect_ambiguous(oid, data); } -static struct repository *sort_ambiguous_repo; -static int sort_ambiguous(const void *a, const void *b) +static int sort_ambiguous(const void *a, const void *b, void *ctx) { + struct repository *sort_ambiguous_repo = ctx; int a_type = oid_object_info(sort_ambiguous_repo, a, NULL); int b_type = oid_object_info(sort_ambiguous_repo, b, NULL); int a_type_sort; @@ -434,10 +434,7 @@ static int sort_ambiguous(const void *a, const void *b) static void sort_ambiguous_oid_array(struct repository *r, struct oid_array *a) { - /* mutex will be needed if this code is to be made thread safe */ - sort_ambiguous_repo = r; - QSORT(a->oid, a->nr, sort_ambiguous); - sort_ambiguous_repo = NULL; + QSORT_S(a->oid, a->nr, sort_ambiguous, r); } static enum get_oid_result get_short_oid(struct repository *r, @@ -774,8 +774,10 @@ void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) } } -static int is_rfc3986_reserved(char ch) +int is_rfc3986_reserved_or_unreserved(char ch) { + if (is_rfc3986_unreserved(ch)) + return 1; switch (ch) { case '!': case '*': case '\'': case '(': case ')': case ';': case ':': case '@': case '&': case '=': case '+': case '$': @@ -785,20 +787,19 @@ static int is_rfc3986_reserved(char ch) return 0; } -static int is_rfc3986_unreserved(char ch) +int is_rfc3986_unreserved(char ch) { return isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~'; } static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len, - int reserved) + char_predicate allow_unencoded_fn) { strbuf_grow(sb, len); while (len--) { char ch = *s++; - if (is_rfc3986_unreserved(ch) || - (!reserved && is_rfc3986_reserved(ch))) + if (allow_unencoded_fn(ch)) strbuf_addch(sb, ch); else strbuf_addf(sb, "%%%02x", (unsigned char)ch); @@ -806,9 +807,9 @@ static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len, } void strbuf_addstr_urlencode(struct strbuf *sb, const char *s, - int reserved) + char_predicate allow_unencoded_fn) { - strbuf_add_urlencode(sb, s, strlen(s), reserved); + strbuf_add_urlencode(sb, s, strlen(s), allow_unencoded_fn); } static void strbuf_humanise(struct strbuf *buf, off_t bytes, @@ -672,8 +672,13 @@ void strbuf_branchname(struct strbuf *sb, const char *name, */ int strbuf_check_branch_ref(struct strbuf *sb, const char *name); +typedef int (*char_predicate)(char ch); + +int is_rfc3986_unreserved(char ch); +int is_rfc3986_reserved_or_unreserved(char ch); + void strbuf_addstr_urlencode(struct strbuf *sb, const char *name, - int reserved); + char_predicate allow_unencoded_fn); __attribute__((format (printf,1,2))) int printf_ln(const char *fmt, ...); diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c index 7e79b555de..244977a29b 100644 --- a/t/helper/test-read-cache.c +++ b/t/helper/test-read-cache.c @@ -4,11 +4,10 @@ int cmd__read_cache(int argc, const char **argv) { - int i, cnt = 1, namelen; + int i, cnt = 1; const char *name = NULL; if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) { - namelen = strlen(name); argc--; argv++; } @@ -24,7 +23,7 @@ int cmd__read_cache(int argc, const char **argv) refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL); - pos = index_name_pos(&the_index, name, namelen); + pos = index_name_pos(&the_index, name, strlen(name)); if (pos < 0) die("%s not in index", name); printf("%s is%s up to date\n", name, diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 5d4ae629e1..bc0b9c71f8 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -69,7 +69,7 @@ svn_cmd () { maybe_start_httpd () { loc=${1-svn} - if git env--helper --type=bool --default=false --exit-code GIT_TEST_HTTPD + if git env--helper --type=bool --default=false --exit-code GIT_TEST_SVN_HTTPD then . "$TEST_DIRECTORY"/lib-httpd.sh LIB_HTTPD_SVN="$loc" diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh index 7ea30e5006..6d87961e41 100644 --- a/t/lib-rebase.sh +++ b/t/lib-rebase.sh @@ -44,10 +44,10 @@ set_fake_editor () { rm -f "$1" echo 'rebase -i script before editing:' cat "$1".tmp - action=pick + action=\& for line in $FAKE_LINES; do case $line in - pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d) + pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m) action="$line";; exec_*|x_*|break|b) echo "$line" | sed 's/_/ /g' >> "$1";; @@ -58,11 +58,12 @@ set_fake_editor () { bad) action="badcmd";; fakesha) + test \& != "$action" || action=pick echo "$action XXXXXXX False commit" >> "$1" action=pick;; *) - sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" - action=pick;; + sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1" + action=\&;; esac done echo 'rebase -i script after editing:' diff --git a/t/perf/p5600-clone-reference.sh b/t/perf/p5601-clone-reference.sh index 68fed66347..68fed66347 100755 --- a/t/perf/p5600-clone-reference.sh +++ b/t/perf/p5601-clone-reference.sh diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 9ca0818cbe..4c01f60dd3 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -274,23 +274,23 @@ test_expect_success 'pretend we have a mix of all possible results' " test_expect_success C_LOCALE_OUTPUT 'test --verbose' ' test_must_fail run_sub_test_lib_test \ - test-verbose "test verbose" --verbose <<-\EOF && + t1234-verbose "test verbose" --verbose <<-\EOF && test_expect_success "passing test" true test_expect_success "test with output" "echo foo" test_expect_success "failing test" false test_done EOF - mv test-verbose/out test-verbose/out+ && - grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out && - check_sub_test_lib_test test-verbose <<-\EOF - > expecting success: true + mv t1234-verbose/out t1234-verbose/out+ && + grep -v "^Initialized empty" t1234-verbose/out+ >t1234-verbose/out && + check_sub_test_lib_test t1234-verbose <<-\EOF + > expecting success of 1234.1 '\''passing test'\'': true > ok 1 - passing test > Z - > expecting success: echo foo + > expecting success of 1234.2 '\''test with output'\'': echo foo > foo > ok 2 - test with output > Z - > expecting success: false + > expecting success of 1234.3 '\''failing test'\'': false > not ok 3 - failing test > # false > Z @@ -301,17 +301,17 @@ test_expect_success C_LOCALE_OUTPUT 'test --verbose' ' test_expect_success 'test --verbose-only' ' test_must_fail run_sub_test_lib_test \ - test-verbose-only-2 "test verbose-only=2" \ + t2345-verbose-only-2 "test verbose-only=2" \ --verbose-only=2 <<-\EOF && test_expect_success "passing test" true test_expect_success "test with output" "echo foo" test_expect_success "failing test" false test_done EOF - check_sub_test_lib_test test-verbose-only-2 <<-\EOF + check_sub_test_lib_test t2345-verbose-only-2 <<-\EOF > ok 1 - passing test > Z - > expecting success: echo foo + > expecting success of 2345.2 '\''test with output'\'': echo foo > foo > ok 2 - test with output > Z diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index e10f5f787f..c954c709ad 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -390,6 +390,9 @@ test_expect_success PERL 'required process filter should filter data' ' EOF test_cmp_exclude_clean expected.log debug.log && + # Make sure that the file appears dirty, so checkout below has to + # run the configured filter. + touch test.r && filter_git checkout --quiet --no-progress empty-branch && cat >expected.log <<-EOF && START diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index cebc77fab0..705a136ed9 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -399,4 +399,11 @@ test_expect_success 'GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS works' ' test-tool parse-options --ye ' +test_expect_success '--end-of-options treats remainder as args' ' + test-tool parse-options \ + --expect="verbose: -1" \ + --expect="arg 00: --verbose" \ + --end-of-options --verbose +' + test_done diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh index 2c3ad6e8c1..6ee8ee3b67 100755 --- a/t/t0211-trace2-perf.sh +++ b/t/t0211-trace2-perf.sh @@ -130,11 +130,11 @@ test_expect_success 'perf stream, child processes' ' d0|main|version|||||$V d0|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0 d0|main|cmd_name|||||trace2 (trace2) - d0|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 004child test-tool trace2 001return 0 + d0|main|child_start||_T_ABS_|||[ch0] class:? argv:[test-tool trace2 004child test-tool trace2 001return 0] d1|main|version|||||$V d1|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 001return 0 d1|main|cmd_name|||||trace2 (trace2/trace2) - d1|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 001return 0 + d1|main|child_start||_T_ABS_|||[ch0] class:? argv:[test-tool trace2 001return 0] d2|main|version|||||$V d2|main|start||_T_ABS_|||_EXE_ trace2 001return 0 d2|main|cmd_name|||||trace2 (trace2/trace2/trace2) diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index 5bd892f2f7..d4b7e535ea 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh @@ -26,7 +26,7 @@ promise_and_delete () { test_expect_success 'extensions.partialclone without filter' ' test_create_repo server && git clone --filter="blob:none" "file://$(pwd)/server" client && - git -C client config --unset core.partialclonefilter && + git -C client config --unset remote.origin.partialclonefilter && git -C client fetch origin ' @@ -166,8 +166,9 @@ test_expect_success 'fetching of missing objects' ' # associated packfile contains the object ls repo/.git/objects/pack/pack-*.promisor >promisorlist && test_line_count = 1 promisorlist && - IDX=$(cat promisorlist | sed "s/promisor$/idx/") && - git verify-pack --verbose "$IDX" | grep "$HASH" + IDX=$(sed "s/promisor$/idx/" promisorlist) && + git verify-pack --verbose "$IDX" >out && + grep "$HASH" out ' test_expect_success 'fetching of missing objects works with ref-in-want enabled' ' @@ -182,8 +183,55 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled' grep "git< fetch=.*ref-in-want" trace ' +test_expect_success 'fetching of missing objects from another promisor remote' ' + git clone "file://$(pwd)/server" server2 && + test_commit -C server2 bar && + git -C server2 repack -a -d --write-bitmap-index && + HASH2=$(git -C server2 rev-parse bar) && + + git -C repo remote add server2 "file://$(pwd)/server2" && + git -C repo config remote.server2.promisor true && + git -C repo cat-file -p "$HASH2" && + + git -C repo fetch server2 && + rm -rf repo/.git/objects/* && + git -C repo cat-file -p "$HASH2" && + + # Ensure that the .promisor file is written, and check that its + # associated packfile contains the object + ls repo/.git/objects/pack/pack-*.promisor >promisorlist && + test_line_count = 1 promisorlist && + IDX=$(sed "s/promisor$/idx/" promisorlist) && + git verify-pack --verbose "$IDX" >out && + grep "$HASH2" out +' + +test_expect_success 'fetching of missing objects configures a promisor remote' ' + git clone "file://$(pwd)/server" server3 && + test_commit -C server3 baz && + git -C server3 repack -a -d --write-bitmap-index && + HASH3=$(git -C server3 rev-parse baz) && + git -C server3 config uploadpack.allowfilter 1 && + + rm repo/.git/objects/pack/pack-*.promisor && + + git -C repo remote add server3 "file://$(pwd)/server3" && + git -C repo fetch --filter="blob:none" server3 $HASH3 && + + test_cmp_config -C repo true remote.server3.promisor && + + # Ensure that the .promisor file is written, and check that its + # associated packfile contains the object + ls repo/.git/objects/pack/pack-*.promisor >promisorlist && + test_line_count = 1 promisorlist && + IDX=$(sed "s/promisor$/idx/" promisorlist) && + git verify-pack --verbose "$IDX" >out && + grep "$HASH3" out +' + test_expect_success 'fetching of missing blobs works' ' - rm -rf server repo && + rm -rf server server2 repo && + rm -rf server server3 repo && test_create_repo server && test_commit -C server foo && git -C server repack -a -d --write-bitmap-index && @@ -234,7 +282,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' ' git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && - GIT_TEST_COMMIT_GRAPH=0 git -C repo rev-list --exclude-promisor-objects --objects bar >out && + GIT_TEST_COMMIT_GRAPH=0 git -C repo -c core.commitGraph=false rev-list --exclude-promisor-objects --objects bar >out && grep $(git -C repo rev-parse bar) out && ! grep $FOO out ' @@ -514,8 +562,12 @@ test_expect_success 'fetching of missing objects from an HTTP server' ' # associated packfile contains the object ls repo/.git/objects/pack/pack-*.promisor >promisorlist && test_line_count = 1 promisorlist && - IDX=$(cat promisorlist | sed "s/promisor$/idx/") && - git verify-pack --verbose "$IDX" | grep "$HASH" + IDX=$(sed "s/promisor$/idx/" promisorlist) && + git verify-pack --verbose "$IDX" >out && + grep "$HASH" out ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 428177c390..983a0a1583 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1294,26 +1294,25 @@ test_expect_success 'git -c is not confused by empty environment' ' GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list ' -sq="'" test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' ' cat >expect <<-\EOF && env.one one env.two two EOF - GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq} ${sq}env.two=two${sq}" \ + GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ} ${SQ}env.two=two${SQ}" \ git config --get-regexp "env.*" >actual && test_cmp expect actual && cat >expect <<-EOF && - env.one one${sq} + env.one one${SQ} env.two two EOF - GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq$sq$sq ${sq}env.two=two${sq}" \ + GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ$SQ$SQ ${SQ}env.two=two${SQ}" \ git config --get-regexp "env.*" >actual && test_cmp expect actual && test_must_fail env \ - GIT_CONFIG_PARAMETERS="${sq}env.one=one${sq}\\$sq ${sq}env.two=two${sq}" \ + GIT_CONFIG_PARAMETERS="${SQ}env.one=one${SQ}\\$SQ ${SQ}env.two=two${SQ}" \ git config --get-regexp "env.*" ' diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh index 0c37e7180d..3a0de0ddaa 100755 --- a/t/t1309-early-config.sh +++ b/t/t1309-early-config.sh @@ -91,7 +91,12 @@ test_expect_failure 'ignore .git/ with invalid config' ' test_expect_success 'early config and onbranch' ' echo "[broken" >broken && - test_with_config "[includeif \"onbranch:refs/heads/master\"]path=../broken" + test_with_config "[includeif \"onbranch:master\"]path=../broken" +' + +test_expect_success 'onbranch config outside of git repo' ' + test_config_global includeIf.onbranch:master.path non-existent && + nongit git help ' test_done diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh index 970c5c36b9..2d142e5535 100755 --- a/t/t1404-update-ref-errors.sh +++ b/t/t1404-update-ref-errors.sh @@ -32,8 +32,6 @@ test_update_rejected () { test_cmp unchanged actual } -Q="'" - # Test adding and deleting D/F-conflicting references in a single # transaction. df_test() { @@ -93,7 +91,7 @@ df_test() { delname="$delref" fi && cat >expected-err <<-EOF && - fatal: cannot lock ref $Q$addname$Q: $Q$delref$Q exists; cannot create $Q$addref$Q + fatal: cannot lock ref $SQ$addname$SQ: $SQ$delref$SQ exists; cannot create $SQ$addref$SQ EOF $pack && if $add_del @@ -123,7 +121,7 @@ test_expect_success 'existing loose ref is a simple prefix of new' ' prefix=refs/1l && test_update_rejected "a c e" false "b c/x d" \ - "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x$Q" + "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x$SQ" ' @@ -131,7 +129,7 @@ test_expect_success 'existing packed ref is a simple prefix of new' ' prefix=refs/1p && test_update_rejected "a c e" true "b c/x d" \ - "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x$Q" + "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x$SQ" ' @@ -139,7 +137,7 @@ test_expect_success 'existing loose ref is a deeper prefix of new' ' prefix=refs/2l && test_update_rejected "a c e" false "b c/x/y d" \ - "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x/y$Q" + "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x/y$SQ" ' @@ -147,7 +145,7 @@ test_expect_success 'existing packed ref is a deeper prefix of new' ' prefix=refs/2p && test_update_rejected "a c e" true "b c/x/y d" \ - "$Q$prefix/c$Q exists; cannot create $Q$prefix/c/x/y$Q" + "$SQ$prefix/c$SQ exists; cannot create $SQ$prefix/c/x/y$SQ" ' @@ -155,7 +153,7 @@ test_expect_success 'new ref is a simple prefix of existing loose' ' prefix=refs/3l && test_update_rejected "a c/x e" false "b c d" \ - "$Q$prefix/c/x$Q exists; cannot create $Q$prefix/c$Q" + "$SQ$prefix/c/x$SQ exists; cannot create $SQ$prefix/c$SQ" ' @@ -163,7 +161,7 @@ test_expect_success 'new ref is a simple prefix of existing packed' ' prefix=refs/3p && test_update_rejected "a c/x e" true "b c d" \ - "$Q$prefix/c/x$Q exists; cannot create $Q$prefix/c$Q" + "$SQ$prefix/c/x$SQ exists; cannot create $SQ$prefix/c$SQ" ' @@ -171,7 +169,7 @@ test_expect_success 'new ref is a deeper prefix of existing loose' ' prefix=refs/4l && test_update_rejected "a c/x/y e" false "b c d" \ - "$Q$prefix/c/x/y$Q exists; cannot create $Q$prefix/c$Q" + "$SQ$prefix/c/x/y$SQ exists; cannot create $SQ$prefix/c$SQ" ' @@ -179,7 +177,7 @@ test_expect_success 'new ref is a deeper prefix of existing packed' ' prefix=refs/4p && test_update_rejected "a c/x/y e" true "b c d" \ - "$Q$prefix/c/x/y$Q exists; cannot create $Q$prefix/c$Q" + "$SQ$prefix/c/x/y$SQ exists; cannot create $SQ$prefix/c$SQ" ' @@ -187,7 +185,7 @@ test_expect_success 'one new ref is a simple prefix of another' ' prefix=refs/5 && test_update_rejected "a e" false "b c c/x d" \ - "cannot process $Q$prefix/c$Q and $Q$prefix/c/x$Q at the same time" + "cannot process $SQ$prefix/c$SQ and $SQ$prefix/c/x$SQ at the same time" ' @@ -334,7 +332,7 @@ test_expect_success 'D/F conflict prevents indirect delete long packed + indirec test_expect_success 'missing old value blocks update' ' prefix=refs/missing-update && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q + fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ EOF printf "%s\n" "update $prefix/foo $E $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -345,7 +343,7 @@ test_expect_success 'incorrect old value blocks update' ' prefix=refs/incorrect-update && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: is at $C but expected $D + fatal: cannot lock ref $SQ$prefix/foo$SQ: is at $C but expected $D EOF printf "%s\n" "update $prefix/foo $E $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -356,7 +354,7 @@ test_expect_success 'existing old value blocks create' ' prefix=refs/existing-create && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: reference already exists + fatal: cannot lock ref $SQ$prefix/foo$SQ: reference already exists EOF printf "%s\n" "create $prefix/foo $E" | test_must_fail git update-ref --stdin 2>output.err && @@ -367,7 +365,7 @@ test_expect_success 'incorrect old value blocks delete' ' prefix=refs/incorrect-delete && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: is at $C but expected $D + fatal: cannot lock ref $SQ$prefix/foo$SQ: is at $C but expected $D EOF printf "%s\n" "delete $prefix/foo $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -378,7 +376,7 @@ test_expect_success 'missing old value blocks indirect update' ' prefix=refs/missing-indirect-update && git symbolic-ref $prefix/symref $prefix/foo && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q + fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ EOF printf "%s\n" "update $prefix/symref $E $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -390,7 +388,7 @@ test_expect_success 'incorrect old value blocks indirect update' ' git symbolic-ref $prefix/symref $prefix/foo && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D + fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D EOF printf "%s\n" "update $prefix/symref $E $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -402,7 +400,7 @@ test_expect_success 'existing old value blocks indirect create' ' git symbolic-ref $prefix/symref $prefix/foo && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: reference already exists + fatal: cannot lock ref $SQ$prefix/symref$SQ: reference already exists EOF printf "%s\n" "create $prefix/symref $E" | test_must_fail git update-ref --stdin 2>output.err && @@ -414,7 +412,7 @@ test_expect_success 'incorrect old value blocks indirect delete' ' git symbolic-ref $prefix/symref $prefix/foo && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D + fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D EOF printf "%s\n" "delete $prefix/symref $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -425,7 +423,7 @@ test_expect_success 'missing old value blocks indirect no-deref update' ' prefix=refs/missing-noderef-update && git symbolic-ref $prefix/symref $prefix/foo && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: reference is missing but expected $D + fatal: cannot lock ref $SQ$prefix/symref$SQ: reference is missing but expected $D EOF printf "%s\n" "option no-deref" "update $prefix/symref $E $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -437,7 +435,7 @@ test_expect_success 'incorrect old value blocks indirect no-deref update' ' git symbolic-ref $prefix/symref $prefix/foo && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D + fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D EOF printf "%s\n" "option no-deref" "update $prefix/symref $E $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -449,7 +447,7 @@ test_expect_success 'existing old value blocks indirect no-deref create' ' git symbolic-ref $prefix/symref $prefix/foo && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: reference already exists + fatal: cannot lock ref $SQ$prefix/symref$SQ: reference already exists EOF printf "%s\n" "option no-deref" "create $prefix/symref $E" | test_must_fail git update-ref --stdin 2>output.err && @@ -461,7 +459,7 @@ test_expect_success 'incorrect old value blocks indirect no-deref delete' ' git symbolic-ref $prefix/symref $prefix/foo && git update-ref $prefix/foo $C && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: is at $C but expected $D + fatal: cannot lock ref $SQ$prefix/symref$SQ: is at $C but expected $D EOF printf "%s\n" "option no-deref" "delete $prefix/symref $D" | test_must_fail git update-ref --stdin 2>output.err && @@ -474,13 +472,13 @@ test_expect_success 'non-empty directory blocks create' ' : >.git/$prefix/foo/bar/baz.lock && test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: there is a non-empty directory $Q.git/$prefix/foo$Q blocking reference $Q$prefix/foo$Q + fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ EOF printf "%s\n" "update $prefix/foo $C" | test_must_fail git update-ref --stdin 2>output.err && test_cmp expected output.err && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q + fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ EOF printf "%s\n" "update $prefix/foo $D $C" | test_must_fail git update-ref --stdin 2>output.err && @@ -493,13 +491,13 @@ test_expect_success 'broken reference blocks create' ' echo "gobbledigook" >.git/$prefix/foo && test_when_finished "rm -f .git/$prefix/foo" && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken + fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken EOF printf "%s\n" "update $prefix/foo $C" | test_must_fail git update-ref --stdin 2>output.err && test_cmp expected output.err && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/foo$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken + fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken EOF printf "%s\n" "update $prefix/foo $D $C" | test_must_fail git update-ref --stdin 2>output.err && @@ -513,13 +511,13 @@ test_expect_success 'non-empty directory blocks indirect create' ' : >.git/$prefix/foo/bar/baz.lock && test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: there is a non-empty directory $Q.git/$prefix/foo$Q blocking reference $Q$prefix/foo$Q + fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ EOF printf "%s\n" "update $prefix/symref $C" | test_must_fail git update-ref --stdin 2>output.err && test_cmp expected output.err && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q + fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ EOF printf "%s\n" "update $prefix/symref $D $C" | test_must_fail git update-ref --stdin 2>output.err && @@ -532,13 +530,13 @@ test_expect_success 'broken reference blocks indirect create' ' echo "gobbledigook" >.git/$prefix/foo && test_when_finished "rm -f .git/$prefix/foo" && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken + fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken EOF printf "%s\n" "update $prefix/symref $C" | test_must_fail git update-ref --stdin 2>output.err && test_cmp expected output.err && cat >expected <<-EOF && - fatal: cannot lock ref $Q$prefix/symref$Q: unable to resolve reference $Q$prefix/foo$Q: reference broken + fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken EOF printf "%s\n" "update $prefix/symref $D $C" | test_must_fail git update-ref --stdin 2>output.err && @@ -614,7 +612,7 @@ test_expect_success 'delete fails cleanly if packed-refs file is locked' ' test_when_finished "rm -f .git/packed-refs.lock" && test_must_fail git update-ref -d $prefix/foo >out 2>err && git for-each-ref $prefix >actual && - test_i18ngrep "Unable to create $Q.*packed-refs.lock$Q: " err && + test_i18ngrep "Unable to create $SQ.*packed-refs.lock$SQ: " err && test_cmp unchanged actual ' diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh index feb1efd8ff..1181a9fb28 100755 --- a/t/t1414-reflog-walk.sh +++ b/t/t1414-reflog-walk.sh @@ -18,10 +18,9 @@ do_walk () { git log -g --format="%gd %gs" "$@" } -sq="'" test_expect_success 'set up expected reflog' ' cat >expect.all <<-EOF - HEAD@{0} commit (merge): Merge branch ${sq}master${sq} into side + HEAD@{0} commit (merge): Merge branch ${SQ}master${SQ} into side HEAD@{1} commit: three HEAD@{2} checkout: moving from master to side HEAD@{3} commit: two diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh index 4ee009da66..21a9c8ffb2 100755 --- a/t/t1506-rev-parse-diagnosis.sh +++ b/t/t1506-rev-parse-diagnosis.sh @@ -8,10 +8,9 @@ exec </dev/null test_did_you_mean () { - sq="'" && cat >expected <<-EOF && - fatal: Path '$2$3' $4, but not ${5:-$sq$3$sq}. - Did you mean '$1:$2$3'${2:+ aka $sq$1:./$3$sq}? + fatal: Path '$2$3' $4, but not ${5:-$SQ$3$SQ}. + Did you mean '$1:$2$3'${2:+ aka $SQ$1:./$3$SQ}? EOF test_cmp expected error } diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh index fa3e499641..8b4cf8a6e3 100755 --- a/t/t1507-rev-parse-upstream.sh +++ b/t/t1507-rev-parse-upstream.sh @@ -28,8 +28,6 @@ test_expect_success 'setup' ' ) ' -sq="'" - full_name () { (cd clone && git rev-parse --symbolic-full-name "$@") @@ -129,7 +127,7 @@ test_expect_success 'merge my-side@{u} records the correct name' ' git branch -t new my-side@{u} && git merge -s ours new@{u} && git show -s --pretty=tformat:%s >actual && - echo "Merge remote-tracking branch ${sq}origin/side${sq}" >expect && + echo "Merge remote-tracking branch ${SQ}origin/side${SQ}" >expect && test_cmp expect actual ) ' @@ -156,7 +154,7 @@ test_expect_success 'branch@{u} works when tracking a local branch' ' test_expect_success 'branch@{u} error message when no upstream' ' cat >expect <<-EOF && - fatal: no upstream configured for branch ${sq}non-tracking${sq} + fatal: no upstream configured for branch ${SQ}non-tracking${SQ} EOF error_message non-tracking@{u} && test_i18ncmp expect error @@ -164,7 +162,7 @@ test_expect_success 'branch@{u} error message when no upstream' ' test_expect_success '@{u} error message when no upstream' ' cat >expect <<-EOF && - fatal: no upstream configured for branch ${sq}master${sq} + fatal: no upstream configured for branch ${SQ}master${SQ} EOF test_must_fail git rev-parse --verify @{u} 2>actual && test_i18ncmp expect actual @@ -172,7 +170,7 @@ test_expect_success '@{u} error message when no upstream' ' test_expect_success 'branch@{u} error message with misspelt branch' ' cat >expect <<-EOF && - fatal: no such branch: ${sq}no-such-branch${sq} + fatal: no such branch: ${SQ}no-such-branch${SQ} EOF error_message no-such-branch@{u} && test_i18ncmp expect error @@ -189,7 +187,7 @@ test_expect_success '@{u} error message when not on a branch' ' test_expect_success 'branch@{u} error message if upstream branch not fetched' ' cat >expect <<-EOF && - fatal: upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch + fatal: upstream branch ${SQ}refs/heads/side${SQ} not stored as a remote-tracking branch EOF error_message bad-upstream@{u} && test_i18ncmp expect error diff --git a/t/t1600-index.sh b/t/t1600-index.sh index 42962ed7d4..c77721b580 100755 --- a/t/t1600-index.sh +++ b/t/t1600-index.sh @@ -59,17 +59,38 @@ test_expect_success 'out of bounds index.version issues warning' ' ) ' -test_expect_success 'GIT_INDEX_VERSION takes precedence over config' ' +test_index_version () { + INDEX_VERSION_CONFIG=$1 && + FEATURE_MANY_FILES=$2 && + ENV_VAR_VERSION=$3 + EXPECTED_OUTPUT_VERSION=$4 && ( rm -f .git/index && - GIT_INDEX_VERSION=4 && - export GIT_INDEX_VERSION && - git config --add index.version 2 && + rm -f .git/config && + if test "$INDEX_VERSION_CONFIG" -ne 0 + then + git config --add index.version $INDEX_VERSION_CONFIG + fi && + git config --add feature.manyFiles $FEATURE_MANY_FILES + if test "$ENV_VAR_VERSION" -ne 0 + then + GIT_INDEX_VERSION=$ENV_VAR_VERSION && + export GIT_INDEX_VERSION + else + unset GIT_INDEX_VERSION + fi && git add a 2>&1 && - echo 4 >expect && + echo $EXPECTED_OUTPUT_VERSION >expect && test-tool index-version <.git/index >actual && test_cmp expect actual ) +} + +test_expect_success 'index version config precedence' ' + test_index_version 2 false 4 4 && + test_index_version 2 true 0 2 && + test_index_version 0 true 0 4 && + test_index_version 0 true 2 2 ' test_done diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh index fc3eb43b89..6844afafc0 100755 --- a/t/t2022-checkout-paths.sh +++ b/t/t2022-checkout-paths.sh @@ -78,4 +78,15 @@ test_expect_success 'do not touch files that are already up-to-date' ' test_cmp expect actual ' +test_expect_success 'checkout HEAD adds deleted intent-to-add file back to index' ' + echo "nonempty" >nonempty && + >empty && + git add nonempty empty && + git commit -m "create files to be deleted" && + git rm --cached nonempty empty && + git add -N nonempty empty && + git checkout HEAD nonempty empty && + git diff --cached --exit-code +' + test_done diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh index 2650df1966..21c3f84459 100755 --- a/t/t2070-restore.sh +++ b/t/t2070-restore.sh @@ -95,4 +95,15 @@ test_expect_success 'restore --ignore-unmerged ignores unmerged entries' ' ) ' +test_expect_success 'restore --staged adds deleted intent-to-add file back to index' ' + echo "nonempty" >nonempty && + >empty && + git add nonempty empty && + git commit -m "create files to be deleted" && + git rm --cached nonempty empty && + git add -N nonempty empty && + git restore --staged nonempty empty && + git diff --cached --exit-code +' + test_done diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh index 209b4c7cd8..2ec69a8a26 100755 --- a/t/t3005-ls-files-relative.sh +++ b/t/t3005-ls-files-relative.sh @@ -7,10 +7,6 @@ This test runs git ls-files with various relative path arguments. . ./test-lib.sh -new_line=' -' -sq=\' - test_expect_success 'prepare' ' : >never-mind-me && git add never-mind-me && @@ -44,9 +40,9 @@ test_expect_success 'ls-files -c' ' cd top/sub && for f in ../y* do - echo "error: pathspec $sq$f$sq did not match any file(s) known to git" + echo "error: pathspec $SQ$f$SQ did not match any file(s) known to git" done >expect.err && - echo "Did you forget to ${sq}git add${sq}?" >>expect.err && + echo "Did you forget to ${SQ}git add${SQ}?" >>expect.err && ls ../x* >expect.out && test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err && test_cmp expect.out actual.out && @@ -59,9 +55,9 @@ test_expect_success 'ls-files -o' ' cd top/sub && for f in ../x* do - echo "error: pathspec $sq$f$sq did not match any file(s) known to git" + echo "error: pathspec $SQ$f$SQ did not match any file(s) known to git" done >expect.err && - echo "Did you forget to ${sq}git add${sq}?" >>expect.err && + echo "Did you forget to ${SQ}git add${SQ}?" >>expect.err && ls ../y* >expect.out && test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err && test_cmp expect.out actual.out && diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 0ea4fc4694..40251c9f8f 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -192,10 +192,10 @@ test_expect_success 'branch --merged with --verbose' ' EOF test_cmp expect actual && git branch --verbose --merged topic >actual && - cat >expect <<-\EOF && - master c77a0a9 second on master - * topic 2c939f4 [ahead 1] foo - zzz c77a0a9 second on master + cat >expect <<-EOF && + master $(git rev-parse --short master) second on master + * topic $(git rev-parse --short topic ) [ahead 1] foo + zzz $(git rev-parse --short zzz ) second on master EOF test_i18ncmp expect actual ' diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index ec548654ce..0120f769f1 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -8,17 +8,124 @@ test_description='range-diff tests' # harm than good. We need some real history. test_expect_success 'setup' ' - git fast-import < "$TEST_DIRECTORY"/t3206/history.export + git fast-import < "$TEST_DIRECTORY"/t3206/history.export && + test_oid_cache <<-EOF + # topic + t1 sha1:4de457d + t2 sha1:fccce22 + t3 sha1:147e64e + t4 sha1:a63e992 + t1 sha256:b89f8b9 + t2 sha256:5f12aad + t3 sha256:ea8b273 + t4 sha256:14b7336 + + # unmodified + u1 sha1:35b9b25 + u2 sha1:de345ab + u3 sha1:9af6654 + u4 sha1:2901f77 + u1 sha256:e3731be + u2 sha256:14fadf8 + u3 sha256:736c4bc + u4 sha256:673e77d + + # reordered + r1 sha1:aca177a + r2 sha1:14ad629 + r3 sha1:ee58208 + r4 sha1:307b27a + r1 sha256:f59d3aa + r2 sha256:fb261a8 + r3 sha256:cb2649b + r4 sha256:958577e + + # removed (deleted) + d1 sha1:7657159 + d2 sha1:43d84d3 + d3 sha1:a740396 + d1 sha256:e312513 + d2 sha256:eb19258 + d3 sha256:1ccb3c1 + + # added + a1 sha1:2716022 + a2 sha1:b62accd + a3 sha1:df46cfa + a4 sha1:3e64548 + a5 sha1:12b4063 + a1 sha256:d724f4d + a2 sha256:1de7762 + a3 sha256:e159431 + a4 sha256:b3e483c + a5 sha256:90866a7 + + # rebased + b1 sha1:cc9c443 + b2 sha1:c5d9641 + b3 sha1:28cc2b6 + b4 sha1:5628ab7 + b5 sha1:a31b12e + b1 sha256:a1a8717 + b2 sha256:20a5862 + b3 sha256:587172a + b4 sha256:2721c5d + b5 sha256:7b57864 + + # changed + c1 sha1:a4b3333 + c2 sha1:f51d370 + c3 sha1:0559556 + c4 sha1:d966c5c + c1 sha256:f8c2b9d + c2 sha256:3fb6318 + c3 sha256:168ab68 + c4 sha256:3526539 + + # changed-message + m1 sha1:f686024 + m2 sha1:4ab067d + m3 sha1:b9cb956 + m4 sha1:8add5f1 + m1 sha256:31e6281 + m2 sha256:a06bf1b + m3 sha256:82dc654 + m4 sha256:48470c5 + + # renamed + n1 sha1:f258d75 + n2 sha1:017b62d + n3 sha1:3ce7af6 + n4 sha1:1e6226b + n1 sha256:ad52114 + n2 sha256:3b54c8f + n3 sha256:3b0a644 + n4 sha256:e461653 + + # added and removed + s1 sha1:096b1ba + s2 sha1:d92e698 + s3 sha1:9a1db4d + s4 sha1:fea3b5c + s1 sha256:a7f9134 + s2 sha256:b4c2580 + s3 sha256:1d62aa2 + s4 sha256:48160e8 + + # Empty delimiter (included so lines match neatly) + __ sha1:------- + __ sha256:------- + EOF ' test_expect_success 'simple A..B A..C (unmodified)' ' git range-diff --no-color master..topic master..unmodified \ >actual && cat >expected <<-EOF && - 1: 4de457d = 1: 35b9b25 s/5/A/ - 2: fccce22 = 2: de345ab s/4/A/ - 3: 147e64e = 3: 9af6654 s/11/B/ - 4: a63e992 = 4: 2901f77 s/12/B/ + 1: $(test_oid t1) = 1: $(test_oid u1) s/5/A/ + 2: $(test_oid t2) = 2: $(test_oid u2) s/4/A/ + 3: $(test_oid t3) = 3: $(test_oid u3) s/11/B/ + 4: $(test_oid t4) = 4: $(test_oid u4) s/12/B/ EOF test_cmp expected actual ' @@ -38,10 +145,10 @@ test_expect_success 'simple A B C (unmodified)' ' test_expect_success 'trivial reordering' ' git range-diff --no-color master topic reordered >actual && cat >expected <<-EOF && - 1: 4de457d = 1: aca177a s/5/A/ - 3: 147e64e = 2: 14ad629 s/11/B/ - 4: a63e992 = 3: ee58208 s/12/B/ - 2: fccce22 = 4: 307b27a s/4/A/ + 1: $(test_oid t1) = 1: $(test_oid r1) s/5/A/ + 3: $(test_oid t3) = 2: $(test_oid r2) s/11/B/ + 4: $(test_oid t4) = 3: $(test_oid r3) s/12/B/ + 2: $(test_oid t2) = 4: $(test_oid r4) s/4/A/ EOF test_cmp expected actual ' @@ -49,10 +156,10 @@ test_expect_success 'trivial reordering' ' test_expect_success 'removed a commit' ' git range-diff --no-color master topic removed >actual && cat >expected <<-EOF && - 1: 4de457d = 1: 7657159 s/5/A/ - 2: fccce22 < -: ------- s/4/A/ - 3: 147e64e = 2: 43d84d3 s/11/B/ - 4: a63e992 = 3: a740396 s/12/B/ + 1: $(test_oid t1) = 1: $(test_oid d1) s/5/A/ + 2: $(test_oid t2) < -: $(test_oid __) s/4/A/ + 3: $(test_oid t3) = 2: $(test_oid d2) s/11/B/ + 4: $(test_oid t4) = 3: $(test_oid d3) s/12/B/ EOF test_cmp expected actual ' @@ -60,11 +167,11 @@ test_expect_success 'removed a commit' ' test_expect_success 'added a commit' ' git range-diff --no-color master topic added >actual && cat >expected <<-EOF && - 1: 4de457d = 1: 2716022 s/5/A/ - 2: fccce22 = 2: b62accd s/4/A/ - -: ------- > 3: df46cfa s/6/A/ - 3: 147e64e = 4: 3e64548 s/11/B/ - 4: a63e992 = 5: 12b4063 s/12/B/ + 1: $(test_oid t1) = 1: $(test_oid a1) s/5/A/ + 2: $(test_oid t2) = 2: $(test_oid a2) s/4/A/ + -: $(test_oid __) > 3: $(test_oid a3) s/6/A/ + 3: $(test_oid t3) = 4: $(test_oid a4) s/11/B/ + 4: $(test_oid t4) = 5: $(test_oid a5) s/12/B/ EOF test_cmp expected actual ' @@ -72,10 +179,10 @@ test_expect_success 'added a commit' ' test_expect_success 'new base, A B C' ' git range-diff --no-color master topic rebased >actual && cat >expected <<-EOF && - 1: 4de457d = 1: cc9c443 s/5/A/ - 2: fccce22 = 2: c5d9641 s/4/A/ - 3: 147e64e = 3: 28cc2b6 s/11/B/ - 4: a63e992 = 4: 5628ab7 s/12/B/ + 1: $(test_oid t1) = 1: $(test_oid b1) s/5/A/ + 2: $(test_oid t2) = 2: $(test_oid b2) s/4/A/ + 3: $(test_oid t3) = 3: $(test_oid b3) s/11/B/ + 4: $(test_oid t4) = 4: $(test_oid b4) s/12/B/ EOF test_cmp expected actual ' @@ -84,11 +191,11 @@ test_expect_success 'new base, B...C' ' # this syntax includes the commits from master! git range-diff --no-color topic...rebased >actual && cat >expected <<-EOF && - -: ------- > 1: a31b12e unrelated - 1: 4de457d = 2: cc9c443 s/5/A/ - 2: fccce22 = 3: c5d9641 s/4/A/ - 3: 147e64e = 4: 28cc2b6 s/11/B/ - 4: a63e992 = 5: 5628ab7 s/12/B/ + -: $(test_oid __) > 1: $(test_oid b5) unrelated + 1: $(test_oid t1) = 2: $(test_oid b1) s/5/A/ + 2: $(test_oid t2) = 3: $(test_oid b2) s/4/A/ + 3: $(test_oid t3) = 4: $(test_oid b3) s/11/B/ + 4: $(test_oid t4) = 5: $(test_oid b4) s/12/B/ EOF test_cmp expected actual ' @@ -96,9 +203,9 @@ test_expect_success 'new base, B...C' ' test_expect_success 'changed commit' ' git range-diff --no-color topic...changed >actual && cat >expected <<-EOF && - 1: 4de457d = 1: a4b3333 s/5/A/ - 2: fccce22 = 2: f51d370 s/4/A/ - 3: 147e64e ! 3: 0559556 s/11/B/ + 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/ + 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/ + 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/ @@ file: A 9 10 @@ -108,7 +215,7 @@ test_expect_success 'changed commit' ' 12 13 14 - 4: a63e992 ! 4: d966c5c s/12/B/ + 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/ @@ file @@ file: A 9 @@ -125,10 +232,10 @@ test_expect_success 'changed commit' ' test_expect_success 'changed commit with --no-patch diff option' ' git range-diff --no-color --no-patch topic...changed >actual && cat >expected <<-EOF && - 1: 4de457d = 1: a4b3333 s/5/A/ - 2: fccce22 = 2: f51d370 s/4/A/ - 3: 147e64e ! 3: 0559556 s/11/B/ - 4: a63e992 ! 4: d966c5c s/12/B/ + 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/ + 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/ + 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/ + 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/ EOF test_cmp expected actual ' @@ -136,16 +243,16 @@ test_expect_success 'changed commit with --no-patch diff option' ' test_expect_success 'changed commit with --stat diff option' ' git range-diff --no-color --stat topic...changed >actual && cat >expected <<-EOF && - 1: 4de457d = 1: a4b3333 s/5/A/ + 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/ a => b | 0 1 file changed, 0 insertions(+), 0 deletions(-) - 2: fccce22 = 2: f51d370 s/4/A/ + 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/ a => b | 0 1 file changed, 0 insertions(+), 0 deletions(-) - 3: 147e64e ! 3: 0559556 s/11/B/ + 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/ a => b | 0 1 file changed, 0 insertions(+), 0 deletions(-) - 4: a63e992 ! 4: d966c5c s/12/B/ + 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/ a => b | 0 1 file changed, 0 insertions(+), 0 deletions(-) EOF @@ -155,9 +262,9 @@ test_expect_success 'changed commit with --stat diff option' ' test_expect_success 'changed commit with sm config' ' git range-diff --no-color --submodule=log topic...changed >actual && cat >expected <<-EOF && - 1: 4de457d = 1: a4b3333 s/5/A/ - 2: fccce22 = 2: f51d370 s/4/A/ - 3: 147e64e ! 3: 0559556 s/11/B/ + 1: $(test_oid t1) = 1: $(test_oid c1) s/5/A/ + 2: $(test_oid t2) = 2: $(test_oid c2) s/4/A/ + 3: $(test_oid t3) ! 3: $(test_oid c3) s/11/B/ @@ file: A 9 10 @@ -167,7 +274,7 @@ test_expect_success 'changed commit with sm config' ' 12 13 14 - 4: a63e992 ! 4: d966c5c s/12/B/ + 4: $(test_oid t4) ! 4: $(test_oid c4) s/12/B/ @@ file @@ file: A 9 @@ -184,8 +291,8 @@ test_expect_success 'changed commit with sm config' ' test_expect_success 'renamed file' ' git range-diff --no-color --submodule=log topic...renamed-file >actual && sed s/Z/\ /g >expected <<-EOF && - 1: 4de457d = 1: f258d75 s/5/A/ - 2: fccce22 ! 2: 017b62d s/4/A/ + 1: $(test_oid t1) = 1: $(test_oid n1) s/5/A/ + 2: $(test_oid t2) ! 2: $(test_oid n2) s/4/A/ @@ Metadata ZAuthor: Thomas Rast <trast@inf.ethz.ch> Z @@ -198,7 +305,7 @@ test_expect_success 'renamed file' ' Z@@ Z 1 Z 2 - 3: 147e64e ! 3: 3ce7af6 s/11/B/ + 3: $(test_oid t3) ! 3: $(test_oid n3) s/11/B/ @@ Metadata Z ## Commit message ## Z s/11/B/ @@ -210,7 +317,7 @@ test_expect_success 'renamed file' ' Z 8 Z 9 Z 10 - 4: a63e992 ! 4: 1e6226b s/12/B/ + 4: $(test_oid t4) ! 4: $(test_oid n4) s/12/B/ @@ Metadata Z ## Commit message ## Z s/12/B/ @@ -229,8 +336,8 @@ test_expect_success 'renamed file' ' test_expect_success 'file added and later removed' ' git range-diff --no-color --submodule=log topic...added-removed >actual && sed s/Z/\ /g >expected <<-EOF && - 1: 4de457d = 1: 096b1ba s/5/A/ - 2: fccce22 ! 2: d92e698 s/4/A/ + 1: $(test_oid t1) = 1: $(test_oid s1) s/5/A/ + 2: $(test_oid t2) ! 2: $(test_oid s2) s/4/A/ @@ Metadata ZAuthor: Thomas Rast <trast@inf.ethz.ch> Z @@ -246,7 +353,7 @@ test_expect_success 'file added and later removed' ' Z 7 + + ## new-file (new) ## - 3: 147e64e ! 3: 9a1db4d s/11/B/ + 3: $(test_oid t3) ! 3: $(test_oid s3) s/11/B/ @@ Metadata ZAuthor: Thomas Rast <trast@inf.ethz.ch> Z @@ -262,7 +369,7 @@ test_expect_success 'file added and later removed' ' Z 14 + + ## new-file (deleted) ## - 4: a63e992 = 4: fea3b5c s/12/B/ + 4: $(test_oid t4) = 4: $(test_oid s4) s/12/B/ EOF test_cmp expected actual ' @@ -275,8 +382,8 @@ test_expect_success 'no commits on one side' ' test_expect_success 'changed message' ' git range-diff --no-color topic...changed-message >actual && sed s/Z/\ /g >expected <<-EOF && - 1: 4de457d = 1: f686024 s/5/A/ - 2: fccce22 ! 2: 4ab067d s/4/A/ + 1: $(test_oid t1) = 1: $(test_oid m1) s/5/A/ + 2: $(test_oid t2) ! 2: $(test_oid m2) s/4/A/ @@ Metadata Z ## Commit message ## Z s/4/A/ @@ -286,16 +393,16 @@ test_expect_success 'changed message' ' Z ## file ## Z@@ Z 1 - 3: 147e64e = 3: b9cb956 s/11/B/ - 4: a63e992 = 4: 8add5f1 s/12/B/ + 3: $(test_oid t3) = 3: $(test_oid m3) s/11/B/ + 4: $(test_oid t4) = 4: $(test_oid m4) s/12/B/ EOF test_cmp expected actual ' test_expect_success 'dual-coloring' ' - sed -e "s|^:||" >expect <<-\EOF && - :<YELLOW>1: a4b3333 = 1: f686024 s/5/A/<RESET> - :<RED>2: f51d370 <RESET><YELLOW>!<RESET><GREEN> 2: 4ab067d<RESET><YELLOW> s/4/A/<RESET> + sed -e "s|^:||" >expect <<-EOF && + :<YELLOW>1: $(test_oid c1) = 1: $(test_oid m1) s/5/A/<RESET> + :<RED>2: $(test_oid c2) <RESET><YELLOW>!<RESET><GREEN> 2: $(test_oid m2)<RESET><YELLOW> s/4/A/<RESET> : <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET> : ## Commit message ##<RESET> : s/4/A/<RESET> @@ -305,7 +412,7 @@ test_expect_success 'dual-coloring' ' : ## file ##<RESET> : <CYAN> @@<RESET> : 1<RESET> - :<RED>3: 0559556 <RESET><YELLOW>!<RESET><GREEN> 3: b9cb956<RESET><YELLOW> s/11/B/<RESET> + :<RED>3: $(test_oid c3) <RESET><YELLOW>!<RESET><GREEN> 3: $(test_oid m3)<RESET><YELLOW> s/11/B/<RESET> : <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET> : 9<RESET> : 10<RESET> @@ -315,7 +422,7 @@ test_expect_success 'dual-coloring' ' : 12<RESET> : 13<RESET> : 14<RESET> - :<RED>4: d966c5c <RESET><YELLOW>!<RESET><GREEN> 4: 8add5f1<RESET><YELLOW> s/12/B/<RESET> + :<RED>4: $(test_oid c4) <RESET><YELLOW>!<RESET><GREEN> 4: $(test_oid m4)<RESET><YELLOW> s/12/B/<RESET> : <REVERSE><CYAN>@@<RESET> <RESET>file<RESET> : <CYAN> @@ file: A<RESET> : 9<RESET> diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index 704bbc6541..d3fa298c6a 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh @@ -66,8 +66,9 @@ test_expect_success 'show notes entry with %N' ' ' test_expect_success 'create reflog entry' ' + ref=$(git rev-parse --short refs/notes/commits) && cat <<-EOF >expect && - a1d8fa6 refs/notes/commits@{0}: notes: Notes added by '\''git notes add'\'' + $ref refs/notes/commits@{0}: notes: Notes added by '\''git notes add'\'' EOF git reflog show refs/notes/commits >actual && test_cmp expect actual @@ -134,8 +135,9 @@ test_expect_success 'can overwrite existing note with "git notes add -f"' ' ' test_expect_success 'show notes' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 7a4ca6ee52a974a66cbaa78e33214535dff1d691 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:14:13 2005 -0700 @@ -152,8 +154,9 @@ test_expect_success 'show notes' ' test_expect_success 'show multi-line notes' ' test_commit 3rd && MSG="b3${LF}c3c3c3c3${LF}d3d3d3" git notes add && + commit=$(git rev-parse HEAD) && cat >expect-multiline <<-EOF && - commit d07d62e5208f22eb5695e7eb47667dc8b9860290 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:15:13 2005 -0700 @@ -174,8 +177,9 @@ test_expect_success 'show -F notes' ' test_commit 4th && echo "xyzzy" >note5 && git notes add -F note5 && + commit=$(git rev-parse HEAD) && cat >expect-F <<-EOF && - commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:16:13 2005 -0700 @@ -198,10 +202,13 @@ test_expect_success 'Re-adding -F notes without -f fails' ' ' test_expect_success 'git log --pretty=raw does not show notes' ' + commit=$(git rev-parse HEAD) && + tree=$(git rev-parse HEAD^{tree}) && + parent=$(git rev-parse HEAD^) && cat >expect <<-EOF && - commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11 - tree 05ac65288c4c4b3b709a020ae94b2ece2f2201ae - parent d07d62e5208f22eb5695e7eb47667dc8b9860290 + commit $commit + tree $tree + parent $parent author A U Thor <author@example.com> 1112912173 -0700 committer C O Mitter <committer@example.com> 1112912173 -0700 @@ -291,8 +298,9 @@ test_expect_success 'git log --no-notes resets ref list' ' test_expect_success 'show -m notes' ' test_commit 5th && git notes add -m spam -m "foo${LF}bar${LF}baz" && + commit=$(git rev-parse HEAD) && cat >expect-m <<-EOF && - commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:17:13 2005 -0700 @@ -313,8 +321,9 @@ test_expect_success 'show -m notes' ' test_expect_success 'remove note with add -f -F /dev/null' ' git notes add -f -F /dev/null && + commit=$(git rev-parse HEAD) && cat >expect-rm-F <<-EOF && - commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:17:13 2005 -0700 @@ -356,14 +365,16 @@ test_expect_success 'create note with combination of -m and -F' ' test_expect_success 'remove note with "git notes remove"' ' git notes remove HEAD^ && git notes remove && + commit=$(git rev-parse HEAD) && + parent=$(git rev-parse HEAD^) && cat >expect-rm-remove <<-EOF && - commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:17:13 2005 -0700 ${indent}5th - commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11 + commit $parent Author: A U Thor <author@example.com> Date: Thu Apr 7 15:16:13 2005 -0700 @@ -459,9 +470,11 @@ test_expect_success 'removing with --stdin --ignore-missing' ' ' test_expect_success 'list notes with "git notes list"' ' - cat >expect <<-EOF && - c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691 - c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290 + commit_2=$(git rev-parse 2nd) && + commit_3=$(git rev-parse 3rd) && + sort -t" " -k2 >expect <<-EOF && + $(git rev-parse refs/notes/commits:$commit_2) $commit_2 + $(git rev-parse refs/notes/commits:$commit_3) $commit_3 EOF git notes list >actual && test_cmp expect actual @@ -474,7 +487,7 @@ test_expect_success 'list notes with "git notes"' ' test_expect_success 'list specific note with "git notes list <object>"' ' cat >expect <<-EOF && - c18dc024e14f08d18d14eea0d747ff692d66d6a3 + $(git rev-parse refs/notes/commits:$commit_3) EOF git notes list HEAD^^ >actual && test_cmp expect actual @@ -498,10 +511,11 @@ test_expect_success 'append to existing note with "git notes append"' ' ' test_expect_success '"git notes list" does not expand to "git notes list HEAD"' ' - cat >expect_list <<-EOF && - c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691 - 4b6ad22357cc8a1296720574b8d2fbc22fab0671 7f9ad8836c775acb134c0a055fc55fb4cd1ba361 - c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290 + commit_5=$(git rev-parse 5th) && + sort -t" " -k2 >expect_list <<-EOF && + $(git rev-parse refs/notes/commits:$commit_2) $commit_2 + $(git rev-parse refs/notes/commits:$commit_3) $commit_3 + $(git rev-parse refs/notes/commits:$commit_5) $commit_5 EOF git notes list >actual && test_cmp expect_list actual @@ -531,8 +545,9 @@ test_expect_success 'appending empty string to non-existing note does not create test_expect_success 'create other note on a different notes ref (setup)' ' test_commit 6th && GIT_NOTES_REF="refs/notes/other" git notes add -m "other note" && + commit=$(git rev-parse HEAD) && cat >expect-not-other <<-EOF && - commit 2c125331118caba0ff8238b7f4958ac6e93fe39c + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:18:13 2005 -0700 @@ -569,8 +584,10 @@ test_expect_success 'Do not show note when core.notesRef is overridden' ' ' test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' ' + commit=$(git rev-parse HEAD) && + parent=$(git rev-parse HEAD^) && cat >expect-both <<-EOF && - commit 2c125331118caba0ff8238b7f4958ac6e93fe39c + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:18:13 2005 -0700 @@ -582,7 +599,7 @@ test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' ' Notes (other): ${indent}other note - commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361 + commit $parent Author: A U Thor <author@example.com> Date: Thu Apr 7 15:17:13 2005 -0700 @@ -616,8 +633,9 @@ test_expect_success 'notes.displayRef can be given more than once' ' ' test_expect_success 'notes.displayRef respects order' ' + commit=$(git rev-parse HEAD) && cat >expect-both-reversed <<-EOF && - commit 2c125331118caba0ff8238b7f4958ac6e93fe39c + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:18:13 2005 -0700 @@ -642,14 +660,16 @@ test_expect_success 'GIT_NOTES_DISPLAY_REF works' ' ' test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' ' + commit=$(git rev-parse HEAD) && + parent=$(git rev-parse HEAD^) && cat >expect-none <<-EOF && - commit 2c125331118caba0ff8238b7f4958ac6e93fe39c + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:18:13 2005 -0700 ${indent}6th - commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361 + commit $parent Author: A U Thor <author@example.com> Date: Thu Apr 7 15:17:13 2005 -0700 @@ -666,8 +686,9 @@ test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' ' ' test_expect_success '--no-standard-notes' ' + commit=$(git rev-parse HEAD) && cat >expect-commits <<-EOF && - commit 2c125331118caba0ff8238b7f4958ac6e93fe39c + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:18:13 2005 -0700 @@ -712,8 +733,10 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' ' ' test_expect_success 'create note from other note with "git notes add -C"' ' + test_commit 7th && + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit fb01e0ca8c33b6cc0c6451dde747f97df567cb5c + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:19:13 2005 -0700 @@ -722,7 +745,6 @@ test_expect_success 'create note from other note with "git notes add -C"' ' Notes: ${indent}order test EOF - test_commit 7th && git notes add -C $(git notes list HEAD^) && git log -1 >actual && test_cmp expect actual && @@ -744,8 +766,9 @@ test_expect_success 'create note from non-blob with "git notes add -C" fails' ' ' test_expect_success 'create note from blob with "git notes add -C" reuses blob id' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 9a4c31c7f722b5d517e92c64e932dd751e1413bf + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:20:13 2005 -0700 @@ -762,8 +785,10 @@ test_expect_success 'create note from blob with "git notes add -C" reuses blob i ' test_expect_success 'create note from other note with "git notes add -c"' ' + test_commit 9th && + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:21:13 2005 -0700 @@ -772,7 +797,6 @@ test_expect_success 'create note from other note with "git notes add -c"' ' Notes: ${indent}yet another note EOF - test_commit 9th && MSG="yet another note" git notes add -c $(git notes list HEAD^^) && git log -1 >actual && test_cmp expect actual @@ -785,8 +809,9 @@ test_expect_success 'create note from non-existing note with "git notes add -c" ' test_expect_success 'append to note from other note with "git notes append -C"' ' + commit=$(git rev-parse HEAD^) && cat >expect <<-EOF && - commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:21:13 2005 -0700 @@ -803,8 +828,9 @@ test_expect_success 'append to note from other note with "git notes append -C"' ' test_expect_success 'create note from other note with "git notes append -c"' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:22:13 2005 -0700 @@ -819,8 +845,9 @@ test_expect_success 'create note from other note with "git notes append -c"' ' ' test_expect_success 'append to note from other note with "git notes append -c"' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:22:13 2005 -0700 @@ -837,8 +864,10 @@ test_expect_success 'append to note from other note with "git notes append -c"' ' test_expect_success 'copy note with "git notes copy"' ' + test_commit 11th && + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:23:13 2005 -0700 @@ -849,7 +878,6 @@ test_expect_success 'copy note with "git notes copy"' ' ${indent} ${indent}yet another note EOF - test_commit 11th && git notes copy HEAD^ HEAD && git log -1 >actual && test_cmp expect actual && @@ -864,8 +892,9 @@ test_expect_success 'prevent overwrite with "git notes copy"' ' ' test_expect_success 'allow overwrite with "git notes copy -f"' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:23:13 2005 -0700 @@ -889,8 +918,10 @@ test_expect_success 'cannot copy note from object without notes' ' ' test_expect_success 'git notes copy --stdin' ' + commit=$(git rev-parse HEAD) && + parent=$(git rev-parse HEAD^) && cat >expect <<-EOF && - commit e871aa61182b1d95d0a6fb75445d891722863b6b + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:25:13 2005 -0700 @@ -901,7 +932,7 @@ test_expect_success 'git notes copy --stdin' ' ${indent} ${indent}yet another note - commit 65e263ded02ae4e8839bc151095113737579dc12 + commit $parent Author: A U Thor <author@example.com> Date: Thu Apr 7 15:24:13 2005 -0700 @@ -922,21 +953,23 @@ test_expect_success 'git notes copy --stdin' ' ' test_expect_success 'git notes copy --for-rewrite (unconfigured)' ' + test_commit 14th && + test_commit 15th && + commit=$(git rev-parse HEAD) && + parent=$(git rev-parse HEAD^) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 ${indent}15th - commit 07c85d77059393ed0154b8c96906547a59dfcddd + commit $parent Author: A U Thor <author@example.com> Date: Thu Apr 7 15:26:13 2005 -0700 ${indent}14th EOF - test_commit 14th && - test_commit 15th && (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) && echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) | git notes copy --for-rewrite=foo && @@ -945,8 +978,10 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' ' ' test_expect_success 'git notes copy --for-rewrite (enabled)' ' + commit=$(git rev-parse HEAD) && + parent=$(git rev-parse HEAD^) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 @@ -957,7 +992,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' ' ${indent} ${indent}yet another note - commit 07c85d77059393ed0154b8c96906547a59dfcddd + commit $parent Author: A U Thor <author@example.com> Date: Thu Apr 7 15:26:13 2005 -0700 @@ -986,8 +1021,9 @@ test_expect_success 'git notes copy --for-rewrite (disabled)' ' ' test_expect_success 'git notes copy --for-rewrite (overwrite)' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 @@ -1015,8 +1051,9 @@ test_expect_success 'git notes copy --for-rewrite (ignore)' ' ' test_expect_success 'git notes copy --for-rewrite (append)' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 @@ -1037,8 +1074,9 @@ test_expect_success 'git notes copy --for-rewrite (append)' ' ' test_expect_success 'git notes copy --for-rewrite (append two to one)' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 @@ -1075,8 +1113,9 @@ test_expect_success 'git notes copy --for-rewrite (append empty)' ' ' test_expect_success 'GIT_NOTES_REWRITE_MODE works' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 @@ -1095,8 +1134,9 @@ test_expect_success 'GIT_NOTES_REWRITE_MODE works' ' ' test_expect_success 'GIT_NOTES_REWRITE_REF works' ' + commit=$(git rev-parse HEAD) && cat >expect <<-EOF && - commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89 + commit $commit Author: A U Thor <author@example.com> Date: Thu Apr 7 15:27:13 2005 -0700 diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh index 54460beec4..831f83d211 100755 --- a/t/t3305-notes-fanout.sh +++ b/t/t3305-notes-fanout.sh @@ -35,15 +35,10 @@ test_expect_success 'many notes created with git-notes triggers fanout' ' git ls-tree -r --name-only refs/notes/commits | while read path do - case "$path" in - ??/??????????????????????????????????????) - : true - ;; - *) + echo $path | grep "^../[0-9a-f]*$" || { echo "Invalid path \"$path\"" && - return 1 - ;; - esac + return 1; + } done ' @@ -77,15 +72,10 @@ test_expect_success 'deleting most notes triggers fanout consolidation' ' git ls-tree -r --name-only refs/notes/commits | while read path do - case "$path" in - ????????????????????????????????????????) - : true - ;; - *) + echo $path | grep -v "^../.*" || { echo "Invalid path \"$path\"" && - return 1 - ;; - esac + return 1; + } done ' diff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh index 61748088eb..8f4102ff9e 100755 --- a/t/t3306-notes-prune.sh +++ b/t/t3306-notes-prune.sh @@ -11,23 +11,26 @@ test_expect_success 'setup: create a few commits with notes' ' test_tick && git commit -m 1st && git notes add -m "Note #1" && + first=$(git rev-parse HEAD) && : > file2 && git add file2 && test_tick && git commit -m 2nd && git notes add -m "Note #2" && + second=$(git rev-parse HEAD) && : > file3 && git add file3 && test_tick && git commit -m 3rd && - COMMIT_FILE=.git/objects/5e/e1c35e83ea47cd3cc4f8cbee0568915fbbbd29 && + third=$(git rev-parse HEAD) && + COMMIT_FILE=$(echo $third | sed "s!^..!.git/objects/&/!") && test -f $COMMIT_FILE && test-tool chmtime =+0 $COMMIT_FILE && git notes add -m "Note #3" ' cat > expect <<END_OF_LOG -commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 +commit $third Author: A U Thor <author@example.com> Date: Thu Apr 7 15:15:13 2005 -0700 @@ -36,7 +39,7 @@ Date: Thu Apr 7 15:15:13 2005 -0700 Notes: Note #3 -commit 08341ad9e94faa089d60fd3f523affb25c6da189 +commit $second Author: A U Thor <author@example.com> Date: Thu Apr 7 15:14:13 2005 -0700 @@ -45,7 +48,7 @@ Date: Thu Apr 7 15:14:13 2005 -0700 Notes: Note #2 -commit ab5f302035f2e7aaf04265f08b42034c23256e1f +commit $first Author: A U Thor <author@example.com> Date: Thu Apr 7 15:13:13 2005 -0700 @@ -70,16 +73,16 @@ test_expect_success 'remove some commits' ' test_expect_success 'verify that commits are gone' ' - test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 && - git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 && - git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f + test_must_fail git cat-file -p $third && + git cat-file -p $second && + git cat-file -p $first ' test_expect_success 'verify that notes are still present' ' - git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 && - git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 && - git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f + git notes show $third && + git notes show $second && + git notes show $first ' test_expect_success 'prune -n does not remove notes' ' @@ -90,13 +93,10 @@ test_expect_success 'prune -n does not remove notes' ' test_cmp expect actual ' -cat > expect <<EOF -5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 -EOF test_expect_success 'prune -n lists prunable notes' ' - + echo $third >expect && git notes prune -n > actual && test_cmp expect actual ' @@ -109,9 +109,9 @@ test_expect_success 'prune notes' ' test_expect_success 'verify that notes are gone' ' - test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 && - git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 && - git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f + test_must_fail git notes show $third && + git notes show $second && + git notes show $first ' test_expect_success 'remove some commits' ' @@ -121,21 +121,18 @@ test_expect_success 'remove some commits' ' git gc --prune=now ' -cat > expect <<EOF -08341ad9e94faa089d60fd3f523affb25c6da189 -EOF - test_expect_success 'prune -v notes' ' + echo $second >expect && git notes prune -v > actual && test_cmp expect actual ' test_expect_success 'verify that notes are gone' ' - test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 && - test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 && - git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f + test_must_fail git notes show $third && + test_must_fail git notes show $second && + git notes show $first ' test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 80b23fd326..ab18ac5f28 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -295,12 +295,48 @@ test_expect_success 'rebase --am and --show-current-patch' ' echo two >>init.t && git commit -a -m two && git tag two && - test_must_fail git rebase --onto init HEAD^ && + test_must_fail git rebase -f --onto init HEAD^ && GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr && grep "show.*$(git rev-parse two)" stderr ) ' +test_expect_success 'rebase --am and .gitattributes' ' + test_create_repo attributes && + ( + cd attributes && + test_commit init && + git config filter.test.clean "sed -e '\''s/smudged/clean/g'\''" && + git config filter.test.smudge "sed -e '\''s/clean/smudged/g'\''" && + + test_commit second && + git checkout -b test HEAD^ && + + echo "*.txt filter=test" >.gitattributes && + git add .gitattributes && + test_commit third && + + echo "This text is smudged." >a.txt && + git add a.txt && + test_commit fourth && + + git checkout -b removal HEAD^ && + git rm .gitattributes && + git add -u && + test_commit fifth && + git cherry-pick test && + + git checkout test && + git rebase master && + grep "smudged" a.txt && + + git checkout removal && + git reset --hard && + git rebase master && + grep "clean" a.txt + ) +' + test_expect_success 'rebase--merge.sh and --show-current-patch' ' test_create_repo conflict-merge && ( diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 461dd539ff..29a35840ed 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -29,9 +29,6 @@ Initial setup: . "$TEST_DIRECTORY"/lib-rebase.sh -# WARNING: Modifications to the initial repository can change the SHA ID used -# in the expect2 file for the 'stop on conflicting pick' test. - test_expect_success 'setup' ' test_commit A file1 && test_commit B file1 && @@ -155,8 +152,6 @@ test_expect_success 'rebase -x with empty command fails' ' test_i18ncmp expected actual ' -LF=' -' test_expect_success 'rebase -x with newline in command fails' ' test_when_finished "git rebase --abort ||:" && test_must_fail env git rebase -x "a${LF}b" @ 2>actual && @@ -233,25 +228,28 @@ test_expect_success 'exchange two commits' ' set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 && test H = $(git cat-file commit HEAD^ | sed -ne \$p) && - test G = $(git cat-file commit HEAD | sed -ne \$p) + test G = $(git cat-file commit HEAD | sed -ne \$p) && + blob1=$(git rev-parse --short HEAD^:file1) && + blob2=$(git rev-parse --short HEAD:file1) && + commit=$(git rev-parse --short HEAD) ' test_expect_success 'stop on conflicting pick' ' - cat >expect <<-\EOF && + cat >expect <<-EOF && diff --git a/file1 b/file1 - index f70f10e..fd79235 100644 + index $blob1..$blob2 100644 --- a/file1 +++ b/file1 @@ -1 +1 @@ -A +G EOF - cat >expect2 <<-\EOF && + cat >expect2 <<-EOF && <<<<<<< HEAD D ======= G - >>>>>>> 5d18e54... G + >>>>>>> $commit... G EOF git tag new-branch1 && set_fake_editor && @@ -1003,7 +1001,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' ' git checkout B && set_fake_editor && test_must_fail env FAKE_LINES="2" git rebase -i --root && - git cat-file commit HEAD | grep "^tree 4b825dc642cb" && + git cat-file commit HEAD | grep "^tree $EMPTY_TREE" && git rebase --abort ' @@ -1058,7 +1056,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int git reset --hard && git checkout conflict-branch && set_fake_editor && - test_must_fail git rebase --onto HEAD~2 HEAD~ && + test_must_fail git rebase -f --onto HEAD~2 HEAD~ && test_must_fail git rebase --edit-todo && git rebase --abort ' @@ -1161,7 +1159,7 @@ test_expect_success 'rebase -i error on commits with \ in message' ' test_expect_code 1 grep " emp" error ' -test_expect_success 'short SHA-1 setup' ' +test_expect_success SHA1 'short SHA-1 setup' ' test_when_finished "git checkout master" && git checkout --orphan collide && git rm -rf . && @@ -1173,7 +1171,7 @@ test_expect_success 'short SHA-1 setup' ' ) ' -test_expect_success 'short SHA-1 collide' ' +test_expect_success SHA1 'short SHA-1 collide' ' test_when_finished "reset_rebase && git checkout master" && git checkout collide && ( @@ -1419,7 +1417,6 @@ test_expect_success 'editor saves as CR/LF' ' ) ' -SQ="'" test_expect_success 'rebase -i --gpg-sign=<key-id>' ' test_when_finished "test_might_fail git rebase --abort" && set_fake_editor && diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh index ddf2f64853..9c2548423b 100755 --- a/t/t3416-rebase-onto-threedots.sh +++ b/t/t3416-rebase-onto-threedots.sh @@ -99,7 +99,64 @@ test_expect_success 'rebase -i --onto master...side' ' git checkout side && git reset --hard K && + set_fake_editor && test_must_fail git rebase -i --onto master...side J ' +test_expect_success 'rebase --keep-base --onto incompatible' ' + test_must_fail git rebase --keep-base --onto master... +' + +test_expect_success 'rebase --keep-base --root incompatible' ' + test_must_fail git rebase --keep-base --root +' + +test_expect_success 'rebase --keep-base master from topic' ' + git reset --hard && + git checkout topic && + git reset --hard G && + + git rebase --keep-base master && + git rev-parse C >base.expect && + git merge-base master HEAD >base.actual && + test_cmp base.expect base.actual && + + git rev-parse HEAD~2 >actual && + git rev-parse C^0 >expect && + test_cmp expect actual +' + +test_expect_success 'rebase --keep-base master from side' ' + git reset --hard && + git checkout side && + git reset --hard K && + + test_must_fail git rebase --keep-base master +' + +test_expect_success 'rebase -i --keep-base master from topic' ' + git reset --hard && + git checkout topic && + git reset --hard G && + + set_fake_editor && + EXPECT_COUNT=2 git rebase -i --keep-base master && + git rev-parse C >base.expect && + git merge-base master HEAD >base.actual && + test_cmp base.expect base.actual && + + git rev-parse HEAD~2 >actual && + git rev-parse C^0 >expect && + test_cmp expect actual +' + +test_expect_success 'rebase -i --keep-base master from side' ' + git reset --hard && + git checkout side && + git reset --hard K && + + set_fake_editor && + test_must_fail git rebase -i --keep-base master +' + test_done diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index 4eff14dae5..7a2da972fd 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -120,6 +120,20 @@ test_expect_success REBASE_P 'rebase passes merge strategy options correctly' ' git rebase --continue ' +test_expect_success 'rebase -r passes merge strategy options correctly' ' + rm -fr .git/rebase-* && + git reset --hard commit-new-file-F3-on-topic-branch && + test_commit merge-theirs && + git reset --hard HEAD^ && + test_commit some-other-commit && + test_tick && + git merge --no-ff merge-theirs && + FAKE_LINES="1 3 edit 4 5 7 8 9" git rebase -i -f -r -m \ + -s recursive --strategy-option=theirs HEAD~2 && + test_commit force-change-ours && + git rebase --continue +' + test_expect_success '--skip after failed fixup cleans commit message' ' test_when_finished "test_might_fail git rebase --abort" && git checkout -b with-conflicting-fixup && diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh index b8f4d03467..5f7e73cf83 100755 --- a/t/t3420-rebase-autostash.sh +++ b/t/t3420-rebase-autostash.sh @@ -37,7 +37,6 @@ test_expect_success setup ' create_expected_success_am () { cat >expected <<-EOF $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) - HEAD is now at $(git rev-parse --short feature-branch) third commit First, rewinding head to replay your work on top of it... Applying: second commit Applying: third commit @@ -48,7 +47,6 @@ create_expected_success_am () { create_expected_success_interactive () { q_to_cr >expected <<-EOF $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) - HEAD is now at $(git rev-parse --short feature-branch) third commit Applied autostash. Successfully rebased and updated refs/heads/rebased-feature-branch. EOF @@ -57,7 +55,6 @@ create_expected_success_interactive () { create_expected_failure_am () { cat >expected <<-EOF $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) - HEAD is now at $(git rev-parse --short feature-branch) third commit First, rewinding head to replay your work on top of it... Applying: second commit Applying: third commit @@ -70,7 +67,6 @@ create_expected_failure_am () { create_expected_failure_interactive () { cat >expected <<-EOF $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual) - HEAD is now at $(git rev-parse --short feature-branch) third commit Applying autostash resulted in conflicts. Your changes are safe in the stash. You can run "git stash pop" or "git stash drop" at any time. @@ -306,4 +302,12 @@ test_expect_success 'branch is left alone when possible' ' test unchanged-branch = "$(git rev-parse --abbrev-ref HEAD)" ' +test_expect_success 'never change active branch' ' + git checkout -b not-the-feature-branch unrelated-onto-branch && + test_when_finished "git reset --hard && git checkout master" && + echo changed >file0 && + git rebase --autostash not-the-feature-branch feature-branch && + test_cmp_rev not-the-feature-branch unrelated-onto-branch +' + test_done diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh index 7274dca40b..b847064f91 100755 --- a/t/t3421-rebase-topology-linear.sh +++ b/t/t3421-rebase-topology-linear.sh @@ -31,6 +31,16 @@ test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase success -p +test_expect_success 'setup branches and remote tracking' ' + git tag -l >tags && + for tag in $(cat tags) + do + git branch branch-$tag $tag || return 1 + done && + git remote add origin "file://$PWD" && + git fetch origin +' + test_run_rebase () { result=$1 shift @@ -57,6 +67,7 @@ test_run_rebase () { " } test_run_rebase success '' +test_run_rebase success --fork-point test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase failure -p @@ -64,6 +75,23 @@ test_have_prereq !REBASE_P || test_run_rebase failure -p test_run_rebase () { result=$1 shift + test_expect_$result "rebase $* -f rewrites even if remote upstream is an ancestor" " + reset_rebase && + git rebase $* -f branch-b branch-e && + ! test_cmp_rev branch-e origin/branch-e && + test_cmp_rev branch-b HEAD~2 && + test_linear_range 'd e' branch-b.. + " +} +test_run_rebase success '' +test_run_rebase success --fork-point +test_run_rebase success -m +test_run_rebase success -i +test_have_prereq !REBASE_P || test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift test_expect_$result "rebase $* fast-forwards from ancestor of upstream" " reset_rebase && git rebase $* e b && @@ -71,6 +99,7 @@ test_run_rebase () { " } test_run_rebase success '' +test_run_rebase success --fork-point test_run_rebase success -m test_run_rebase success -i test_have_prereq !REBASE_P || test_run_rebase success -p diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh index a5868ea152..50e7960702 100755 --- a/t/t3422-rebase-incompatible-options.sh +++ b/t/t3422-rebase-incompatible-options.sh @@ -76,14 +76,4 @@ test_expect_success REBASE_P \ test_must_fail git rebase --preserve-merges --rebase-merges A ' -test_expect_success '--rebase-merges incompatible with --strategy' ' - git checkout B^0 && - test_must_fail git rebase --rebase-merges -s resolve A -' - -test_expect_success '--rebase-merges incompatible with --strategy-option' ' - git checkout B^0 && - test_must_fail git rebase --rebase-merges -Xignore-space-change A -' - test_done diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index d8640522a0..bec48e6a1f 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -11,113 +11,99 @@ commit_message() { git log --pretty=format:%s -1 "$1" } +# There are a few bugs in the rebase with regards to the subtree strategy, and +# this test script tries to document them. First, the following commit history +# is generated (the onelines are shown, time flows from left to right): +# +# master1 - master2 - master3 +# \ +# README ---------------------- Add subproject master - master4 - files_subtree/master5 +# +# Where the merge moves the files master[123].t into the subdirectory +# files_subtree/ and master4 as well as files_subtree/master5 add files to that +# directory directly. +# +# Then, in subsequent test cases, `git filter-branch` is used to distill just +# the commits that touch files_subtree/. To give it a final pre-rebase touch, +# an empty commit is added on top. The pre-rebase commit history looks like +# this: +# +# Add subproject master - master4 - files_subtree/master5 - Empty commit +# +# where the root commit adds three files: master1.t, master2.t and master3.t. +# +# This commit history is then rebased onto `master3` with the +# `-Xsubtree=files_subtree` option in three different ways: +# +# 1. using `--preserve-merges` +# 2. using `--preserve-merges` and --keep-empty +# 3. without specifying a rebase backend + test_expect_success 'setup' ' test_commit README && - mkdir files && - ( - cd files && - git init && - test_commit master1 && - test_commit master2 && - test_commit master3 - ) && - git fetch files master && - git branch files-master FETCH_HEAD && - git read-tree --prefix=files_subtree files-master && - git checkout -- files_subtree && - tree=$(git write-tree) && - head=$(git rev-parse HEAD) && - rev=$(git rev-parse --verify files-master^0) && - commit=$(git commit-tree -p $head -p $rev -m "Add subproject master" $tree) && - git update-ref HEAD $commit && - ( - cd files_subtree && - test_commit master4 - ) && - test_commit files_subtree/master5 -' -# FAILURE: Does not preserve master4. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --preserve-merges --onto commit 4' ' - reset_rebase && - git checkout -b rebase-preserve-merges-4 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && - git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master4" + git init files && + test_commit -C files master1 && + test_commit -C files master2 && + test_commit -C files master3 && + + : perform subtree merge into files_subtree/ && + git fetch files refs/heads/master:refs/heads/files-master && + git merge -s ours --no-commit --allow-unrelated-histories \ + files-master && + git read-tree --prefix=files_subtree -u files-master && + git commit -m "Add subproject master" && + + : add two extra commits to rebase && + test_commit -C files_subtree master4 && + test_commit files_subtree/master5 && + + git checkout -b to-rebase && + git fast-export --no-data HEAD -- files_subtree/ | + sed -e "s%\([0-9a-f]\{40\} \)files_subtree/%\1%" | + git fast-import --force --quiet && + git reset --hard && + git commit -m "Empty commit" --allow-empty ' -# FAILURE: Does not preserve master5. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --preserve-merges --onto commit 5' ' +# FAILURE: Does not preserve master4. +test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' ' reset_rebase && - git checkout -b rebase-preserve-merges-5 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-preserve-merges to-rebase && git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && + verbose test "$(commit_message HEAD~)" = "master4" && verbose test "$(commit_message HEAD)" = "files_subtree/master5" ' # FAILURE: Does not preserve master4. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 4' ' - reset_rebase && - git checkout -b rebase-keep-empty-4 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && - git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" -' - -# FAILURE: Does not preserve master5. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 5' ' - reset_rebase && - git checkout -b rebase-keep-empty-5 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && - git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master5" -' - -# FAILURE: Does not preserve Empty. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --keep-empty --preserve-merges --onto empty commit' ' +test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' ' reset_rebase && - git checkout -b rebase-keep-empty-empty master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-keep-empty to-rebase && git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && + verbose test "$(commit_message HEAD~2)" = "master4" && + verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" ' -# FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto commit 4' ' +test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' ' reset_rebase && - git checkout -b rebase-onto-4 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && - git rebase -Xsubtree=files_subtree --onto files-master master && - verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" + git checkout -b rebase-onto to-rebase && + test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master && + : first pick results in no changes && + git rebase --continue && + verbose test "$(commit_message HEAD~2)" = "master4" && + verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && + verbose test "$(commit_message HEAD)" = "Empty commit" ' -# FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto commit 5' ' - reset_rebase && - git checkout -b rebase-onto-5 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && - git rebase -Xsubtree=files_subtree --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master5" -' -# FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto empty commit' ' +test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' ' reset_rebase && - git checkout -b rebase-onto-empty master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && - git rebase -Xsubtree=files_subtree --onto files-master master && + git checkout -b rebase-merges-onto to-rebase && + test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root && + : first pick results in no changes && + git rebase --continue && + verbose test "$(commit_message HEAD~2)" = "master4" && + verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" ' diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 7b6c4847ad..9efcf4808a 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -37,20 +37,27 @@ test_expect_success 'setup' ' test_commit A && git checkout -b first && test_commit B && + b=$(git rev-parse --short HEAD) && git checkout master && test_commit C && + c=$(git rev-parse --short HEAD) && test_commit D && + d=$(git rev-parse --short HEAD) && git merge --no-commit B && test_tick && git commit -m E && git tag -m E E && + e=$(git rev-parse --short HEAD) && git checkout -b second C && test_commit F && + f=$(git rev-parse --short HEAD) && test_commit G && + g=$(git rev-parse --short HEAD) && git checkout master && git merge --no-commit G && test_tick && git commit -m H && + h=$(git rev-parse --short HEAD) && git tag -m H H && git checkout A && test_commit conflicting-G G.t @@ -93,24 +100,24 @@ test_expect_success 'create completely different structure' ' ' test_expect_success 'generate correct todo list' ' - cat >expect <<-\EOF && + cat >expect <<-EOF && label onto reset onto - pick d9df450 B + pick $b B label E reset onto - pick 5dee784 C + pick $c C label branch-point - pick ca2c861 F - pick 088b00a G + pick $f F + pick $g G label H reset branch-point # C - pick 12bd07b D - merge -C 2051b56 E # E - merge -C 233d48a H # H + pick $d D + merge -C $e E # E + merge -C $h H # H EOF @@ -151,7 +158,6 @@ test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' ' test_path_is_file .git/rebase-merge/patch ' -SQ="'" test_expect_success 'failed `merge <branch>` does not crash' ' test_when_finished "test_might_fail git rebase --abort" && git checkout conflicting-G && @@ -441,4 +447,25 @@ test_expect_success '--continue after resolving conflicts after a merge' ' test_path_is_missing .git/MERGE_HEAD ' +test_expect_success '--rebase-merges with strategies' ' + git checkout -b with-a-strategy F && + test_tick && + git merge -m "Merge conflicting-G" conflicting-G && + + : first, test with a merge strategy option && + git rebase -ir -Xtheirs G && + echo conflicting-G >expect && + test_cmp expect G.t && + + : now, try with a merge strategy other than recursive && + git reset --hard @{1} && + write_script git-merge-override <<-\EOF && + echo overridden$1 >>G.t + git add G.t + EOF + PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G && + test_write_lines G overridden--xopt >expect && + test_cmp expect G.t +' + test_done diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh new file mode 100755 index 0000000000..78851b9a2a --- /dev/null +++ b/t/t3431-rebase-fork-point.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# +# Copyright (c) 2019 Denton Liu +# + +test_description='git rebase --fork-point test' + +. ./test-lib.sh + +# A---B---D---E (master) +# \ +# C*---F---G (side) +# +# C was formerly part of master but master was rewound to remove C +# +test_expect_success setup ' + test_commit A && + test_commit B && + test_commit C && + git branch -t side && + git reset --hard HEAD^ && + test_commit D && + test_commit E && + git checkout side && + test_commit F && + test_commit G +' + +test_rebase () { + expected="$1" && + shift && + test_expect_success "git rebase $*" " + git checkout master && + git reset --hard E && + git checkout side && + git reset --hard G && + git rebase $* && + test_write_lines $expected >expect && + git log --pretty=%s >actual && + test_cmp expect actual + " +} + +test_rebase 'G F E D B A' +test_rebase 'G F D B A' --onto D +test_rebase 'G F B A' --keep-base +test_rebase 'G F C E D B A' --no-fork-point +test_rebase 'G F C D B A' --no-fork-point --onto D +test_rebase 'G F C B A' --no-fork-point --keep-base +test_rebase 'G F E D B A' --fork-point refs/heads/master +test_rebase 'G F D B A' --fork-point --onto D refs/heads/master +test_rebase 'G F B A' --fork-point --keep-base refs/heads/master +test_rebase 'G F C E D B A' refs/heads/master +test_rebase 'G F C D B A' --onto D refs/heads/master +test_rebase 'G F C B A' --keep-base refs/heads/master + +test_done diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh new file mode 100755 index 0000000000..034ffc7e76 --- /dev/null +++ b/t/t3432-rebase-fast-forward.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# +# Copyright (c) 2019 Denton Liu +# + +test_description='ensure rebase fast-forwards commits when possible' + +. ./test-lib.sh + +test_expect_success setup ' + test_commit A && + test_commit B && + test_commit C && + test_commit D && + git checkout -t -b side +' + +test_rebase_same_head () { + status_n="$1" && + shift && + what_n="$1" && + shift && + cmp_n="$1" && + shift && + status_f="$1" && + shift && + what_f="$1" && + shift && + cmp_f="$1" && + shift && + test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" && + test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*" +} + +test_rebase_same_head_ () { + status="$1" && + shift && + what="$1" && + shift && + cmp="$1" && + shift && + flag="$1" + shift && + test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" " + oldhead=\$(git rev-parse HEAD) && + test_when_finished 'git reset --hard \$oldhead' && + git rebase$flag $* >stdout && + if test $what = work + then + # Must check this case first, for 'is up to + # date, rebase forced[...]rewinding head' cases + test_i18ngrep 'rewinding head' stdout + elif test $what = noop + then + test_i18ngrep 'is up to date' stdout && + test_i18ngrep ! 'rebase forced' stdout + elif test $what = noop-force + then + test_i18ngrep 'is up to date, rebase forced' stdout + fi && + newhead=\$(git rev-parse HEAD) && + if test $cmp = same + then + test_cmp_rev \$oldhead \$newhead + elif test $cmp = diff + then + ! test_cmp_rev \$oldhead \$newhead + fi + " +} + +changes='no changes' +test_rebase_same_head success noop same success work same +test_rebase_same_head success noop same success noop-force same master +test_rebase_same_head success noop same success noop-force diff --onto B B +test_rebase_same_head success noop same success noop-force diff --onto B... B +test_rebase_same_head success noop same success noop-force same --onto master... master +test_rebase_same_head success noop same success noop-force same --keep-base master +test_rebase_same_head success noop same success noop-force same --keep-base +test_rebase_same_head success noop same success noop-force same --no-fork-point +test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point +test_rebase_same_head success noop same success work same --fork-point master +test_rebase_same_head success noop same success work diff --fork-point --onto B B +test_rebase_same_head success noop same success work diff --fork-point --onto B... B +test_rebase_same_head success noop same success work same --fork-point --onto master... master +test_rebase_same_head success noop same success work same --keep-base --keep-base master + +test_expect_success 'add work same to side' ' + test_commit E +' + +changes='our changes' +test_rebase_same_head success noop same success work same +test_rebase_same_head success noop same success noop-force same master +test_rebase_same_head success noop same success noop-force diff --onto B B +test_rebase_same_head success noop same success noop-force diff --onto B... B +test_rebase_same_head success noop same success noop-force same --onto master... master +test_rebase_same_head success noop same success noop-force same --keep-base master +test_rebase_same_head success noop same success noop-force same --keep-base +test_rebase_same_head success noop same success noop-force same --no-fork-point +test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point +test_rebase_same_head success noop same success work same --fork-point master +test_rebase_same_head success noop same success work diff --fork-point --onto B B +test_rebase_same_head success noop same success work diff --fork-point --onto B... B +test_rebase_same_head success noop same success work same --fork-point --onto master... master +test_rebase_same_head success noop same success work same --fork-point --keep-base master + +test_expect_success 'add work same to upstream' ' + git checkout master && + test_commit F && + git checkout side +' + +changes='our and their changes' +test_rebase_same_head success noop same success noop-force diff --onto B B +test_rebase_same_head success noop same success noop-force diff --onto B... B +test_rebase_same_head success noop same success work diff --onto master... master +test_rebase_same_head success noop same success work diff --keep-base master +test_rebase_same_head success noop same success work diff --keep-base +test_rebase_same_head failure work same success work diff --fork-point --onto B B +test_rebase_same_head failure work same success work diff --fork-point --onto B... B +test_rebase_same_head success noop same success work diff --fork-point --onto master... master +test_rebase_same_head success noop same success work diff --fork-point --keep-base master + +test_done diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh index 127dd0082f..9d5adbc130 100755 --- a/t/t3506-cherry-pick-ff.sh +++ b/t/t3506-cherry-pick-ff.sh @@ -16,7 +16,11 @@ test_expect_success setup ' git add file1 && test_tick && git commit -m "second" && - git tag second + git tag second && + test_oid_cache <<-EOF + cp_ff sha1:1df192cd8bc58a2b275d842cede4d221ad9000d1 + cp_ff sha256:e70d6b7fc064bddb516b8d512c9057094b96ce6ff08e12080acc4fe7f1d60a1d + EOF ' test_expect_success 'cherry-pick using --ff fast forwards' ' @@ -102,7 +106,7 @@ test_expect_success 'cherry pick a root commit with --ff' ' git add file2 && git commit --amend -m "file2" && git cherry-pick --ff first && - test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1" + test "$(git rev-parse --verify HEAD)" = "$(test_oid cp_ff)" ' test_expect_success 'cherry-pick --ff on unborn branch' ' diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 66282a720e..8c8cca5bfb 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -240,12 +240,14 @@ test_expect_success 'refresh index before checking if it is up-to-date' ' ' test_expect_success 'choking "git rm" should not let it die with cruft' ' + test_oid_init && git reset -q --hard && test_when_finished "rm -f .git/index.lock && git reset -q --hard" && i=0 && + hash=$(test_oid deadbeef) && while test $i -lt 12000 do - echo "100644 1234567890123456789012345678901234567890 0 some-file-$i" + echo "100644 $hash 0 some-file-$i" i=$(( $i + 1 )) done | git update-index --index-info && git rm -n "some-file-*" | : && diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh index 8eb47942e2..64dcc5ec28 100755 --- a/t/t3800-mktag.sh +++ b/t/t3800-mktag.sh @@ -23,6 +23,7 @@ check_verify_failure () { # first create a commit, so we have a valid object/type # for the tag. test_expect_success 'setup' ' + test_oid_init && echo Hello >A && git update-index --add A && git commit -m "Initial commit" && @@ -69,28 +70,28 @@ check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$' # 4. type line label check cat >tag.sig <<EOF -object 779e9b33986b1c2670fff52c5067603117b3e895 +object $head xxxx tag tag mytag tagger . <> 0 +0000 EOF -check_verify_failure '"type" line label check' '^error: char47: .*"\\ntype "$' +check_verify_failure '"type" line label check' '^error: char.*: .*"\\ntype "$' ############################################################ # 5. type line eol check -echo "object 779e9b33986b1c2670fff52c5067603117b3e895" >tag.sig +echo "object $head" >tag.sig printf "type tagsssssssssssssssssssssssssssssss" >>tag.sig -check_verify_failure '"type" line eol check' '^error: char48: .*"\\n"$' +check_verify_failure '"type" line eol check' '^error: char.*: .*"\\n"$' ############################################################ # 6. tag line label check #1 cat >tag.sig <<EOF -object 779e9b33986b1c2670fff52c5067603117b3e895 +object $head type tag xxx mytag tagger . <> 0 +0000 @@ -98,37 +99,37 @@ tagger . <> 0 +0000 EOF check_verify_failure '"tag" line label check #1' \ - '^error: char57: no "tag " found$' + '^error: char.*: no "tag " found$' ############################################################ # 7. tag line label check #2 cat >tag.sig <<EOF -object 779e9b33986b1c2670fff52c5067603117b3e895 +object $head type taggggggggggggggggggggggggggggggg tag EOF check_verify_failure '"tag" line label check #2' \ - '^error: char87: no "tag " found$' + '^error: char.*: no "tag " found$' ############################################################ # 8. type line type-name length check cat >tag.sig <<EOF -object 779e9b33986b1c2670fff52c5067603117b3e895 +object $head type taggggggggggggggggggggggggggggggg tag mytag EOF check_verify_failure '"type" line type-name length check' \ - '^error: char53: type too long$' + '^error: char.*: type too long$' ############################################################ # 9. verify object (SHA1/type) check cat >tag.sig <<EOF -object 779e9b33986b1c2670fff52c5067603117b3e895 +object $(test_oid deadbeef) type tagggg tag mytag tagger . <> 0 +0000 @@ -150,7 +151,7 @@ tagger . <> 0 +0000 EOF check_verify_failure 'verify tag-name check' \ - '^error: char67: could not verify tag name$' + '^error: char.*: could not verify tag name$' ############################################################ # 11. tagger line label check #1 @@ -164,7 +165,7 @@ This is filler EOF check_verify_failure '"tagger" line label check #1' \ - '^error: char70: could not find "tagger "$' + '^error: char.*: could not find "tagger "$' ############################################################ # 12. tagger line label check #2 @@ -179,7 +180,7 @@ This is filler EOF check_verify_failure '"tagger" line label check #2' \ - '^error: char70: could not find "tagger "$' + '^error: char.*: could not find "tagger "$' ############################################################ # 13. disallow missing tag author name @@ -194,7 +195,7 @@ This is filler EOF check_verify_failure 'disallow missing tag author name' \ - '^error: char77: missing tagger name$' + '^error: char.*: missing tagger name$' ############################################################ # 14. disallow missing tag author name @@ -209,7 +210,7 @@ tagger T A Gger < EOF check_verify_failure 'disallow malformed tagger' \ - '^error: char77: malformed tagger field$' + '^error: char.*: malformed tagger field$' ############################################################ # 15. allow empty tag email @@ -238,7 +239,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000 EOF check_verify_failure 'disallow spaces in tag email' \ - '^error: char77: malformed tagger field$' + '^error: char.*: malformed tagger field$' ############################################################ # 17. disallow missing tag timestamp @@ -252,7 +253,7 @@ tagger T A Gger <tagger@example.com>__ EOF check_verify_failure 'disallow missing tag timestamp' \ - '^error: char107: missing tag timestamp$' + '^error: char.*: missing tag timestamp$' ############################################################ # 18. detect invalid tag timestamp1 @@ -266,7 +267,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008 EOF check_verify_failure 'detect invalid tag timestamp1' \ - '^error: char107: missing tag timestamp$' + '^error: char.*: missing tag timestamp$' ############################################################ # 19. detect invalid tag timestamp2 @@ -280,7 +281,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500 EOF check_verify_failure 'detect invalid tag timestamp2' \ - '^error: char111: malformed tag timestamp$' + '^error: char.*: malformed tag timestamp$' ############################################################ # 20. detect invalid tag timezone1 @@ -294,7 +295,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT EOF check_verify_failure 'detect invalid tag timezone1' \ - '^error: char118: malformed tag timezone$' + '^error: char.*: malformed tag timezone$' ############################################################ # 21. detect invalid tag timezone2 @@ -308,7 +309,7 @@ tagger T A Gger <tagger@example.com> 1206478233 + 30 EOF check_verify_failure 'detect invalid tag timezone2' \ - '^error: char118: malformed tag timezone$' + '^error: char.*: malformed tag timezone$' ############################################################ # 22. detect invalid tag timezone3 @@ -322,7 +323,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430 EOF check_verify_failure 'detect invalid tag timezone3' \ - '^error: char118: malformed tag timezone$' + '^error: char.*: malformed tag timezone$' ############################################################ # 23. detect invalid header entry @@ -337,7 +338,7 @@ this line should not be here EOF check_verify_failure 'detect invalid header entry' \ - '^error: char124: trailing garbage in tag header$' + '^error: char.*: trailing garbage in tag header$' ############################################################ # 24. create valid tag diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index b8e337893f..820b350aeb 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -7,6 +7,18 @@ test_description='Test git stash' . ./test-lib.sh +diff_cmp () { + for i in "$1" "$2" + do + sed -e 's/^index 0000000\.\.[0-9a-f]*/index 0000000..1234567/' \ + -e 's/^index [0-9a-f]*\.\.[0-9a-f]*/index 1234567..89abcde/' \ + -e 's/^index [0-9a-f]*,[0-9a-f]*\.\.[0-9a-f]*/index 1234567,7654321..89abcde/' \ + "$i" >"$i.compare" || return 1 + done && + test_cmp "$1.compare" "$2.compare" && + rm -f "$1.compare" "$2.compare" +} + test_expect_success 'stash some dirty working directory' ' echo 1 >file && git add file && @@ -36,7 +48,7 @@ EOF test_expect_success 'parents of stash' ' test $(git rev-parse stash^) = $(git rev-parse HEAD) && git diff stash^2..stash >output && - test_cmp expect output + diff_cmp expect output ' test_expect_success 'applying bogus stash does nothing' ' @@ -210,13 +222,13 @@ test_expect_success 'stash branch' ' test refs/heads/stashbranch = $(git symbolic-ref HEAD) && test $(git rev-parse HEAD) = $(git rev-parse master^) && git diff --cached >output && - test_cmp expect output && + diff_cmp expect output && git diff >output && - test_cmp expect1 output && + diff_cmp expect1 output && git add file && git commit -m alternate\ second && git diff master..stashbranch >output && - test_cmp output expect2 && + diff_cmp output expect2 && test 0 = $(git stash list | wc -l) ' @@ -577,7 +589,7 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' ' +bar EOF git stash show -p ${STASH_ID} >actual && - test_cmp expected actual + diff_cmp expected actual ' test_expect_success 'stash show - no stashes on stack, stash-like argument' ' @@ -609,7 +621,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' ' +foo EOF git stash show -p ${STASH_ID} >actual && - test_cmp expected actual + diff_cmp expected actual ' test_expect_success 'stash show --patience shows diff' ' @@ -627,7 +639,7 @@ test_expect_success 'stash show --patience shows diff' ' +foo EOF git stash show --patience ${STASH_ID} >actual && - test_cmp expected actual + diff_cmp expected actual ' test_expect_success 'drop: fail early if specified stash is not a stash ref' ' @@ -791,7 +803,7 @@ test_expect_success 'stash where working directory contains "HEAD" file' ' git diff-index --cached --quiet HEAD && test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" && git diff stash^..stash >output && - test_cmp expect output + diff_cmp expect output ' test_expect_success 'store called with invalid commit' ' @@ -847,7 +859,7 @@ test_expect_success 'stash list implies --first-parent -m' ' +working EOF git stash list --format=%gd -p >actual && - test_cmp expect actual + diff_cmp expect actual ' test_expect_success 'stash list --cc shows combined diff' ' @@ -864,7 +876,7 @@ test_expect_success 'stash list --cc shows combined diff' ' ++working EOF git stash list --format=%gd -p --cc >actual && - test_cmp expect actual + diff_cmp expect actual ' test_expect_success 'stash is not confused by partial renames' ' diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index 8de36b7d12..e5116a76a1 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -78,7 +78,7 @@ test_expect_success 'git diff-files --no-patch --patch shows the patch' ' test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' ' git diff-files --no-patch --patch-with-raw >actual && - grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M path0\$" actual && + grep -q "^:100644 100755 .* $ZERO_OID M path0\$" actual && tail -n +4 actual >actual-patch && compare_diff_patch expected actual-patch ' diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index 3a6c21e825..cbcdd10464 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -7,123 +7,272 @@ test_description='Test diff raw-output. ' . ./test-lib.sh + . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh -cat >.test-plain-OA <<\EOF -:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA -:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A AN -:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD -:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42 A DF -:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D DM -:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D DN -:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL -:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M MD -:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M MM -:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M MN -:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS -:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M TT -:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49 M Z +test_oid_init + +test_oid_cache <<\EOF +aa_1 sha1:ccba72ad3888a3520b39efcf780b9ee64167535d +aa_1 sha256:9febfbf18197819b2735c45291f138525d2476d59470f98239647544586ba403 + +aa_2 sha1:6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 +aa_2 sha256:6eaa3437de83f145a4aaa6ba355303075ade547b128ec6a2cd00a81ff7ce7a56 + +an_1 sha1:7e426fb079479fd67f6d81f984e4ec649a44bc25 +an_1 sha256:8f92a0bec99e399a38e3bd0e1bf19fbf121e0160efb29b857df79d439f1c4536 + +dd_1 sha1:bcc68ef997017466d5c9094bcf7692295f588c9a +dd_1 sha256:07e17428b00639b85485d2b01083d219e2f3e3ba8579e9ca44e9cc8dd554d952 + +df_1 sha1:6d50f65d3bdab91c63444294d38f08aeff328e42 +df_1 sha256:e367cecc27e9bf5451b1c65828cb21938d36a5f8e39c1b03ad6509cc36bb8e9d + +df_2 sha1:71420ab81e254145d26d6fc0cddee64c1acd4787 +df_2 sha256:0f0a86d10347ff6921d03a3c954679f3f1d14fa3d5cd82f57b32c09755f3a47d + +dfd1 sha1:68a6d8b91da11045cf4aa3a5ab9f2a781c701249 +dfd1 sha256:f3bd3265b02b6978ce86490d8ad026c573639c974b3de1d9faf30d8d5a77d3d5 + +dm_1 sha1:141c1f1642328e4bc46a7d801a71da392e66791e +dm_1 sha256:c89f8656e7b94e21ee5fbaf0e2149bbf783c51edbe2ce110349cac13059ee7ed + +dm_2 sha1:3c4d8de5fbad08572bab8e10eef8dbb264cf0231 +dm_2 sha256:83a572e37e0c94086294dae2cecc43d9131afd6f6c906e495c78972230b54988 + +dn_1 sha1:35abde1506ddf806572ff4d407bd06885d0f8ee9 +dn_1 sha256:775d5852582070e620be63327bfa515fab8f71c7ac3e4f0c3cd6267b4377ba28 + +ll_2 sha1:1d41122ebdd7a640f29d3c9cc4f9d70094374762 +ll_2 sha256:7917b4948a883cfed0a77d3d5a625dc8577d6ddcc3c6c3bbc56c4d4226a2246d + +md_1 sha1:03f24c8c4700babccfd28b654e7e8eac402ad6cd +md_1 sha256:fc9f30369b978595ad685ba11ca9a17de0af16d79cd4b629975f4f1590033902 + +md_2 sha1:103d9f89b50b9aad03054b579be5e7aa665f2d57 +md_2 sha256:fc78ec75275628762fe520479a6b2398dec295ce7aabcb1d15e5963c7b4e9317 + +mm_1 sha1:b258508afb7ceb449981bd9d63d2d3e971bf8d34 +mm_1 sha256:a4b7847d228e900e3000285e240c20fd96f9dd41ce1445305f6eada126d4a04a + +mm_2 sha1:b431b272d829ff3aa4d1a5085f4394ab4d3305b6 +mm_2 sha256:3f8b83ea36aacf689bcf1a1290a9a8ed341564d32682ea6f76fea9a979186782 + +mm_3 sha1:19989d4559aae417fedee240ccf2ba315ea4dc2b +mm_3 sha256:71b3bfc5747ac033fff9ea0ab39ee453a3af2969890e75d6ef547b87544e2681 + +mn_1 sha1:bd084b0c27c7b6cc34f11d6d0509a29be3caf970 +mn_1 sha256:47a67450583d7a329eb01a7c4ba644945af72c0ed2c7c95eb5a00d6e46d4d483 + +mn_2 sha1:a716d58de4a570e0038f5c307bd8db34daea021f +mn_2 sha256:f95104c1ebe27acb84bac25a7be98c71f6b8d3054b21f357a5be0c524ad97e08 + +nm_1 sha1:c8f25781e8f1792e3e40b74225e20553041b5226 +nm_1 sha256:09baddc7afaa62e62e152c23c9c3ab94bf15a3894031e227e9be7fe68e1f4e49 + +nm_2 sha1:cdb9a8c3da571502ac30225e9c17beccb8387983 +nm_2 sha256:58b5227956ac2d2a08d0efa513c0ae37430948b16791ea3869a1308dbf05536d + +na_1 sha1:15885881ea69115351c09b38371f0348a3fb8c67 +na_1 sha256:18e4fdd1670cd7968ee23d35bfd29e5418d56fb190c840094c1c57ceee0aad8f + +nd_1 sha1:a4e179e4291e5536a5e1c82e091052772d2c5a93 +nd_1 sha256:07dac9b01d00956ea0c65bd993d7de4864aeef2ed3cbb1255d9f1d949fcd6df6 + +ss_1 sha1:40c959f984c8b89a2b02520d17f00d717f024397 +ss_1 sha256:50fc1b5df74d9910db2f9270993484235f15b69b75b01bcfb53e059289d14af9 + +ss_2 sha1:2ac547ae9614a00d1b28275de608131f7a0e259f +ss_2 sha256:a90f02e6044f1497d13db587d22ab12f90150a7d1e084afcf96065fab35ae2bc + +tt_1 sha1:4ac13458899ab908ef3b1128fa378daefc88d356 +tt_1 sha256:c53113c7dd5060e86b5b251428bd058f6726f66273c6a24bff1c61a04f498dd3 + +tt_2 sha1:4c86f9a85fbc5e6804ee2e17a797538fbe785bca +tt_2 sha256:0775f2a296129a7cf2862b46bc0e88c14d593f2773a3e3fb1c5193db6f5a7e77 + +tt_3 sha1:c4e4a12231b9fa79a0053cb6077fcb21bb5b135a +tt_3 sha256:47860f93cdd211f96443e0560f21c57ab6c2f4b0ac27ff03651a352e53fe8484 + +z__1 sha1:7d670fdcdb9929f6c7dac196ff78689cd1c566a1 +z__1 sha256:44d0f37aff5e51cfcfdd1134c93a6419bcca7b9964f792ffcd5f9b4fcba1ee63 + +z__2 sha1:5e5f22072bb39f6e12cf663a57cb634c76eefb49 +z__2 sha256:d29de162113190fed104eb5f010820cef4e315f89b9326e8497f7219fb737894 + +z__3 sha1:1ba523955d5160681af65cb776411f574c1e8155 +z__3 sha256:07422d772b07794ab4369a5648e617719f89c2d2212cbeab05d97214b6471636 + +zaa1 sha1:8acb8e9750e3f644bf323fcf3d338849db106c77 +zaa1 sha256:e79b029282c8abec2d9f3f7faceaf2a1405e02d1f368e66450ae66cf5b68d1f4 + +zaa2 sha1:6c0b99286d0bce551ac4a7b3dff8b706edff3715 +zaa2 sha256:c82bd78c3e69ea1796e6b1a7a3ba45bb106c50e819296475b862123d3f5cc5a0 + +zan1 sha1:087494262084cefee7ed484d20c8dc0580791272 +zan1 sha256:4b159eb3804d05599023dd074f771d06d02870f4ab24a7165add8ac3d703b8d3 + +zdd1 sha1:879007efae624d2b1307214b24a956f0a8d686a8 +zdd1 sha256:eecfdd4d8092dd0363fb6d4548b54c6afc8982c3ed9b34e393f1d6a921d8eaa3 + +zdm1 sha1:9b541b2275c06e3a7b13f28badf5294e2ae63df4 +zdm1 sha256:ab136e88e19a843c4bf7713d2090d5a2186ba16a6a80dacc12eeddd256a8e556 + +zdm2 sha1:d77371d15817fcaa57eeec27f770c505ba974ec1 +zdm2 sha256:1c1a5f57363f46a15d95ce8527b3c2c158d88d16853b4acbf81bd20fd2c89a46 + +zdn1 sha1:beb5d38c55283d280685ea21a0e50cfcc0ca064a +zdn1 sha256:0f0eca66183617b0aa5ad74b256540329f841470922ca6760263c996d825eb18 + +zmd1 sha1:d41fda41b7ec4de46b43cb7ea42a45001ae393d5 +zmd1 sha256:1ed32d481852eddf31a0ce12652a0ad14bf5b7a842667b5dbb0b50f35bf1c80a + +zmd2 sha1:a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 +zmd2 sha256:b238da211b404f8917df2d9c6f7030535e904b2186131007a3c292ec6902f933 + +zmm1 sha1:4ca22bae2527d3d9e1676498a0fba3b355bd1278 +zmm1 sha256:072b1d85b5f34fabc99dfa46008c5418df68302d3e317430006f49b32d244226 + +zmm2 sha1:61422ba9c2c873416061a88cd40a59a35b576474 +zmm2 sha256:81dd5d2b3c5cda16fef552256aed4e2ea0802a8450a08f308a92142112ff6dda + +zmm3 sha1:697aad7715a1e7306ca76290a3dd4208fbaeddfa +zmm3 sha256:8b10fab49e9be3414aa5e9a93d0e46f9569053440138a7c19a5eb5536d8e95bf + +zmn1 sha1:b16d7b25b869f2beb124efa53467d8a1550ad694 +zmn1 sha256:609e4f75d1295e844c826feeba213acb0b6cfc609adfe8ff705b19e3829ae3e9 + +zmn2 sha1:a5c544c21cfcb07eb80a4d89a5b7d1570002edfd +zmn2 sha256:d6d03edf2dc1a3b267a8205de5f41a2ff4b03def8c7ae02052b543fb09d589fc + +zna1 sha1:d12979c22fff69c59ca9409e7a8fe3ee25eaee80 +zna1 sha256:b37b80e789e8ea32aa323f004628f02013f632124b0282c7fe00a127d3c64c3c + +znd1 sha1:a18393c636b98e9bd7296b8b437ea4992b72440c +znd1 sha256:af92a22eee8c38410a0c9d2b5135a10aeb052cbc7cf675541ed9a67bfcaf7cf9 + +znm1 sha1:3fdbe17fd013303a2e981e1ca1c6cd6e72789087 +znm1 sha256:f75aeaa0c11e76918e381c105f0752932c6150e941fec565d24fa31098a13dc1 + +znm2 sha1:7e09d6a3a14bd630913e8c75693cea32157b606d +znm2 sha256:938d73cfbaa1c902a84fb5b3afd9736aa0590367fb9bd59c6c4d072ce70fcd6d +EOF + +cat >.test-plain-OA <<EOF +:000000 100644 $(test_oid zero) $(test_oid aa_1) A AA +:000000 100644 $(test_oid zero) $(test_oid an_1) A AN +:100644 000000 $(test_oid dd_1) $(test_oid zero) D DD +:000000 040000 $(test_oid zero) $(test_oid df_1) A DF +:100644 000000 $(test_oid dm_1) $(test_oid zero) D DM +:100644 000000 $(test_oid dn_1) $(test_oid zero) D DN +:000000 100644 $(test_oid zero) $(test_oid ll_2) A LL +:100644 100644 $(test_oid md_1) $(test_oid md_2) M MD +:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M MM +:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M MN +:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M SS +:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M TT +:040000 040000 $(test_oid z__1) $(test_oid z__2) M Z EOF -cat >.test-recursive-OA <<\EOF -:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA -:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A AN -:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD -:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 A DF/DF -:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D DM -:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D DN -:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL -:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M MD -:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M MM -:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M MN -:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS -:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M TT -:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77 A Z/AA -:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272 A Z/AN -:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D Z/DD -:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000 D Z/DM -:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000 D Z/DN -:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 M Z/MD -:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474 M Z/MM -:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd M Z/MN +cat >.test-recursive-OA <<EOF +:000000 100644 $(test_oid zero) $(test_oid aa_1) A AA +:000000 100644 $(test_oid zero) $(test_oid an_1) A AN +:100644 000000 $(test_oid dd_1) $(test_oid zero) D DD +:000000 100644 $(test_oid zero) $(test_oid dfd1) A DF/DF +:100644 000000 $(test_oid dm_1) $(test_oid zero) D DM +:100644 000000 $(test_oid dn_1) $(test_oid zero) D DN +:000000 100644 $(test_oid zero) $(test_oid ll_2) A LL +:100644 100644 $(test_oid md_1) $(test_oid md_2) M MD +:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M MM +:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M MN +:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M SS +:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M TT +:000000 100644 $(test_oid zero) $(test_oid zaa1) A Z/AA +:000000 100644 $(test_oid zero) $(test_oid zan1) A Z/AN +:100644 000000 $(test_oid zdd1) $(test_oid zero) D Z/DD +:100644 000000 $(test_oid zdm1) $(test_oid zero) D Z/DM +:100644 000000 $(test_oid zdn1) $(test_oid zero) D Z/DN +:100644 100644 $(test_oid zmd1) $(test_oid zmd2) M Z/MD +:100644 100644 $(test_oid zmm1) $(test_oid zmm2) M Z/MM +:100644 100644 $(test_oid zmn1) $(test_oid zmn2) M Z/MN EOF -cat >.test-plain-OB <<\EOF -:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A AA -:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD -:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF -:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M DM -:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL -:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D MD -:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM -:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA -:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND -:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM -:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS -:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT -:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155 M Z +cat >.test-plain-OB <<EOF +:000000 100644 $(test_oid zero) $(test_oid aa_2) A AA +:100644 000000 $(test_oid dd_1) $(test_oid zero) D DD +:000000 100644 $(test_oid zero) $(test_oid df_2) A DF +:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M DM +:000000 100644 $(test_oid zero) $(test_oid ll_2) A LL +:100644 000000 $(test_oid md_1) $(test_oid zero) D MD +:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M MM +:000000 100644 $(test_oid zero) $(test_oid na_1) A NA +:100644 000000 $(test_oid nd_1) $(test_oid zero) D ND +:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M NM +:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M SS +:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M TT +:040000 040000 $(test_oid z__1) $(test_oid z__3) M Z EOF -cat >.test-recursive-OB <<\EOF -:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A AA -:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD -:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF -:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M DM -:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL -:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D MD -:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM -:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA -:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND -:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM -:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS -:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT -:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715 A Z/AA -:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D Z/DD -:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1 M Z/DM -:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000 D Z/MD -:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa M Z/MM -:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A Z/NA -:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D Z/ND -:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M Z/NM +cat >.test-recursive-OB <<EOF +:000000 100644 $(test_oid zero) $(test_oid aa_2) A AA +:100644 000000 $(test_oid dd_1) $(test_oid zero) D DD +:000000 100644 $(test_oid zero) $(test_oid df_2) A DF +:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M DM +:000000 100644 $(test_oid zero) $(test_oid ll_2) A LL +:100644 000000 $(test_oid md_1) $(test_oid zero) D MD +:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M MM +:000000 100644 $(test_oid zero) $(test_oid na_1) A NA +:100644 000000 $(test_oid nd_1) $(test_oid zero) D ND +:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M NM +:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M SS +:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M TT +:000000 100644 $(test_oid zero) $(test_oid zaa2) A Z/AA +:100644 000000 $(test_oid zdd1) $(test_oid zero) D Z/DD +:100644 100644 $(test_oid zdm1) $(test_oid zdm2) M Z/DM +:100644 000000 $(test_oid zmd1) $(test_oid zero) D Z/MD +:100644 100644 $(test_oid zmm1) $(test_oid zmm3) M Z/MM +:000000 100644 $(test_oid zero) $(test_oid zna1) A Z/NA +:100644 000000 $(test_oid znd1) $(test_oid zero) D Z/ND +:100644 100644 $(test_oid znm1) $(test_oid znm2) M Z/NM EOF -cat >.test-plain-AB <<\EOF -:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M AA -:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D AN -:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF -:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000 D DF -:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A DM -:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A DN -:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D MD -:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM -:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M MN -:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA -:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND -:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM -:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT -:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155 M Z +cat >.test-plain-AB <<EOF +:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M AA +:100644 000000 $(test_oid an_1) $(test_oid zero) D AN +:000000 100644 $(test_oid zero) $(test_oid df_2) A DF +:040000 000000 $(test_oid df_1) $(test_oid zero) D DF +:000000 100644 $(test_oid zero) $(test_oid dm_2) A DM +:000000 100644 $(test_oid zero) $(test_oid dn_1) A DN +:100644 000000 $(test_oid md_2) $(test_oid zero) D MD +:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M MM +:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M MN +:000000 100644 $(test_oid zero) $(test_oid na_1) A NA +:100644 000000 $(test_oid nd_1) $(test_oid zero) D ND +:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M NM +:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M TT +:040000 040000 $(test_oid z__2) $(test_oid z__3) M Z EOF -cat >.test-recursive-AB <<\EOF -:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M AA -:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D AN -:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF -:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000 D DF/DF -:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A DM -:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A DN -:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D MD -:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM -:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M MN -:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA -:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND -:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM -:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT -:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715 M Z/AA -:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000 D Z/AN -:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1 A Z/DM -:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a A Z/DN -:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000 D Z/MD -:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa M Z/MM -:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694 M Z/MN -:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A Z/NA -:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D Z/ND -:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M Z/NM +cat >.test-recursive-AB <<EOF +:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M AA +:100644 000000 $(test_oid an_1) $(test_oid zero) D AN +:000000 100644 $(test_oid zero) $(test_oid df_2) A DF +:100644 000000 $(test_oid dfd1) $(test_oid zero) D DF/DF +:000000 100644 $(test_oid zero) $(test_oid dm_2) A DM +:000000 100644 $(test_oid zero) $(test_oid dn_1) A DN +:100644 000000 $(test_oid md_2) $(test_oid zero) D MD +:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M MM +:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M MN +:000000 100644 $(test_oid zero) $(test_oid na_1) A NA +:100644 000000 $(test_oid nd_1) $(test_oid zero) D ND +:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M NM +:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M TT +:100644 100644 $(test_oid zaa1) $(test_oid zaa2) M Z/AA +:100644 000000 $(test_oid zan1) $(test_oid zero) D Z/AN +:000000 100644 $(test_oid zero) $(test_oid zdm2) A Z/DM +:000000 100644 $(test_oid zero) $(test_oid zdn1) A Z/DN +:100644 000000 $(test_oid zmd2) $(test_oid zero) D Z/MD +:100644 100644 $(test_oid zmm2) $(test_oid zmm3) M Z/MM +:100644 100644 $(test_oid zmn2) $(test_oid zmn1) M Z/MN +:000000 100644 $(test_oid zero) $(test_oid zna1) A Z/NA +:100644 000000 $(test_oid znd1) $(test_oid zero) D Z/ND +:100644 100644 $(test_oid znm1) $(test_oid znm2) M Z/NM EOF cmp_diff_files_output () { diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh index 3641fd84d6..b63bdf031f 100755 --- a/t/t4009-diff-rename-4.sh +++ b/t/t4009-diff-rename-4.sh @@ -14,6 +14,7 @@ test_expect_success \ 'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && + orig=$(git hash-object COPYING) && tree=$(git write-tree) && echo $tree' @@ -22,6 +23,8 @@ test_expect_success \ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 && sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 && rm -f COPYING && + c1=$(git hash-object COPYING.1) && + c2=$(git hash-object COPYING.2) && git update-index --add --remove COPYING COPYING.?' # tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2, @@ -31,11 +34,11 @@ test_expect_success \ git diff-index -z -C $tree >current -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 +cat >expected <<EOF +:100644 100644 $orig $c1 C1234 COPYING COPYING.1 -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234 +:100644 100644 $orig $c2 R1234 COPYING COPYING.2 EOF @@ -57,10 +60,10 @@ test_expect_success \ # about rezrov. git diff-index -z -C $tree >current -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M +cat >expected <<EOF +:100644 100644 $orig $c2 M COPYING -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 +:100644 100644 $orig $c1 C1234 COPYING COPYING.1 EOF @@ -82,8 +85,8 @@ test_expect_success \ git update-index --add --remove COPYING COPYING.1' git diff-index -z -C --find-copies-harder $tree >current -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 +cat >expected <<EOF +:100644 100644 $orig $c1 C1234 COPYING COPYING.1 EOF diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index a9054d2db1..5ac94b390d 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -7,9 +7,6 @@ test_description='Various diff formatting options' . ./test-lib.sh -LF=' -' - test_expect_success setup ' GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" && diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index ca7debf1d4..83f52614d3 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -9,7 +9,6 @@ test_description='various format-patch tests' . "$TEST_DIRECTORY"/lib-terminal.sh test_expect_success setup ' - for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file && cat file >elif && git add file elif && @@ -34,7 +33,8 @@ test_expect_success setup ' git commit -m "Side changes #3 with \\n backslash-n in it." && git checkout master && - git diff-tree -p C2 | git apply --index && + git diff-tree -p C2 >patch && + git apply --index <patch && test_tick && git commit -m "Master accepts moral equivalent of #2" && @@ -59,33 +59,28 @@ test_expect_success setup ' git checkout master ' -test_expect_success "format-patch --ignore-if-in-upstream" ' - +test_expect_success 'format-patch --ignore-if-in-upstream' ' git format-patch --stdout master..side >patch0 && - cnt=$(grep "^From " patch0 | wc -l) && - test $cnt = 3 - + grep "^From " patch0 >from0 && + test_line_count = 3 from0 ' -test_expect_success "format-patch --ignore-if-in-upstream" ' - +test_expect_success 'format-patch --ignore-if-in-upstream' ' git format-patch --stdout \ --ignore-if-in-upstream master..side >patch1 && - cnt=$(grep "^From " patch1 | wc -l) && - test $cnt = 2 - + grep "^From " patch1 >from1 && + test_line_count = 2 from1 ' -test_expect_success "format-patch --ignore-if-in-upstream handles tags" ' +test_expect_success 'format-patch --ignore-if-in-upstream handles tags' ' git tag -a v1 -m tag side && git tag -a v2 -m tag master && git format-patch --stdout --ignore-if-in-upstream v2..v1 >patch1 && - cnt=$(grep "^From " patch1 | wc -l) && - test $cnt = 2 + grep "^From " patch1 >from1 && + test_line_count = 2 from1 ' test_expect_success "format-patch doesn't consider merge commits" ' - git checkout -b slave master && echo "Another line" >>file && test_tick && @@ -96,148 +91,138 @@ test_expect_success "format-patch doesn't consider merge commits" ' git checkout -b merger master && test_tick && git merge --no-ff slave && - cnt=$(git format-patch -3 --stdout | grep "^From " | wc -l) && - test $cnt = 3 + git format-patch -3 --stdout >patch && + grep "^From " patch >from && + test_line_count = 3 from ' -test_expect_success "format-patch result applies" ' - +test_expect_success 'format-patch result applies' ' git checkout -b rebuild-0 master && git am -3 patch0 && - cnt=$(git rev-list master.. | wc -l) && - test $cnt = 2 + git rev-list master.. >list && + test_line_count = 2 list ' -test_expect_success "format-patch --ignore-if-in-upstream result applies" ' - +test_expect_success 'format-patch --ignore-if-in-upstream result applies' ' git checkout -b rebuild-1 master && git am -3 patch1 && - cnt=$(git rev-list master.. | wc -l) && - test $cnt = 2 + git rev-list master.. >list && + test_line_count = 2 list ' test_expect_success 'commit did not screw up the log message' ' - - git cat-file commit side | grep "^Side .* with .* backslash-n" - + git cat-file commit side >actual && + grep "^Side .* with .* backslash-n" actual ' test_expect_success 'format-patch did not screw up the log message' ' - grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 && grep "^Subject: .*Side changes #3 with .* backslash-n" patch1 - ' test_expect_success 'replay did not screw up the log message' ' - - git cat-file commit rebuild-1 | grep "^Side .* with .* backslash-n" - + git cat-file commit rebuild-1 >actual && + grep "^Side .* with .* backslash-n" actual ' test_expect_success 'extra headers' ' - git config format.headers "To: R E Cipient <rcipient@example.com> " && git config --add format.headers "Cc: S E Cipient <scipient@example.com> " && - git format-patch --stdout master..side > patch2 && - sed -e "/^\$/q" patch2 > hdrs2 && + git format-patch --stdout master..side >patch2 && + sed -e "/^\$/q" patch2 >hdrs2 && grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 && grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2 - ' test_expect_success 'extra headers without newlines' ' - git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" && git config --add format.headers "Cc: S E Cipient <scipient@example.com>" && git format-patch --stdout master..side >patch3 && - sed -e "/^\$/q" patch3 > hdrs3 && + sed -e "/^\$/q" patch3 >hdrs3 && grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 && grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3 - ' test_expect_success 'extra headers with multiple To:s' ' - git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" && git config --add format.headers "To: S E Cipient <scipient@example.com>" && - git format-patch --stdout master..side > patch4 && - sed -e "/^\$/q" patch4 > hdrs4 && + git format-patch --stdout master..side >patch4 && + sed -e "/^\$/q" patch4 >hdrs4 && grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 && grep "^ *S E Cipient <scipient@example.com>\$" hdrs4 ' test_expect_success 'additional command line cc (ascii)' ' - git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && - git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 && - grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 && - grep "^ *S E Cipient <scipient@example.com>\$" patch5 + git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side >patch5 && + sed -e "/^\$/q" patch5 >hdrs5 && + grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 && + grep "^ *S E Cipient <scipient@example.com>\$" hdrs5 ' test_expect_failure 'additional command line cc (rfc822)' ' - git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && - git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 && - grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 && - grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" patch5 + git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side >patch5 && + sed -e "/^\$/q" patch5 >hdrs5 && + grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 && + grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" hdrs5 ' test_expect_success 'command line headers' ' - git config --unset-all format.headers && - git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 && - grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6 + git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side >patch6 && + sed -e "/^\$/q" patch6 >hdrs6 && + grep "^Cc: R E Cipient <rcipient@example.com>\$" hdrs6 ' test_expect_success 'configuration headers and command line headers' ' - git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && - git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 && - grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 && - grep "^ *S E Cipient <scipient@example.com>\$" patch7 + git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side >patch7 && + sed -e "/^\$/q" patch7 >hdrs7 && + grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs7 && + grep "^ *S E Cipient <scipient@example.com>\$" hdrs7 ' test_expect_success 'command line To: header (ascii)' ' - git config --unset-all format.headers && - git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 && - grep "^To: R E Cipient <rcipient@example.com>\$" patch8 + git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side >patch8 && + sed -e "/^\$/q" patch8 >hdrs8 && + grep "^To: R E Cipient <rcipient@example.com>\$" hdrs8 ' test_expect_failure 'command line To: header (rfc822)' ' - - git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 && - grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch8 + git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side >patch8 && + sed -e "/^\$/q" patch8 >hdrs8 && + grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs8 ' test_expect_failure 'command line To: header (rfc2047)' ' - - git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 && - grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8 + git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side >patch8 && + sed -e "/^\$/q" patch8 >hdrs8 && + grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs8 ' test_expect_success 'configuration To: header (ascii)' ' - git config format.to "R E Cipient <rcipient@example.com>" && - git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && - grep "^To: R E Cipient <rcipient@example.com>\$" patch9 + git format-patch --stdout master..side >patch9 && + sed -e "/^\$/q" patch9 >hdrs9 && + grep "^To: R E Cipient <rcipient@example.com>\$" hdrs9 ' test_expect_failure 'configuration To: header (rfc822)' ' - git config format.to "R. E. Cipient <rcipient@example.com>" && - git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && - grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch9 + git format-patch --stdout master..side >patch9 && + sed -e "/^\$/q" patch9 >hdrs9 && + grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs9 ' test_expect_failure 'configuration To: header (rfc2047)' ' - git config format.to "R Ä Cipient <rcipient@example.com>" && - git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && - grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9 + git format-patch --stdout master..side >patch9 && + sed -e "/^\$/q" patch9 >hdrs9 && + grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9 ' # check_patch <patch>: Verify that <patch> looks like a half-sane @@ -249,89 +234,79 @@ check_patch () { } test_expect_success 'format.from=false' ' - - git -c format.from=false format-patch --stdout master..side | - sed -e "/^\$/q" >patch && + git -c format.from=false format-patch --stdout master..side >patch && + sed -e "/^\$/q" patch >hdrs && check_patch patch && - ! grep "^From: C O Mitter <committer@example.com>\$" patch + ! grep "^From: C O Mitter <committer@example.com>\$" hdrs ' test_expect_success 'format.from=true' ' - - git -c format.from=true format-patch --stdout master..side | - sed -e "/^\$/q" >patch && - check_patch patch && - grep "^From: C O Mitter <committer@example.com>\$" patch + git -c format.from=true format-patch --stdout master..side >patch && + sed -e "/^\$/q" patch >hdrs && + check_patch hdrs && + grep "^From: C O Mitter <committer@example.com>\$" hdrs ' test_expect_success 'format.from with address' ' - - git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side | - sed -e "/^\$/q" >patch && - check_patch patch && - grep "^From: F R Om <from@example.com>\$" patch + git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side >patch && + sed -e "/^\$/q" patch >hdrs && + check_patch hdrs && + grep "^From: F R Om <from@example.com>\$" hdrs ' test_expect_success '--no-from overrides format.from' ' - - git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side | - sed -e "/^\$/q" >patch && - check_patch patch && - ! grep "^From: F R Om <from@example.com>\$" patch + git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side >patch && + sed -e "/^\$/q" patch >hdrs && + check_patch hdrs && + ! grep "^From: F R Om <from@example.com>\$" hdrs ' test_expect_success '--from overrides format.from' ' - - git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side | - sed -e "/^\$/q" >patch && - check_patch patch && - ! grep "^From: F R Om <from@example.com>\$" patch + git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side >patch && + sed -e "/^\$/q" patch >hdrs && + check_patch hdrs && + ! grep "^From: F R Om <from@example.com>\$" hdrs ' test_expect_success '--no-to overrides config.to' ' - git config --replace-all format.to \ "R E Cipient <rcipient@example.com>" && - git format-patch --no-to --stdout master..side | - sed -e "/^\$/q" >patch10 && - check_patch patch10 && - ! grep "^To: R E Cipient <rcipient@example.com>\$" patch10 + git format-patch --no-to --stdout master..side >patch10 && + sed -e "/^\$/q" patch10 >hdrs10 && + check_patch hdrs10 && + ! grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10 ' test_expect_success '--no-to and --to replaces config.to' ' - git config --replace-all format.to \ "Someone <someone@out.there>" && git format-patch --no-to --to="Someone Else <else@out.there>" \ - --stdout master..side | - sed -e "/^\$/q" >patch11 && - check_patch patch11 && - ! grep "^To: Someone <someone@out.there>\$" patch11 && - grep "^To: Someone Else <else@out.there>\$" patch11 + --stdout master..side >patch11 && + sed -e "/^\$/q" patch11 >hdrs11 && + check_patch hdrs11 && + ! grep "^To: Someone <someone@out.there>\$" hdrs11 && + grep "^To: Someone Else <else@out.there>\$" hdrs11 ' test_expect_success '--no-cc overrides config.cc' ' - git config --replace-all format.cc \ "C E Cipient <rcipient@example.com>" && - git format-patch --no-cc --stdout master..side | - sed -e "/^\$/q" >patch12 && - check_patch patch12 && - ! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12 + git format-patch --no-cc --stdout master..side >patch12 && + sed -e "/^\$/q" patch12 >hdrs12 && + check_patch hdrs12 && + ! grep "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12 ' test_expect_success '--no-add-header overrides config.headers' ' - git config --replace-all format.headers \ "Header1: B E Cipient <rcipient@example.com>" && - git format-patch --no-add-header --stdout master..side | - sed -e "/^\$/q" >patch13 && - check_patch patch13 && - ! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13 + git format-patch --no-add-header --stdout master..side >patch13 && + sed -e "/^\$/q" patch13 >hdrs13 && + check_patch hdrs13 && + ! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13 ' test_expect_success 'multiple files' ' - rm -rf patches/ && git checkout side && git format-patch -o patches/ master && @@ -357,7 +332,7 @@ test_expect_success 'reroll count (-v)' ' check_threading () { expect="$1" && shift && - (git format-patch --stdout "$@"; echo $? > status.out) | + git format-patch --stdout "$@" >patch && # Prints everything between the Message-ID and In-Reply-To, # and replaces all Message-ID-lookalikes by a sequence number perl -ne ' @@ -372,12 +347,11 @@ check_threading () { print; } print "---\n" if /^From /i; - ' > actual && - test 0 = "$(cat status.out)" && + ' <patch >actual && test_cmp "$expect" actual } -cat >> expect.no-threading <<EOF +cat >>expect.no-threading <<EOF --- --- --- @@ -388,7 +362,7 @@ test_expect_success 'no threading' ' check_threading expect.no-threading master ' -cat > expect.thread <<EOF +cat >expect.thread <<EOF --- Message-Id: <0> --- @@ -405,7 +379,7 @@ test_expect_success 'thread' ' check_threading expect.thread --thread master ' -cat > expect.in-reply-to <<EOF +cat >expect.in-reply-to <<EOF --- Message-Id: <0> In-Reply-To: <1> @@ -425,7 +399,7 @@ test_expect_success 'thread in-reply-to' ' --thread master ' -cat > expect.cover-letter <<EOF +cat >expect.cover-letter <<EOF --- Message-Id: <0> --- @@ -446,7 +420,7 @@ test_expect_success 'thread cover-letter' ' check_threading expect.cover-letter --cover-letter --thread master ' -cat > expect.cl-irt <<EOF +cat >expect.cl-irt <<EOF --- Message-Id: <0> In-Reply-To: <1> @@ -478,7 +452,7 @@ test_expect_success 'thread explicit shallow' ' --in-reply-to="<test.message>" --thread=shallow master ' -cat > expect.deep <<EOF +cat >expect.deep <<EOF --- Message-Id: <0> --- @@ -496,7 +470,7 @@ test_expect_success 'thread deep' ' check_threading expect.deep --thread=deep master ' -cat > expect.deep-irt <<EOF +cat >expect.deep-irt <<EOF --- Message-Id: <0> In-Reply-To: <1> @@ -519,7 +493,7 @@ test_expect_success 'thread deep in-reply-to' ' --in-reply-to="<test.message>" master ' -cat > expect.deep-cl <<EOF +cat >expect.deep-cl <<EOF --- Message-Id: <0> --- @@ -543,7 +517,7 @@ test_expect_success 'thread deep cover-letter' ' check_threading expect.deep-cl --cover-letter --thread=deep master ' -cat > expect.deep-cl-irt <<EOF +cat >expect.deep-cl-irt <<EOF --- Message-Id: <0> In-Reply-To: <1> @@ -594,7 +568,6 @@ test_expect_success 'thread config + --no-thread' ' ' test_expect_success 'excessive subject' ' - rm -rf patches/ && git checkout side && before=$(git hash-object file) && @@ -622,10 +595,9 @@ test_expect_success 'cover-letter inherits diff options' ' ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch && git format-patch --cover-letter -1 -M && grep "file => foo .* 0 *\$" 0000-cover-letter.patch - ' -cat > expect << EOF +cat >expect <<EOF This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole @@ -636,14 +608,12 @@ cat > expect << EOF EOF test_expect_success 'shortlog of cover-letter wraps overly-long onelines' ' - git format-patch --cover-letter -2 && - sed -e "1,/A U Thor/d" -e "/^\$/q" < 0000-cover-letter.patch > output && + sed -e "1,/A U Thor/d" -e "/^\$/q" 0000-cover-letter.patch >output && test_cmp expect output - ' -cat > expect << EOF +cat >expect <<EOF index $before..$after 100644 --- a/file +++ b/file @@ -656,16 +626,14 @@ index $before..$after 100644 EOF test_expect_success 'format-patch respects -U' ' - git format-patch -U4 -2 && sed -e "1,/^diff/d" -e "/^+5/q" \ <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch \ >output && test_cmp expect output - ' -cat > expect << EOF +cat >expect <<EOF diff --git a/file b/file index $before..$after 100644 @@ -679,11 +647,9 @@ index $before..$after 100644 EOF test_expect_success 'format-patch -p suppresses stat' ' - git format-patch -p -2 && - sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output && + sed -e "1,/^\$/d" -e "/^+5/q" 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output && test_cmp expect output - ' test_expect_success 'format-patch from a subdirectory (1)' ' @@ -736,7 +702,7 @@ test_expect_success 'format-patch from a subdirectory (3)' ' ' test_expect_success 'format-patch --in-reply-to' ' - git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 && + git format-patch -1 --stdout --in-reply-to "baz@foo.bar" >patch8 && grep "^In-Reply-To: <baz@foo.bar>" patch8 && grep "^References: <baz@foo.bar>" patch8 ' @@ -827,21 +793,24 @@ test_expect_success 'format-patch with multiple notes refs' ' ! grep "this is note 2" out ' -echo "fatal: --name-only does not make sense" > expect.name-only -echo "fatal: --name-status does not make sense" > expect.name-status -echo "fatal: --check does not make sense" > expect.check +echo "fatal: --name-only does not make sense" >expect.name-only +echo "fatal: --name-status does not make sense" >expect.name-status +echo "fatal: --check does not make sense" >expect.check test_expect_success 'options no longer allowed for format-patch' ' - test_must_fail git format-patch --name-only 2> output && + test_must_fail git format-patch --name-only 2>output && test_i18ncmp expect.name-only output && - test_must_fail git format-patch --name-status 2> output && + test_must_fail git format-patch --name-status 2>output && test_i18ncmp expect.name-status output && - test_must_fail git format-patch --check 2> output && - test_i18ncmp expect.check output' + test_must_fail git format-patch --check 2>output && + test_i18ncmp expect.check output +' test_expect_success 'format-patch --numstat should produce a patch' ' - git format-patch --numstat --stdout master..side > output && - test 5 = $(grep "^diff --git a/" output | wc -l)' + git format-patch --numstat --stdout master..side >output && + grep "^diff --git a/" output >diff && + test_line_count = 5 diff +' test_expect_success 'format-patch -- <path>' ' git format-patch master..side -- file 2>error && @@ -852,20 +821,25 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' ' git format-patch --ignore-if-in-upstream HEAD ' -git_version="$(git --version | sed "s/.* //")" +test_expect_success 'get git version' ' + git_version=$(git --version) && + git_version=${git_version##* } +' signature() { printf "%s\n%s\n\n" "-- " "${1:-$git_version}" } test_expect_success 'format-patch default signature' ' - git format-patch --stdout -1 | tail -n 3 >output && + git format-patch --stdout -1 >patch && + tail -n 3 patch >output && signature >expect && test_cmp expect output ' test_expect_success 'format-patch --signature' ' - git format-patch --stdout --signature="my sig" -1 | tail -n 3 >output && + git format-patch --stdout --signature="my sig" -1 >patch && + tail -n 3 patch >output && signature "my sig" >expect && test_cmp expect output ' @@ -897,8 +871,8 @@ test_expect_success 'format-patch --signature --cover-letter' ' git config --unset-all format.signature && git format-patch --stdout --signature="my sig" --cover-letter \ -1 >output && - grep "my sig" output && - test 2 = $(grep "my sig" output | wc -l) + grep "my sig" output >sig && + test_line_count = 2 sig ' test_expect_success 'format.signature="" suppresses signatures' ' @@ -935,7 +909,7 @@ test_expect_success 'prepare mail-signature input' ' test_expect_success '--signature-file=file works' ' git format-patch --stdout --signature-file=mail-signature -1 >output && check_patch output && - sed -e "1,/^-- \$/d" <output >actual && + sed -e "1,/^-- \$/d" output >actual && { cat mail-signature && echo } >expect && @@ -946,7 +920,7 @@ test_expect_success 'format.signaturefile works' ' test_config format.signaturefile mail-signature && git format-patch --stdout -1 >output && check_patch output && - sed -e "1,/^-- \$/d" <output >actual && + sed -e "1,/^-- \$/d" output >actual && { cat mail-signature && echo } >expect && @@ -968,7 +942,7 @@ test_expect_success '--signature-file overrides format.signaturefile' ' git format-patch --stdout \ --signature-file=other-mail-signature -1 >output && check_patch output && - sed -e "1,/^-- \$/d" <output >actual && + sed -e "1,/^-- \$/d" output >actual && { cat other-mail-signature && echo } >expect && @@ -1037,7 +1011,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' ' git add file && git commit -m "$M512" && git format-patch --stdout -1 >patch && - sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject && + sed -n "/^Subject/p; /^ /p; /^$/q" patch >subject && test_cmp expect subject ' @@ -1076,7 +1050,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' ' git add file && git commit -m "$M512" && git format-patch --stdout -1 >patch && - sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject && + sed -n "/^Subject/p; /^ /p; /^$/q" patch >subject && test_cmp expect subject ' @@ -1085,7 +1059,7 @@ check_author() { git add file && GIT_AUTHOR_NAME=$1 git commit -m author-check && git format-patch --stdout -1 >patch && - sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual && + sed -n "/^From: /p; /^ /p; /^$/q" patch >actual && test_cmp expect actual } @@ -1205,7 +1179,7 @@ test_expect_success '--from=ident replaces author' ' From: A U Thor <author@example.com> EOF - sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head && + sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head && test_cmp expect patch.head ' @@ -1217,7 +1191,7 @@ test_expect_success '--from uses committer ident' ' From: A U Thor <author@example.com> EOF - sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head && + sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head && test_cmp expect patch.head ' @@ -1227,7 +1201,7 @@ test_expect_success '--from omits redundant in-body header' ' From: A U Thor <author@example.com> EOF - sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head && + sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head && test_cmp expect patch.head ' @@ -1242,7 +1216,7 @@ test_expect_success 'in-body headers trigger content encoding' ' From: éxötìc <author@example.com> EOF - sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head && + sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" patch >patch.head && test_cmp expect patch.head ' @@ -1256,283 +1230,283 @@ append_signoff() test_expect_success 'signoff: commit with no body' ' append_signoff </dev/null >actual && - cat <<\EOF | sed "s/EOL$//" >expected && -4:Subject: [PATCH] EOL -8: -9:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + cat <<-\EOF | sed "s/EOL$//" >expect && + 4:Subject: [PATCH] EOL + 8: + 9:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: commit with only subject' ' echo subject | append_signoff >actual && - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -9:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 9:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: commit with only subject that does not end with NL' ' printf subject | append_signoff >actual && - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -9:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 9:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: no existing signoffs' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -11:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + body + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 11:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: no existing signoffs and no trailing NL' ' printf "subject\n\nbody" | append_signoff >actual && - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -11:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 11:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: some random signoff' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body + body -Signed-off-by: my@house -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -11:Signed-off-by: my@house -12:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + Signed-off-by: my@house + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 11:Signed-off-by: my@house + 12:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: misc conforming footer elements' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body + body -Signed-off-by: my@house -(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709) -Tested-by: Some One <someone@example.com> -Bug: 1234 -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -11:Signed-off-by: my@house -15:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + Signed-off-by: my@house + (cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709) + Tested-by: Some One <someone@example.com> + Bug: 1234 + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 11:Signed-off-by: my@house + 15:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: some random signoff-alike' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body -Fooled-by-me: my@house -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -11: -12:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + body + Fooled-by-me: my@house + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 11: + 12:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: not really a signoff' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -I want to mention about Signed-off-by: here. -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -9:I want to mention about Signed-off-by: here. -10: -11:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + I want to mention about Signed-off-by: here. + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 9:I want to mention about Signed-off-by: here. + 10: + 11:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: not really a signoff (2)' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -My unfortunate -Signed-off-by: example happens to be wrapped here. -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10:Signed-off-by: example happens to be wrapped here. -11:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + My unfortunate + Signed-off-by: example happens to be wrapped here. + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10:Signed-off-by: example happens to be wrapped here. + 11:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: valid S-o-b paragraph in the middle' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -Signed-off-by: my@house -Signed-off-by: your@house + Signed-off-by: my@house + Signed-off-by: your@house -A lot of houses. -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -9:Signed-off-by: my@house -10:Signed-off-by: your@house -11: -13: -14:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + A lot of houses. + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 9:Signed-off-by: my@house + 10:Signed-off-by: your@house + 11: + 13: + 14:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: the same signoff at the end' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body + body -Signed-off-by: C O Mitter <committer@example.com> -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -11:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + Signed-off-by: C O Mitter <committer@example.com> + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 11:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: the same signoff at the end, no trailing NL' ' printf "subject\n\nSigned-off-by: C O Mitter <committer@example.com>" | append_signoff >actual && - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -9:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 9:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: the same signoff NOT at the end' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body + body -Signed-off-by: C O Mitter <committer@example.com> -Signed-off-by: my@house -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -11:Signed-off-by: C O Mitter <committer@example.com> -12:Signed-off-by: my@house -EOF - test_cmp expected actual + Signed-off-by: C O Mitter <committer@example.com> + Signed-off-by: my@house + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 11:Signed-off-by: C O Mitter <committer@example.com> + 12:Signed-off-by: my@house + EOF + test_cmp expect actual ' test_expect_success 'signoff: tolerate garbage in conforming footer' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body + body -Tested-by: my@house -Some Trash -Signed-off-by: C O Mitter <committer@example.com> -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -13:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + Tested-by: my@house + Some Trash + Signed-off-by: C O Mitter <committer@example.com> + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 13:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: respect trailer config' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -Myfooter: x -Some Trash -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -11: -12:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual && + Myfooter: x + Some Trash + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 11: + 12:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual && test_config trailer.Myfooter.ifexists add && - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -Myfooter: x -Some Trash -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -11:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + Myfooter: x + Some Trash + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 11:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'signoff: footer begins with non-signoff without @ sign' ' - append_signoff <<\EOF >actual && -subject + append_signoff <<-\EOF >actual && + subject -body + body -Reviewed-id: Noone -Tested-by: my@house -Change-id: Ideadbeef -Signed-off-by: C O Mitter <committer@example.com> -Bug: 1234 -EOF - cat >expected <<\EOF && -4:Subject: [PATCH] subject -8: -10: -14:Signed-off-by: C O Mitter <committer@example.com> -EOF - test_cmp expected actual + Reviewed-id: Noone + Tested-by: my@house + Change-id: Ideadbeef + Signed-off-by: C O Mitter <committer@example.com> + Bug: 1234 + EOF + cat >expect <<-\EOF && + 4:Subject: [PATCH] subject + 8: + 10: + 14:Signed-off-by: C O Mitter <committer@example.com> + EOF + test_cmp expect actual ' test_expect_success 'format patch ignores color.ui' ' @@ -1547,42 +1521,42 @@ test_expect_success 'cover letter using branch description (1)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter master >actual && - grep hello actual >/dev/null + grep hello actual ' test_expect_success 'cover letter using branch description (2)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual && - grep hello actual >/dev/null + grep hello actual ' test_expect_success 'cover letter using branch description (3)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter ^master rebuild-1 >actual && - grep hello actual >/dev/null + grep hello actual ' test_expect_success 'cover letter using branch description (4)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter master.. >actual && - grep hello actual >/dev/null + grep hello actual ' test_expect_success 'cover letter using branch description (5)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter -2 HEAD >actual && - grep hello actual >/dev/null + grep hello actual ' test_expect_success 'cover letter using branch description (6)' ' git checkout rebuild-1 && test_config branch.rebuild-1.description hello && git format-patch --stdout --cover-letter -2 >actual && - grep hello actual >/dev/null + grep hello actual ' test_expect_success 'cover letter with nothing' ' @@ -1636,7 +1610,8 @@ test_expect_success 'format-patch format.outputDirectory option' ' test_config format.outputDirectory patches && rm -fr patches && git format-patch master..side && - test $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l) + git rev-list master..side >list && + test_line_count = $(ls patches | wc -l) list ' test_expect_success 'format-patch -o overrides format.outputDirectory' ' @@ -1649,20 +1624,41 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' ' test_expect_success 'format-patch --base' ' git checkout patchid && - git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 && - git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 && - echo >expected && - echo "base-commit: $(git rev-parse HEAD~3)" >>expected && - echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected && - echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected && - signature >> expected && - test_cmp expected actual1 && - test_cmp expected actual2 && + + git format-patch --stdout --base=HEAD~3 -1 >patch && + tail -n 7 patch >actual1 && + + git format-patch --stdout --base=HEAD~3 HEAD~.. >patch && + tail -n 7 patch >actual2 && + + echo >expect && + git rev-parse HEAD~3 >commit-id-base && + echo "base-commit: $(cat commit-id-base)" >>expect && + + git show --patch HEAD~2 >patch && + git patch-id --stable <patch >patch.id.raw && + awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>expect && + + git show --patch HEAD~1 >patch && + git patch-id --stable <patch >patch.id.raw && + awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>expect && + + signature >>expect && + test_cmp expect actual1 && + test_cmp expect actual2 && + echo >fail && - echo "base-commit: $(git rev-parse HEAD~3)" >>fail && - echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --unstable | awk "{print \$1}")" >>fail && - echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --unstable | awk "{print \$1}")" >>fail && - signature >> fail && + echo "base-commit: $(cat commit-id-base)" >>fail && + + git show --patch HEAD~2 >patch && + git patch-id --unstable <patch >patch.id.raw && + awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>fail && + + git show --patch HEAD~1 >patch && + git patch-id --unstable <patch >patch.id.raw && + awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>fail && + + signature >>fail && ! test_cmp fail actual1 && ! test_cmp fail actual2 ' @@ -1672,8 +1668,9 @@ test_expect_success 'format-patch --base errors out when base commit is in revis test_must_fail git format-patch --base=HEAD~1 -2 && git format-patch --stdout --base=HEAD~2 -2 >patch && grep "^base-commit:" patch >actual && - echo "base-commit: $(git rev-parse HEAD~2)" >expected && - test_cmp expected actual + git rev-parse HEAD~2 >commit-id-base && + echo "base-commit: $(cat commit-id-base)" >expect && + test_cmp expect actual ' test_expect_success 'format-patch --base errors out when base commit is not ancestor of revision list' ' @@ -1699,8 +1696,8 @@ test_expect_success 'format-patch --base errors out when base commit is not ance test_must_fail git format-patch --base=$(cat commit-id-Z) -3 && git format-patch --stdout --base=$(cat commit-id-base) -3 >patch && grep "^base-commit:" patch >actual && - echo "base-commit: $(cat commit-id-base)" >expected && - test_cmp expected actual + echo "base-commit: $(cat commit-id-base)" >expect && + test_cmp expect actual ' test_expect_success 'format-patch --base=auto' ' @@ -1711,8 +1708,9 @@ test_expect_success 'format-patch --base=auto' ' test_commit N2 && git format-patch --stdout --base=auto -2 >patch && grep "^base-commit:" patch >actual && - echo "base-commit: $(git rev-parse upstream)" >expected && - test_cmp expected actual + git rev-parse upstream >commit-id-base && + echo "base-commit: $(cat commit-id-base)" >expect && + test_cmp expect actual ' test_expect_success 'format-patch errors out when history involves criss-cross' ' @@ -1748,8 +1746,9 @@ test_expect_success 'format-patch format.useAutoBaseoption' ' git config format.useAutoBase true && git format-patch --stdout -1 >patch && grep "^base-commit:" patch >actual && - echo "base-commit: $(git rev-parse upstream)" >expected && - test_cmp expected actual + git rev-parse upstream >commit-id-base && + echo "base-commit: $(cat commit-id-base)" >expect && + test_cmp expect actual ' test_expect_success 'format-patch --base overrides format.useAutoBase' ' @@ -1757,8 +1756,9 @@ test_expect_success 'format-patch --base overrides format.useAutoBase' ' git config format.useAutoBase true && git format-patch --stdout --base=HEAD~1 -1 >patch && grep "^base-commit:" patch >actual && - echo "base-commit: $(git rev-parse HEAD~1)" >expected && - test_cmp expected actual + git rev-parse HEAD~1 >commit-id-base && + echo "base-commit: $(cat commit-id-base)" >expect && + test_cmp expect actual ' test_expect_success 'format-patch --base with --attach' ' @@ -1833,7 +1833,7 @@ test_expect_success 'interdiff: cover-letter' ' git format-patch --cover-letter --interdiff=boop~2 -1 boop && test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch && test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch && - sed "1,/^@@ /d; /^-- $/q" <0000-cover-letter.patch >actual && + sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual && test_cmp expect actual ' @@ -1849,7 +1849,7 @@ test_expect_success 'interdiff: solo-patch' ' EOF git format-patch --interdiff=boop~2 -1 boop && test_i18ngrep "^Interdiff:$" 0001-fleep.patch && - sed "1,/^ @@ /d; /^$/q" <0001-fleep.patch >actual && + sed "1,/^ @@ /d; /^$/q" 0001-fleep.patch >actual && test_cmp expect actual ' diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 9261d6d3a0..6f5ef0035e 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -31,6 +31,7 @@ diffpatterns=" cpp csharp css + dts fortran fountain golang diff --git a/t/t4018/dts-labels b/t/t4018/dts-labels new file mode 100644 index 0000000000..b21ef8737b --- /dev/null +++ b/t/t4018/dts-labels @@ -0,0 +1,9 @@ +/ { + label_1: node1@ff00 { + label2: RIGHT { + vendor,some-property; + + ChangeMe = <0x45-30>; + }; + }; +}; diff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless new file mode 100644 index 0000000000..c5287d9141 --- /dev/null +++ b/t/t4018/dts-node-unitless @@ -0,0 +1,8 @@ +/ { + label_1: node1 { + RIGHT { + prop-array = <1>, <4>; + ChangeMe = <0xffeedd00>; + }; + }; +}; diff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes new file mode 100644 index 0000000000..5a4334bb16 --- /dev/null +++ b/t/t4018/dts-nodes @@ -0,0 +1,8 @@ +/ { + label_1: node1@ff00 { + RIGHT@deadf00,4000 { + #size-cells = <1>; + ChangeMe = <0xffeedd00>; + }; + }; +}; diff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1 new file mode 100644 index 0000000000..559dfce9b3 --- /dev/null +++ b/t/t4018/dts-nodes-comment1 @@ -0,0 +1,8 @@ +/ { + label_1: node1@ff00 { + RIGHT@deadf00,4000 /* &a comment */ { + #size-cells = <1>; + ChangeMe = <0xffeedd00>; + }; + }; +}; diff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2 new file mode 100644 index 0000000000..27e9718b31 --- /dev/null +++ b/t/t4018/dts-nodes-comment2 @@ -0,0 +1,8 @@ +/ { + label_1: node1@ff00 { + RIGHT@deadf00,4000 { /* a trailing comment */ + #size-cells = <1>; + ChangeMe = <0xffeedd00>; + }; + }; +}; diff --git a/t/t4018/dts-reference b/t/t4018/dts-reference new file mode 100644 index 0000000000..8f0c87d863 --- /dev/null +++ b/t/t4018/dts-reference @@ -0,0 +1,9 @@ +&label_1 { + TEST = <455>; +}; + +&RIGHT { + vendor,some-property; + + ChangeMe = <0x45-30>; +}; diff --git a/t/t4018/dts-root b/t/t4018/dts-root new file mode 100644 index 0000000000..2ef9e6ffaa --- /dev/null +++ b/t/t4018/dts-root @@ -0,0 +1,5 @@ +/RIGHT { /* Technically just supposed to be a slash */ + #size-cells = <1>; + + ChangeMe = <0xffeedd00>; +}; diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 912df91226..9a93c2a3e0 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -303,6 +303,7 @@ test_language_driver bibtex test_language_driver cpp test_language_driver csharp test_language_driver css +test_language_driver dts test_language_driver fortran test_language_driver html test_language_driver java diff --git a/t/t4034/dts/expect b/t/t4034/dts/expect new file mode 100644 index 0000000000..560fc99184 --- /dev/null +++ b/t/t4034/dts/expect @@ -0,0 +1,37 @@ +<BOLD>diff --git a/pre b/post<RESET> +<BOLD>index b6a9051..7803aee 100644<RESET> +<BOLD>--- a/pre<RESET> +<BOLD>+++ b/post<RESET> +<CYAN>@@ -1,32 +1,32 @@<RESET> +/ {<RESET> + <RED>this_handle<RESET><GREEN>HANDLE_2<RESET>: <RED>node<RESET><GREEN>new-node<RESET>@<RED>f00<RESET><GREEN>eeda<RESET> { + compatible = "<RED>mydev<RESET><GREEN>vendor,compat<RESET>"; + string-prop = <RED>start<RESET><GREEN>end<RESET>: "hello <RED>world!<RESET><GREEN>world?<RESET>" <RED>end<RESET><GREEN>start<RESET>: ; + <RED>#size-cells<RESET><GREEN>#address-cells<RESET> = <<RED>0+0<RESET><GREEN>0+40<RESET>>; + reg = <<RED>0xf00<RESET><GREEN>0xeeda<RESET>>; + prop = <<GREEN>(<RESET>1<GREEN>)<RESET>>; + prop = <<GREEN>(<RESET>-1e10<GREEN>)<RESET>>; + prop = <(!<RED>3<RESET><GREEN>1<RESET>)>; + prop = <(~<RED>3<RESET><GREEN>1<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>*<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>&<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>*<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>/<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>%<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3+4<RESET><GREEN>1+2<RESET>)>; + prop = <(<RED>3-4<RESET><GREEN>1-2<RESET>)>; + prop = /bits/ <RED>64<RESET><GREEN>32<RESET> <(<RED>3<RESET><GREEN>1<RESET><<<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>>><RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>&<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>^<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>|<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>&&<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>3<RESET><GREEN>1<RESET>||<RED>4<RESET><GREEN>2<RESET>)>; + prop = <(<RED>4?5<RESET><GREEN>1?2<RESET>:3)>; + list = <&<RED>this_handle<RESET><GREEN>HANDLE_2<RESET>>, <0 0 0 <RED>0<RESET><GREEN>1<RESET>>; + };<RESET> + + &<RED>phandle<RESET><GREEN>phandle2<RESET> { + <RED>pre-phandle<RESET><GREEN>prop_handle<RESET> = <&<RED>this_handle<RESET><GREEN>HANDLE_2<RESET>>; + };<RESET> +};<RESET> diff --git a/t/t4034/dts/post b/t/t4034/dts/post new file mode 100644 index 0000000000..7803aee280 --- /dev/null +++ b/t/t4034/dts/post @@ -0,0 +1,32 @@ +/ { + HANDLE_2: new-node@eeda { + compatible = "vendor,compat"; + string-prop = end: "hello world?" start: ; + #address-cells = <0+40>; + reg = <0xeeda>; + prop = <(1)>; + prop = <(-1e10)>; + prop = <(!1)>; + prop = <(~1)>; + prop = <(1*2)>; + prop = <(1&2)>; + prop = <(1*2)>; + prop = <(1/2)>; + prop = <(1%2)>; + prop = <(1+2)>; + prop = <(1-2)>; + prop = /bits/ 32 <(1<<2)>; + prop = <(1>>2)>; + prop = <(1&2)>; + prop = <(1^2)>; + prop = <(1|2)>; + prop = <(1&&2)>; + prop = <(1||2)>; + prop = <(1?2:3)>; + list = <&HANDLE_2>, <0 0 0 1>; + }; + + &phandle2 { + prop_handle = <&HANDLE_2>; + }; +}; diff --git a/t/t4034/dts/pre b/t/t4034/dts/pre new file mode 100644 index 0000000000..b6a905113c --- /dev/null +++ b/t/t4034/dts/pre @@ -0,0 +1,32 @@ +/ { + this_handle: node@f00 { + compatible = "mydev"; + string-prop = start: "hello world!" end: ; + #size-cells = <0+0>; + reg = <0xf00>; + prop = <1>; + prop = <-1e10>; + prop = <(!3)>; + prop = <(~3)>; + prop = <(3*4)>; + prop = <(3&4)>; + prop = <(3*4)>; + prop = <(3/4)>; + prop = <(3%4)>; + prop = <(3+4)>; + prop = <(3-4)>; + prop = /bits/ 64 <(3<<4)>; + prop = <(3>>4)>; + prop = <(3&4)>; + prop = <(3^4)>; + prop = <(3|4)>; + prop = <(3&&4)>; + prop = <(3||4)>; + prop = <(4?5:3)>; + list = <&this_handle>, <0 0 0 0>; + }; + + &phandle { + pre-phandle = <&this_handle>; + }; +}; diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh index 90c8fb2901..4831ad35e6 100755 --- a/t/t4067-diff-partial-clone.sh +++ b/t/t4067-diff-partial-clone.sh @@ -75,6 +75,37 @@ test_expect_success 'diff skips same-OID blobs' ' ! grep "want $(cat hash-b)" trace ' +test_expect_success 'when fetching missing objects, diff skips GITLINKs' ' + test_when_finished "rm -rf sub server client trace" && + + test_create_repo sub && + test_commit -C sub first && + + test_create_repo server && + echo a >server/a && + git -C server add a && + git -C server submodule add "file://$(pwd)/sub" && + git -C server commit -m x && + + test_commit -C server/sub second && + echo another-a >server/a && + git -C server add a sub && + git -C server commit -m x && + + test_config -C server uploadpack.allowfilter 1 && + test_config -C server uploadpack.allowanysha1inwant 1 && + git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client && + + echo a | git hash-object --stdin >hash-old-a && + echo another-a | git hash-object --stdin >hash-new-a && + + # Ensure that a and another-a are fetched, and check (by successful + # execution of the diff) that no invalid OIDs are sent. + GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD && + grep "want $(cat hash-old-a)" trace && + grep "want $(cat hash-new-a)" trace +' + test_expect_success 'diff with rename detection batches blobs' ' test_when_finished "rm -rf server client trace" && diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 3f7f750cc8..4f1e24ecbe 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -1061,4 +1061,56 @@ test_expect_success 'am --quit keeps HEAD where it is' ' test_cmp expected actual ' +test_expect_success 'am and .gitattibutes' ' + test_create_repo attributes && + ( + cd attributes && + test_commit init && + git config filter.test.clean "sed -e '\''s/smudged/clean/g'\''" && + git config filter.test.smudge "sed -e '\''s/clean/smudged/g'\''" && + + test_commit second && + git checkout -b test HEAD^ && + + echo "*.txt filter=test conflict-marker-size=10" >.gitattributes && + git add .gitattributes && + test_commit third && + + echo "This text is smudged." >a.txt && + git add a.txt && + test_commit fourth && + + git checkout -b removal HEAD^ && + git rm .gitattributes && + git add -u && + test_commit fifth && + git cherry-pick test && + + git checkout -b conflict third && + echo "This text is different." >a.txt && + git add a.txt && + test_commit sixth && + + git checkout test && + git format-patch --stdout master..HEAD >patches && + git reset --hard master && + git am patches && + grep "smudged" a.txt && + + git checkout removal && + git reset --hard && + git format-patch --stdout master..HEAD >patches && + git reset --hard master && + git am patches && + grep "clean" a.txt && + + git checkout conflict && + git reset --hard && + git format-patch --stdout master..HEAD >patches && + git reset --hard fourth && + test_must_fail git am -3 patches && + grep "<<<<<<<<<<" a.txt + ) +' + test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index c20209324c..e88ccb04a9 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -1707,4 +1707,11 @@ test_expect_success '--exclude-promisor-objects does not BUG-crash' ' test_must_fail git log --exclude-promisor-objects source-a ' +test_expect_success 'log --end-of-options' ' + git update-ref refs/heads/--source HEAD && + git log --end-of-options --source >actual && + git log >expect && + test_cmp expect actual +' + test_done diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 1db7bd0f59..8319163744 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -132,4 +132,86 @@ test_expect_success '--raw is forbidden' ' test_must_fail git log -L1,24:b.c --raw ' +test_expect_success 'setup for checking fancy rename following' ' + git checkout --orphan moves-start && + git reset --hard && + + printf "%s\n" 12 13 14 15 b c d e >file-1 && + printf "%s\n" 22 23 24 25 B C D E >file-2 && + git add file-1 file-2 && + test_tick && + git commit -m "Add file-1 and file-2" && + oid_add_f1_f2=$(git rev-parse --short HEAD) && + + git checkout -b moves-main && + printf "%s\n" 11 12 13 14 15 b c d e >file-1 && + git commit -a -m "Modify file-1 on main" && + oid_mod_f1_main=$(git rev-parse --short HEAD) && + + printf "%s\n" 21 22 23 24 25 B C D E >file-2 && + git commit -a -m "Modify file-2 on main #1" && + oid_mod_f2_main_1=$(git rev-parse --short HEAD) && + + git mv file-1 renamed-1 && + git commit -m "Rename file-1 to renamed-1 on main" && + + printf "%s\n" 11 12 13 14 15 b c d e f >renamed-1 && + git commit -a -m "Modify renamed-1 on main" && + oid_mod_r1_main=$(git rev-parse --short HEAD) && + + printf "%s\n" 21 22 23 24 25 B C D E F >file-2 && + git commit -a -m "Modify file-2 on main #2" && + oid_mod_f2_main_2=$(git rev-parse --short HEAD) && + + git checkout -b moves-side moves-start && + printf "%s\n" 12 13 14 15 16 b c d e >file-1 && + git commit -a -m "Modify file-1 on side #1" && + oid_mod_f1_side_1=$(git rev-parse --short HEAD) && + + printf "%s\n" 22 23 24 25 26 B C D E >file-2 && + git commit -a -m "Modify file-2 on side" && + oid_mod_f2_side=$(git rev-parse --short HEAD) && + + git mv file-2 renamed-2 && + git commit -m "Rename file-2 to renamed-2 on side" && + + printf "%s\n" 12 13 14 15 16 a b c d e >file-1 && + git commit -a -m "Modify file-1 on side #2" && + oid_mod_f1_side_2=$(git rev-parse --short HEAD) && + + printf "%s\n" 22 23 24 25 26 A B C D E >renamed-2 && + git commit -a -m "Modify renamed-2 on side" && + oid_mod_r2_side=$(git rev-parse --short HEAD) && + + git checkout moves-main && + git merge moves-side && + oid_merge=$(git rev-parse --short HEAD) +' + +test_expect_success 'fancy rename following #1' ' + cat >expect <<-EOF && + $oid_merge Merge branch '\''moves-side'\'' into moves-main + $oid_mod_f1_side_2 Modify file-1 on side #2 + $oid_mod_f1_side_1 Modify file-1 on side #1 + $oid_mod_r1_main Modify renamed-1 on main + $oid_mod_f1_main Modify file-1 on main + $oid_add_f1_f2 Add file-1 and file-2 + EOF + git log -L1:renamed-1 --oneline --no-patch >actual && + test_cmp expect actual +' + +test_expect_success 'fancy rename following #2' ' + cat >expect <<-EOF && + $oid_merge Merge branch '\''moves-side'\'' into moves-main + $oid_mod_r2_side Modify renamed-2 on side + $oid_mod_f2_side Modify file-2 on side + $oid_mod_f2_main_2 Modify file-2 on main #2 + $oid_mod_f2_main_1 Modify file-2 on main #1 + $oid_add_f1_f2 Add file-1 and file-2 + EOF + git log -L1:renamed-2 --oneline --no-patch >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index 271eb5a1fd..3e7b23cb32 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh @@ -204,4 +204,23 @@ test_expect_success EXPENSIVE,LONG_IS_64BIT,UNZIP,UNZIP_ZIP64_SUPPORT,ZIPINFO \ grep $size big.lst ' +build_tree() { + perl -e ' + my $hash = $ARGV[0]; + foreach my $order (2..6) { + $first = 10 ** $order; + foreach my $i (-13..-9) { + my $name = "a" x ($first + $i); + print "100644 blob $hash\t$name\n" + } + } + ' "$1" +} + +test_expect_success 'tar archive with long paths' ' + blob=$(echo foo | git hash-object -w --stdin) && + tree=$(build_tree $blob | git mktree) && + git archive -o long_paths.tar $tree +' + test_done diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh index dacb440b27..f4338abb78 100755 --- a/t/t5307-pack-missing-commit.sh +++ b/t/t5307-pack-missing-commit.sh @@ -24,11 +24,11 @@ test_expect_success 'check corruption' ' ' test_expect_success 'rev-list notices corruption (1)' ' - test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list HEAD + test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list HEAD ' test_expect_success 'rev-list notices corruption (2)' ' - test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --objects HEAD + test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --objects HEAD ' test_expect_success 'pack-objects notices corruption' ' diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 22cb9d6643..ab3eccf0fa 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -23,11 +23,10 @@ test_expect_success 'write graph with no packs' ' test_path_is_missing info/commit-graph ' -test_expect_success 'close with correct error on bad input' ' +test_expect_success 'exit with correct error on bad input to --stdin-packs' ' cd "$TRASH_DIRECTORY/full" && echo doesnotexist >in && - { git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } && - test "$ret" = 1 && + test_expect_code 1 git commit-graph write --stdin-packs <in 2>stderr && test_i18ngrep "error adding pack" stderr ' @@ -41,6 +40,15 @@ test_expect_success 'create commits and repack' ' git repack ' +test_expect_success 'exit with correct error on bad input to --stdin-commits' ' + cd "$TRASH_DIRECTORY/full" && + echo HEAD | test_expect_code 1 git commit-graph write --stdin-commits 2>stderr && + test_i18ngrep "invalid commit object id" stderr && + # valid tree OID, but not a commit OID + git rev-parse HEAD^{tree} | test_expect_code 1 git commit-graph write --stdin-commits 2>stderr && + test_i18ngrep "invalid commit object id" stderr +' + graph_git_two_modes() { git -c core.commitGraph=true $1 >output git -c core.commitGraph=false $1 >expect diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh index 99f4ef4c19..e2c39533d3 100755 --- a/t/t5324-split-commit-graph.sh +++ b/t/t5324-split-commit-graph.sh @@ -8,6 +8,7 @@ GIT_TEST_COMMIT_GRAPH=0 test_expect_success 'setup repo' ' git init && git config core.commitGraph true && + git config gc.writeCommitGraph false && infodir=".git/objects/info" && graphdir="$infodir/commit-graphs" && test_oid_init @@ -334,6 +335,7 @@ test_expect_success 'split across alternate where alternate is not split' ' git clone --no-hardlinks . alt-split && ( cd alt-split && + rm -f .git/objects/info/commit-graph && echo "$(pwd)"/../.git/objects >.git/objects/info/alternates && test_commit 18 && git commit-graph write --reachable --split && diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 1c71c0ec77..5115711562 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -708,13 +708,22 @@ do # file with scheme for p in file do - test_expect_success "fetch-pack --diag-url $p://$h/$r" ' + test_expect_success !MINGW "fetch-pack --diag-url $p://$h/$r" ' check_prot_path $p://$h/$r $p "/$r" ' + test_expect_success MINGW "fetch-pack --diag-url $p://$h/$r" ' + check_prot_path $p://$h/$r $p "//$h/$r" + ' + test_expect_success MINGW "fetch-pack --diag-url $p:///$r" ' + check_prot_path $p:///$r $p "/$r" + ' # No "/~" -> "~" conversion for file - test_expect_success "fetch-pack --diag-url $p://$h/~$r" ' + test_expect_success !MINGW "fetch-pack --diag-url $p://$h/~$r" ' check_prot_path $p://$h/~$r $p "/~$r" ' + test_expect_success MINGW "fetch-pack --diag-url $p://$h/~$r" ' + check_prot_path $p://$h/~$r $p "//$h/~$r" + ' done # file without scheme for h in nohost nohost:12 [::1] [::1]:23 [ [:aa @@ -920,4 +929,7 @@ test_expect_success 'fetch with --filter=blob:limit=0 and HTTP' ' fetch_filter_blob_limit_zero "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server" ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 139f7106f7..ecabbe1616 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -570,6 +570,19 @@ test_expect_success 'LHS of refspec follows ref disambiguation rules' ' ) ' +test_expect_success 'fetch.writeCommitGraph' ' + git clone three write && + ( + cd three && + test_commit new + ) && + ( + cd write && + git -c fetch.writeCommitGraph fetch origin && + test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain + ) +' + # configured prune tests set_config_tristate () { @@ -902,6 +915,29 @@ test_expect_success C_LOCALE_OUTPUT 'fetch compact output' ' test_cmp expect actual ' +test_expect_success '--no-show-forced-updates' ' + mkdir forced-updates && + ( + cd forced-updates && + git init && + test_commit 1 && + test_commit 2 + ) && + git clone forced-updates forced-update-clone && + git clone forced-updates no-forced-update-clone && + git -C forced-updates reset --hard HEAD~1 && + ( + cd forced-update-clone && + git fetch --show-forced-updates origin 2>output && + test_i18ngrep "(forced update)" output + ) && + ( + cd no-forced-update-clone && + git fetch --no-show-forced-updates origin 2>output && + test_i18ngrep ! "(forced update)" output + ) +' + setup_negotiation_tip () { SERVER="$1" URL="$2" @@ -978,27 +1014,7 @@ test_expect_success '--negotiation-tip limits "have" lines sent with HTTP protoc check_negotiation_tip ' -test_expect_success '--no-show-forced-updates' ' - mkdir forced-updates && - ( - cd forced-updates && - git init && - test_commit 1 && - test_commit 2 - ) && - git clone forced-updates forced-update-clone && - git clone forced-updates no-forced-update-clone && - git -C forced-updates reset --hard HEAD~1 && - ( - cd forced-update-clone && - git fetch --show-forced-updates origin 2>output && - test_i18ngrep "(forced update)" output - ) && - ( - cd no-forced-update-clone && - git fetch --no-show-forced-updates origin 2>output && - ! test_i18ngrep "(forced update)" output - ) -' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. test_done diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index e55d8474ef..961eb35c99 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -12,9 +12,6 @@ GIT_TEST_PROTOCOL_VERSION= . ./test-lib.sh -LF=' -' - test_expect_success setup ' GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" && GIT_COMMITTER_DATE="2006-06-26 00:00:00 +0000" && diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh index c05a661400..e4edd56404 100755 --- a/t/t5517-push-mirror.sh +++ b/t/t5517-push-mirror.sh @@ -265,4 +265,14 @@ test_expect_success 'remote.foo.mirror=no has no effect' ' ' +test_expect_success 'push to mirrored repository with refspec fails' ' + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git config --add remote.up.mirror true && + test_must_fail git push up master + ) +' + test_done diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh index 66f0b64d39..97a67728ca 100755 --- a/t/t5537-fetch-shallow.sh +++ b/t/t5537-fetch-shallow.sh @@ -255,4 +255,7 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f git -C client fsck ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh index 6d1d59c9b1..04b34c4de1 100755 --- a/t/t5545-push-options.sh +++ b/t/t5545-push-options.sh @@ -278,4 +278,7 @@ test_expect_success 'push options keep quoted characters intact (http)' ' test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh index 8a14be51a1..f70cbcc9ca 100755 --- a/t/t5552-skipping-fetch-negotiator.sh +++ b/t/t5552-skipping-fetch-negotiator.sh @@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' -test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' - rm -rf server client trace && - git init server && - test_commit -C server to_fetch && - - git init client && - test_commit -C client on_client && - git -C client checkout on_client && - - test_config -C client fetch.negotiationAlgorithm invalid && - test_must_fail git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep "unknown fetch negotiation algorithm" err && - - # Explicit "default" value - test_config -C client fetch.negotiationAlgorithm default && - git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && - - # Implementation detail: If there is nothing to fetch, we will not error out - test_config -C client fetch.negotiationAlgorithm invalid && - git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep ! "unknown fetch negotiation algorithm" err -' - test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server && diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh new file mode 100755 index 0000000000..81975ad8f9 --- /dev/null +++ b/t/t5553-set-upstream.sh @@ -0,0 +1,178 @@ +#!/bin/sh + +test_description='"git fetch/pull --set-upstream" basic tests.' +. ./test-lib.sh + +check_config () { + printf "%s\n" "$2" "$3" >"expect.$1" && + { + git config "branch.$1.remote" && git config "branch.$1.merge" + } >"actual.$1" && + test_cmp "expect.$1" "actual.$1" +} + +check_config_missing () { + test_expect_code 1 git config "branch.$1.remote" && + test_expect_code 1 git config "branch.$1.merge" +} + +clear_config () { + for branch in "$@"; do + test_might_fail git config --unset-all "branch.$branch.remote" + test_might_fail git config --unset-all "branch.$branch.merge" + done +} + +ensure_fresh_upstream () { + rm -rf parent && git init --bare parent +} + +test_expect_success 'setup bare parent fetch' ' + ensure_fresh_upstream && + git remote add upstream parent +' + +test_expect_success 'setup commit on master and other fetch' ' + test_commit one && + git push upstream master && + git checkout -b other && + test_commit two && + git push upstream other +' + +# tests for fetch --set-upstream + +test_expect_success 'fetch --set-upstream does not set upstream w/o branch' ' + clear_config master other && + git checkout master && + git fetch --set-upstream upstream && + check_config_missing master && + check_config_missing other +' + +test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' ' + clear_config master other && + git fetch --set-upstream upstream master && + check_config master upstream refs/heads/master && + check_config_missing other +' + +test_expect_success 'fetch --set-upstream upstream other sets branch other' ' + clear_config master other && + git fetch --set-upstream upstream other && + check_config master upstream refs/heads/other && + check_config_missing other +' + +test_expect_success 'fetch --set-upstream master:other does not set the branch other2' ' + clear_config other2 && + git fetch --set-upstream upstream master:other2 && + check_config_missing other2 +' + +test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' ' + # master explicitly not cleared, we check that it is not touched from previous value + clear_config other other2 && + test_must_fail git fetch --set-upstream http://nosuchdomain.example.com && + check_config master upstream refs/heads/other && + check_config_missing other && + check_config_missing other2 +' + +test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' ' + clear_config other other2 && + url="file://'"$PWD"'" && + git fetch --set-upstream "$url" && + check_config master "$url" HEAD && + check_config_missing other && + check_config_missing other2 +' + +# tests for pull --set-upstream + +test_expect_success 'setup bare parent pull' ' + git remote rm upstream && + ensure_fresh_upstream && + git remote add upstream parent +' + +test_expect_success 'setup commit on master and other pull' ' + test_commit three && + git push --tags upstream master && + test_commit four && + git push upstream other +' + +test_expect_success 'pull --set-upstream upstream master sets branch master but not other' ' + clear_config master other && + git pull --set-upstream upstream master && + check_config master upstream refs/heads/master && + check_config_missing other +' + +test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' ' + clear_config other2 && + git pull --set-upstream upstream master:other2 && + check_config_missing other2 +' + +test_expect_success 'pull --set-upstream upstream other sets branch master' ' + clear_config master other && + git pull --set-upstream upstream other && + check_config master upstream refs/heads/other && + check_config_missing other +' + +test_expect_success 'pull --set-upstream upstream tag does not set the tag' ' + clear_config three && + git pull --tags --set-upstream upstream three && + check_config_missing three +' + +test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' ' + # master explicitly not cleared, we check that it is not touched from previous value + clear_config other other2 three && + test_must_fail git pull --set-upstream http://nosuchdomain.example.com && + check_config master upstream refs/heads/other && + check_config_missing other && + check_config_missing other2 && + check_config_missing three +' + +test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' ' + clear_config master other && + git pull --set-upstream upstream HEAD && + check_config master upstream HEAD && + git checkout other && + git pull --set-upstream upstream HEAD && + check_config other upstream HEAD +' + +test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' ' + clear_config master three && + git pull --set-upstream upstream master three && + check_config_missing master && + check_config_missing three +' + +test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' ' + clear_config master other other2 && + git checkout master && + url="file://'"$PWD"'" && + git pull --set-upstream "$url" && + check_config master "$url" HEAD && + check_config_missing other && + check_config_missing other2 +' + +test_expect_success 'pull --set-upstream with valid URL and branch sets branch' ' + clear_config master other other2 && + git checkout master && + url="file://'"$PWD"'" && + git pull --set-upstream "$url" master && + check_config master "$url" refs/heads/master && + check_config_missing other && + check_config_missing other2 +' + +test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 37d76808d4..ad8c41176e 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -434,7 +434,6 @@ test_expect_success 'double quoted plink.exe in GIT_SSH_COMMAND' ' expect_ssh "-v -P 123" myhost src ' -SQ="'" test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' ' copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" && GIT_SSH_COMMAND="$SQ$TRASH_DIRECTORY/plink.exe$SQ -v" \ @@ -654,7 +653,8 @@ partial_clone () { git -C client fsck && # Ensure that unneeded blobs are not inadvertently fetched. - test_config -C client extensions.partialclone "not a remote" && + test_config -C client remote.origin.promisor "false" && + git -C client config --unset remote.origin.partialclonefilter && test_must_fail git -C client cat-file -e "$HASH1" && # But this blob was fetched, because clone performs an initial checkout @@ -739,4 +739,7 @@ test_expect_success 'partial clone using HTTP' ' partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server" ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh index 2a0fb15cf1..b7a3fdf02d 100755 --- a/t/t5607-clone-bundle.sh +++ b/t/t5607-clone-bundle.sh @@ -83,4 +83,15 @@ test_expect_success 'failed bundle creation does not leave cruft' ' test_path_is_missing fail.bundle.lock ' +test_expect_success 'fetch SHA-1 from bundle' ' + test_create_repo foo && + test_commit -C foo x && + git -C foo bundle create tip.bundle -1 master && + git -C foo rev-parse HEAD >hash && + + # Exercise to ensure that fetching a SHA-1 from a bundle works with no + # errors + git fetch --no-tags foo/tip.bundle "$(cat hash)" +' + test_done diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index b91ef548f8..fc634a56b2 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh @@ -42,8 +42,8 @@ test_expect_success 'do partial clone 1' ' test_cmp expect_1.oids observed.oids && test "$(git -C pc1 config --local core.repositoryformatversion)" = "1" && - test "$(git -C pc1 config --local extensions.partialclone)" = "origin" && - test "$(git -C pc1 config --local core.partialclonefilter)" = "blob:none" + test "$(git -C pc1 config --local remote.origin.promisor)" = "true" && + test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none" ' # checkout master to force dynamic object fetch of blobs at HEAD. @@ -208,6 +208,25 @@ test_expect_success 'use fsck before and after manually fetching a missing subtr test_cmp unique_types.expected unique_types.observed ' +test_expect_success 'implicitly construct combine: filter with repeated flags' ' + GIT_TRACE=$(pwd)/trace git clone --bare \ + --filter=blob:none --filter=tree:1 \ + "file://$(pwd)/srv.bare" pc2 && + grep "trace:.* git pack-objects .*--filter=combine:blob:none+tree:1" \ + trace && + git -C pc2 rev-list --objects --missing=allow-any HEAD >objects && + + # We should have gotten some root trees. + grep " $" objects && + # Should not have gotten any non-root trees or blobs. + ! grep " ." objects && + + xargs -n 1 git -C pc2 cat-file -t <objects >types && + sort -u types >unique_types.actual && + test_write_lines commit tree >unique_types.expected && + test_cmp unique_types.expected unique_types.actual +' + test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' ' rm -rf src dst && git init src && @@ -417,4 +436,7 @@ test_expect_success 'tolerate server sending REF_DELTA against missing promisor ! test -e "$HTTPD_ROOT_PATH/one-time-sed" ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh index 7c9511c593..2571eb90b7 100755 --- a/t/t5700-protocol-v1.sh +++ b/t/t5700-protocol-v1.sh @@ -292,4 +292,7 @@ test_expect_success 'push with http:// using protocol v1' ' grep "git< version 1" log ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index 011b81d4fc..ae9175cedf 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -631,6 +631,19 @@ test_expect_success 'fetch with http:// using protocol v2' ' grep "git< version 2" log ' +test_expect_success 'fetch with http:// by hash without tag following with protocol v2 does not list refs' ' + test_when_finished "rm -f log" && + + test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two_a && + git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" rev-parse two_a >two_a_hash && + + GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \ + fetch --no-tags origin $(cat two_a_hash) && + + grep "fetch< version 2" log && + ! grep "fetch> command=ls-refs" log +' + test_expect_success 'fetch from namespaced repo respects namespaces' ' test_when_finished "rm -f log" && @@ -723,4 +736,7 @@ test_expect_success 'when server does not send "ready", expect FLUSH' ' test_i18ngrep "expected no other sections to be sent after no .ready." err ' +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh index de4b6106ef..3a2c143c6d 100755 --- a/t/t5703-upload-pack-ref-in-want.sh +++ b/t/t5703-upload-pack-ref-in-want.sh @@ -157,106 +157,6 @@ test_expect_success 'want-ref with ref we already have commit for' ' check_output ' -. "$TEST_DIRECTORY"/lib-httpd.sh -start_httpd - -REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo" -LOCAL_PRISTINE="$(pwd)/local_pristine" - -test_expect_success 'setup repos for change-while-negotiating test' ' - ( - git init "$REPO" && - cd "$REPO" && - >.git/git-daemon-export-ok && - test_commit m1 && - git tag -d m1 && - - # Local repo with many commits (so that negotiation will take - # more than 1 request/response pair) - git clone "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" "$LOCAL_PRISTINE" && - cd "$LOCAL_PRISTINE" && - git checkout -b side && - test_commit_bulk --id=s 33 && - - # Add novel commits to upstream - git checkout master && - cd "$REPO" && - test_commit m2 && - test_commit m3 && - git tag -d m2 m3 - ) && - git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_sed/repo" && - git -C "$LOCAL_PRISTINE" config protocol.version 2 -' - -inconsistency () { - # Simulate that the server initially reports $2 as the ref - # corresponding to $1, and after that, $1 as the ref corresponding to - # $1. This corresponds to the real-life situation where the server's - # repository appears to change during negotiation, for example, when - # different servers in a load-balancing arrangement serve (stateless) - # RPCs during a single negotiation. - printf "s/%s/%s/" \ - $(git -C "$REPO" rev-parse $1 | tr -d "\n") \ - $(git -C "$REPO" rev-parse $2 | tr -d "\n") \ - >"$HTTPD_ROOT_PATH/one-time-sed" -} - -test_expect_success 'server is initially ahead - no ref in want' ' - git -C "$REPO" config uploadpack.allowRefInWant false && - rm -rf local && - cp -r "$LOCAL_PRISTINE" local && - inconsistency master 1234567890123456789012345678901234567890 && - test_must_fail git -C local fetch 2>err && - test_i18ngrep "fatal: remote error: upload-pack: not our ref" err -' - -test_expect_success 'server is initially ahead - ref in want' ' - git -C "$REPO" config uploadpack.allowRefInWant true && - rm -rf local && - cp -r "$LOCAL_PRISTINE" local && - inconsistency master 1234567890123456789012345678901234567890 && - git -C local fetch && - - git -C "$REPO" rev-parse --verify master >expected && - git -C local rev-parse --verify refs/remotes/origin/master >actual && - test_cmp expected actual -' - -test_expect_success 'server is initially behind - no ref in want' ' - git -C "$REPO" config uploadpack.allowRefInWant false && - rm -rf local && - cp -r "$LOCAL_PRISTINE" local && - inconsistency master "master^" && - git -C local fetch && - - git -C "$REPO" rev-parse --verify "master^" >expected && - git -C local rev-parse --verify refs/remotes/origin/master >actual && - test_cmp expected actual -' - -test_expect_success 'server is initially behind - ref in want' ' - git -C "$REPO" config uploadpack.allowRefInWant true && - rm -rf local && - cp -r "$LOCAL_PRISTINE" local && - inconsistency master "master^" && - git -C local fetch && - - git -C "$REPO" rev-parse --verify "master" >expected && - git -C local rev-parse --verify refs/remotes/origin/master >actual && - test_cmp expected actual -' - -test_expect_success 'server loses a ref - ref in want' ' - git -C "$REPO" config uploadpack.allowRefInWant true && - rm -rf local && - cp -r "$LOCAL_PRISTINE" local && - echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" && - test_must_fail git -C local fetch 2>err && - - test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err -' - REPO="$(pwd)/repo" LOCAL_PRISTINE="$(pwd)/local_pristine" @@ -372,4 +272,108 @@ test_expect_success 'fetching with wildcard that matches multiple refs' ' grep "want-ref refs/heads/o/bar" log ' +. "$TEST_DIRECTORY"/lib-httpd.sh +start_httpd + +REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo" +LOCAL_PRISTINE="$(pwd)/local_pristine" + +test_expect_success 'setup repos for change-while-negotiating test' ' + ( + git init "$REPO" && + cd "$REPO" && + >.git/git-daemon-export-ok && + test_commit m1 && + git tag -d m1 && + + # Local repo with many commits (so that negotiation will take + # more than 1 request/response pair) + rm -rf "$LOCAL_PRISTINE" && + git clone "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" "$LOCAL_PRISTINE" && + cd "$LOCAL_PRISTINE" && + git checkout -b side && + test_commit_bulk --id=s 33 && + + # Add novel commits to upstream + git checkout master && + cd "$REPO" && + test_commit m2 && + test_commit m3 && + git tag -d m2 m3 + ) && + git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_sed/repo" && + git -C "$LOCAL_PRISTINE" config protocol.version 2 +' + +inconsistency () { + # Simulate that the server initially reports $2 as the ref + # corresponding to $1, and after that, $1 as the ref corresponding to + # $1. This corresponds to the real-life situation where the server's + # repository appears to change during negotiation, for example, when + # different servers in a load-balancing arrangement serve (stateless) + # RPCs during a single negotiation. + printf "s/%s/%s/" \ + $(git -C "$REPO" rev-parse $1 | tr -d "\n") \ + $(git -C "$REPO" rev-parse $2 | tr -d "\n") \ + >"$HTTPD_ROOT_PATH/one-time-sed" +} + +test_expect_success 'server is initially ahead - no ref in want' ' + git -C "$REPO" config uploadpack.allowRefInWant false && + rm -rf local && + cp -r "$LOCAL_PRISTINE" local && + inconsistency master 1234567890123456789012345678901234567890 && + test_must_fail git -C local fetch 2>err && + test_i18ngrep "fatal: remote error: upload-pack: not our ref" err +' + +test_expect_success 'server is initially ahead - ref in want' ' + git -C "$REPO" config uploadpack.allowRefInWant true && + rm -rf local && + cp -r "$LOCAL_PRISTINE" local && + inconsistency master 1234567890123456789012345678901234567890 && + git -C local fetch && + + git -C "$REPO" rev-parse --verify master >expected && + git -C local rev-parse --verify refs/remotes/origin/master >actual && + test_cmp expected actual +' + +test_expect_success 'server is initially behind - no ref in want' ' + git -C "$REPO" config uploadpack.allowRefInWant false && + rm -rf local && + cp -r "$LOCAL_PRISTINE" local && + inconsistency master "master^" && + git -C local fetch && + + git -C "$REPO" rev-parse --verify "master^" >expected && + git -C local rev-parse --verify refs/remotes/origin/master >actual && + test_cmp expected actual +' + +test_expect_success 'server is initially behind - ref in want' ' + git -C "$REPO" config uploadpack.allowRefInWant true && + rm -rf local && + cp -r "$LOCAL_PRISTINE" local && + inconsistency master "master^" && + git -C local fetch && + + git -C "$REPO" rev-parse --verify "master" >expected && + git -C local rev-parse --verify refs/remotes/origin/master >actual && + test_cmp expected actual +' + +test_expect_success 'server loses a ref - ref in want' ' + git -C "$REPO" config uploadpack.allowRefInWant true && + rm -rf local && + cp -r "$LOCAL_PRISTINE" local && + echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" && + test_must_fail git -C local fetch 2>err && + + test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err +' + +# DO NOT add non-httpd-specific tests here, because the last part of this +# test script is only executed when httpd is available and enabled. + test_done diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index 52a9e38d66..b8cf82349b 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -140,4 +140,12 @@ test_expect_success '--header shows a NUL after each commit' ' test_cmp expect actual ' +test_expect_success 'rev-list --end-of-options' ' + git update-ref refs/heads/--output=yikes HEAD && + git rev-list --end-of-options --output=yikes >actual && + test_path_is_missing yikes && + git rev-list HEAD >expect && + test_cmp expect actual +' + test_done diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index da113d975b..cfb74d0e03 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -501,9 +501,8 @@ test_expect_success 'reflog identity' ' ' test_expect_success 'oneline with empty message' ' - git commit -m "dummy" --allow-empty && - git commit -m "dummy" --allow-empty && - git filter-branch --msg-filter "sed -e s/dummy//" HEAD^^.. && + git commit --allow-empty --cleanup=verbatim -m "$LF" && + git commit --allow-empty --allow-empty-message && git rev-list --oneline HEAD >test.txt && test_line_count = 5 test.txt && git rev-list --oneline --graph HEAD >testg.txt && diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh index 545b461e51..bad02cf5b8 100755 --- a/t/t6011-rev-list-with-bad-commit.sh +++ b/t/t6011-rev-list-with-bad-commit.sh @@ -42,7 +42,7 @@ test_expect_success 'corrupt second commit object' \ ' test_expect_success 'rev-list should fail' ' - test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --all > /dev/null + test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --all > /dev/null ' test_expect_success 'git repack _MUST_ fail' \ diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index acd7f5ab80..de0e5a5d36 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -278,7 +278,19 @@ test_expect_success 'verify skipping tree iteration when not collecting omits' ' test_line_count = 2 actual && # Make sure no other trees were considered besides the root. - ! grep "Skipping contents of tree [^.]" filter_trace + ! grep "Skipping contents of tree [^.]" filter_trace && + + # Try this again with "combine:". If both sub-filters are skipping + # trees, the composite filter should also skip trees. This is not + # important unless the user does combine:tree:X+tree:Y or another filter + # besides "tree:" is implemented in the future which can skip trees. + GIT_TRACE=1 git -C r3 rev-list \ + --objects --filter=combine:tree:1+tree:3 HEAD 2>filter_trace && + + # Only skip the dir1/ tree, which is shared between the two commits. + grep "Skipping contents of tree " filter_trace >actual && + test_write_lines "Skipping contents of tree dir1/..." >expected && + test_cmp expected actual ' # Test tree:# filters. @@ -330,6 +342,148 @@ test_expect_success 'verify tree:3 includes everything expected' ' test_line_count = 10 actual ' +test_expect_success 'combine:... for a simple combination' ' + git -C r3 rev-list --objects --filter=combine:tree:2+blob:none HEAD \ + >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD dir1 && + + # There are also 2 commit objects + test_line_count = 5 actual && + + cp actual expected && + + # Try again using repeated --filter - this is equivalent to a manual + # combine with "combine:...+..." + git -C r3 rev-list --objects --filter=combine:tree:2 \ + --filter=blob:none HEAD >actual && + + test_cmp expected actual +' + +test_expect_success 'combine:... with URL encoding' ' + git -C r3 rev-list --objects \ + --filter=combine:tree%3a2+blob:%6Eon%65 HEAD >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD dir1 && + + # There are also 2 commit objects + test_line_count = 5 actual +' + +expect_invalid_filter_spec () { + spec="$1" && + err="$2" && + + test_must_fail git -C r3 rev-list --objects --filter="$spec" HEAD \ + >actual 2>actual_stderr && + test_must_be_empty actual && + test_i18ngrep "$err" actual_stderr +} + +test_expect_success 'combine:... while URL-encoding things that should not be' ' + expect_invalid_filter_spec combine%3Atree:2+blob:none \ + "invalid filter-spec" +' + +test_expect_success 'combine: with nothing after the :' ' + expect_invalid_filter_spec combine: "expected something after combine:" +' + +test_expect_success 'parse error in first sub-filter in combine:' ' + expect_invalid_filter_spec combine:tree:asdf+blob:none \ + "expected .tree:<depth>." +' + +test_expect_success 'combine:... with non-encoded reserved chars' ' + expect_invalid_filter_spec combine:tree:2+sparse:@xyz \ + "must escape char in sub-filter-spec: .@." && + expect_invalid_filter_spec combine:tree:2+sparse:\` \ + "must escape char in sub-filter-spec: .\`." && + expect_invalid_filter_spec combine:tree:2+sparse:~abc \ + "must escape char in sub-filter-spec: .\~." +' + +test_expect_success 'validate err msg for "combine:<valid-filter>+"' ' + expect_invalid_filter_spec combine:tree:2+ "expected .tree:<depth>." +' + +test_expect_success 'combine:... with edge-case hex digits: Ff Aa 0 9' ' + git -C r3 rev-list --objects --filter="combine:tree:2+bl%6Fb:n%6fne" \ + HEAD >actual && + test_line_count = 5 actual && + git -C r3 rev-list --objects --filter="combine:tree%3A2+blob%3anone" \ + HEAD >actual && + test_line_count = 5 actual && + git -C r3 rev-list --objects --filter="combine:tree:%30" HEAD >actual && + test_line_count = 2 actual && + git -C r3 rev-list --objects --filter="combine:tree:%39+blob:none" \ + HEAD >actual && + test_line_count = 5 actual +' + +test_expect_success 'add sparse pattern blobs whose paths have reserved chars' ' + cp r3/pattern r3/pattern1+renamed% && + cp r3/pattern "r3/p;at%ter+n" && + cp r3/pattern r3/^~pattern && + git -C r3 add pattern1+renamed% "p;at%ter+n" ^~pattern && + git -C r3 commit -m "add sparse pattern files with reserved chars" +' + +test_expect_success 'combine:... with more than two sub-filters' ' + git -C r3 rev-list --objects \ + --filter=combine:tree:3+blob:limit=40+sparse:oid=master:pattern \ + HEAD >actual && + + expect_has HEAD "" && + expect_has HEAD~1 "" && + expect_has HEAD~2 "" && + expect_has HEAD dir1 && + expect_has HEAD dir1/sparse1 && + expect_has HEAD dir1/sparse2 && + + # Should also have 3 commits + test_line_count = 9 actual && + + # Try again, this time making sure the last sub-filter is only + # URL-decoded once. + cp actual expect && + + git -C r3 rev-list --objects \ + --filter=combine:tree:3+blob:limit=40+sparse:oid=master:pattern1%2brenamed%25 \ + HEAD >actual && + test_cmp expect actual && + + # Use the same composite filter again, but with a pattern file name that + # requires encoding multiple characters, and use implicit filter + # combining. + test_when_finished "rm -f trace1" && + GIT_TRACE=$(pwd)/trace1 git -C r3 rev-list --objects \ + --filter=tree:3 --filter=blob:limit=40 \ + --filter=sparse:oid="master:p;at%ter+n" \ + HEAD >actual && + + test_cmp expect actual && + grep "Add to combine filter-spec: sparse:oid=master:p%3bat%25ter%2bn" \ + trace1 && + + # Repeat the above test, but this time, the characters to encode are in + # the LHS of the combined filter. + test_when_finished "rm -f trace2" && + GIT_TRACE=$(pwd)/trace2 git -C r3 rev-list --objects \ + --filter=sparse:oid=master:^~pattern \ + --filter=tree:3 --filter=blob:limit=40 \ + HEAD >actual && + + test_cmp expect actual && + grep "Add to combine filter-spec: sparse:oid=master:%5e%7epattern" \ + trace2 +' + # Test provisional omit collection logic with a repo that has objects appearing # at multiple depths - first deeper than the filter's threshold, then shallow. @@ -373,6 +527,37 @@ test_expect_success 'verify skipping tree iteration when collecting omits' ' test_cmp expect actual ' +test_expect_success 'setup r5' ' + git init r5 && + mkdir -p r5/subdir && + + echo 1 >r5/short-root && + echo 12345 >r5/long-root && + echo a >r5/subdir/short-subdir && + echo abcde >r5/subdir/long-subdir && + + git -C r5 add short-root long-root subdir && + git -C r5 commit -m "commit msg" +' + +test_expect_success 'verify collecting omits in combined: filter' ' + # Note that this test guards against the naive implementation of simply + # giving both filters the same "omits" set and expecting it to + # automatically merge them. + git -C r5 rev-list --objects --quiet --filter-print-omitted \ + --filter=combine:tree:2+blob:limit=3 HEAD >actual && + + # Expect 0 trees/commits, 3 blobs omitted (all blobs except short-root) + omitted_1=$(echo 12345 | git hash-object --stdin) && + omitted_2=$(echo a | git hash-object --stdin) && + omitted_3=$(echo abcde | git hash-object --stdin) && + + grep ~$omitted_1 actual && + grep ~$omitted_2 actual && + grep ~$omitted_3 actual && + test_line_count = 3 actual +' + # Test tree:<depth> where a tree is iterated to twice - once where a subentry is # too deep to be included, and again where the blob inside it is shallow enough # to be included. This makes sure we don't use LOFR_MARK_SEEN incorrectly (we @@ -441,11 +626,4 @@ test_expect_success 'expand blob limit in protocol' ' grep "blob:limit=1024" trace ' -test_expect_success 'expand tree depth limit in protocol' ' - GIT_TRACE_PACKET="$(pwd)/tree_trace" git -c protocol.version=2 clone \ - --filter=tree:0k "file://$(pwd)/r2" tree && - ! grep "tree:0k" tree_trace && - grep "tree:0" tree_trace -' - test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index ab69aa176d..9c910ce746 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -526,6 +526,25 @@ test_expect_success 'Check ambiguous head and tag refs II (loose)' ' test_cmp expected actual ' +test_expect_success 'create tag without tagger' ' + git tag -a -m "Broken tag" taggerless && + git tag -f taggerless $(git cat-file tag taggerless | + sed -e "/^tagger /d" | + git hash-object --stdin -w -t tag) +' + +test_atom refs/tags/taggerless type 'commit' +test_atom refs/tags/taggerless tag 'taggerless' +test_atom refs/tags/taggerless tagger '' +test_atom refs/tags/taggerless taggername '' +test_atom refs/tags/taggerless taggeremail '' +test_atom refs/tags/taggerless taggerdate '' +test_atom refs/tags/taggerless committer '' +test_atom refs/tags/taggerless committername '' +test_atom refs/tags/taggerless committeremail '' +test_atom refs/tags/taggerless committerdate '' +test_atom refs/tags/taggerless subject 'Broken tag' + test_expect_success 'an unusual tag with an incomplete line' ' git tag -m "bogo" bogo && diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh index 033871ee5f..f30b4849b6 100755 --- a/t/t6501-freshen-objects.sh +++ b/t/t6501-freshen-objects.sh @@ -137,7 +137,7 @@ test_expect_success 'do not complain about existing broken links (commit)' ' some message EOF commit=$(git hash-object -t commit -w broken-commit) && - git gc 2>stderr && + git gc -q 2>stderr && verbose git cat-file -e $commit && test_must_be_empty stderr ' @@ -147,7 +147,7 @@ test_expect_success 'do not complain about existing broken links (tree)' ' 100644 blob 0000000000000000000000000000000000000003 foo EOF tree=$(git mktree --missing <broken-tree) && - git gc 2>stderr && + git gc -q 2>stderr && git cat-file -e $tree && test_must_be_empty stderr ' @@ -162,7 +162,7 @@ test_expect_success 'do not complain about existing broken links (tag)' ' this is a broken tag EOF tag=$(git hash-object -t tag -w broken-tag) && - git gc 2>stderr && + git gc -q 2>stderr && git cat-file -e $tag && test_must_be_empty stderr ' diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index a2c45d1902..d01fd120ab 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -669,6 +669,28 @@ test_expect_success 'git clean -d skips untracked dirs containing ignored files' test_path_is_missing foo/b/bb ' +test_expect_failure 'git clean -d skips nested repo containing ignored files' ' + test_when_finished "rm -rf nested-repo-with-ignored-file" && + + git init nested-repo-with-ignored-file && + ( + cd nested-repo-with-ignored-file && + >file && + git add file && + git commit -m Initial && + + # This file is ignored by a .gitignore rule in the outer repo + # added in the previous test. + >ignoreme + ) && + + git clean -fd && + + test_path_is_file nested-repo-with-ignored-file/.git/index && + test_path_is_file nested-repo-with-ignored-file/ignoreme && + test_path_is_file nested-repo-with-ignored-file/file +' + test_expect_success MINGW 'handle clean & core.longpaths = false nicely' ' test_config core.longpaths false && a50=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index c973278300..df34c994d2 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -158,7 +158,6 @@ test_expect_success 'submodule update --init from and of subdirectory' ' test_i18ncmp expect2 actual2 ' -apos="'"; test_expect_success 'submodule update does not fetch already present commits' ' (cd submodule && echo line3 >> file && @@ -168,7 +167,7 @@ test_expect_success 'submodule update does not fetch already present commits' ' ) && (cd super/submodule && head=$(git rev-parse --verify HEAD) && - echo "Submodule path ${apos}submodule$apos: checked out $apos$head$apos" > ../../expected && + echo "Submodule path ${SQ}submodule$SQ: checked out $SQ$head$SQ" > ../../expected && git reset --hard HEAD~1 ) && (cd super && diff --git a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh new file mode 100755 index 0000000000..b3485450a2 --- /dev/null +++ b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh @@ -0,0 +1,281 @@ +#!/bin/sh + +test_description='pre-commit and pre-merge-commit hooks' + +. ./test-lib.sh + +HOOKDIR="$(git rev-parse --git-dir)/hooks" +PRECOMMIT="$HOOKDIR/pre-commit" +PREMERGE="$HOOKDIR/pre-merge-commit" + +# Prepare sample scripts that write their $0 to actual_hooks +test_expect_success 'sample script setup' ' + mkdir -p "$HOOKDIR" && + write_script "$HOOKDIR/success.sample" <<-\EOF && + echo $0 >>actual_hooks + exit 0 + EOF + write_script "$HOOKDIR/fail.sample" <<-\EOF && + echo $0 >>actual_hooks + exit 1 + EOF + write_script "$HOOKDIR/non-exec.sample" <<-\EOF && + echo $0 >>actual_hooks + exit 1 + EOF + chmod -x "$HOOKDIR/non-exec.sample" && + write_script "$HOOKDIR/require-prefix.sample" <<-\EOF && + echo $0 >>actual_hooks + test $GIT_PREFIX = "success/" + EOF + write_script "$HOOKDIR/check-author.sample" <<-\EOF + echo $0 >>actual_hooks + test "$GIT_AUTHOR_NAME" = "New Author" && + test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com" + EOF +' + +test_expect_success 'root commit' ' + echo "root" >file && + git add file && + git commit -m "zeroth" && + git checkout -b side && + echo "foo" >foo && + git add foo && + git commit -m "make it non-ff" && + git branch side-orig side && + git checkout master +' + +test_expect_success 'setup conflicting branches' ' + test_when_finished "git checkout master" && + git checkout -b conflicting-a master && + echo a >conflicting && + git add conflicting && + git commit -m conflicting-a && + git checkout -b conflicting-b master && + echo b >conflicting && + git add conflicting && + git commit -m conflicting-b +' + +test_expect_success 'with no hook' ' + test_when_finished "rm -f actual_hooks" && + echo "foo" >file && + git add file && + git commit -m "first" && + test_path_is_missing actual_hooks +' + +test_expect_success 'with no hook (merge)' ' + test_when_finished "rm -f actual_hooks" && + git branch -f side side-orig && + git checkout side && + git merge -m "merge master" master && + git checkout master && + test_path_is_missing actual_hooks +' + +test_expect_success '--no-verify with no hook' ' + test_when_finished "rm -f actual_hooks" && + echo "bar" >file && + git add file && + git commit --no-verify -m "bar" && + test_path_is_missing actual_hooks +' + +test_expect_success '--no-verify with no hook (merge)' ' + test_when_finished "rm -f actual_hooks" && + git branch -f side side-orig && + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master && + test_path_is_missing actual_hooks +' + +test_expect_success 'with succeeding hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" && + cp "$HOOKDIR/success.sample" "$PRECOMMIT" && + echo "$PRECOMMIT" >expected_hooks && + echo "more" >>file && + git add file && + git commit -m "more" && + test_cmp expected_hooks actual_hooks +' + +test_expect_success 'with succeeding hook (merge)' ' + test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" && + cp "$HOOKDIR/success.sample" "$PREMERGE" && + echo "$PREMERGE" >expected_hooks && + git checkout side && + git merge -m "merge master" master && + git checkout master && + test_cmp expected_hooks actual_hooks +' + +test_expect_success 'automatic merge fails; both hooks are available' ' + test_when_finished "rm -f \"$PREMERGE\" \"$PRECOMMIT\"" && + test_when_finished "rm -f expected_hooks actual_hooks" && + test_when_finished "git checkout master" && + cp "$HOOKDIR/success.sample" "$PREMERGE" && + cp "$HOOKDIR/success.sample" "$PRECOMMIT" && + + git checkout conflicting-a && + test_must_fail git merge -m "merge conflicting-b" conflicting-b && + test_path_is_missing actual_hooks && + + echo "$PRECOMMIT" >expected_hooks && + echo a+b >conflicting && + git add conflicting && + git commit -m "resolve conflict" && + test_cmp expected_hooks actual_hooks +' + +test_expect_success '--no-verify with succeeding hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" && + cp "$HOOKDIR/success.sample" "$PRECOMMIT" && + echo "even more" >>file && + git add file && + git commit --no-verify -m "even more" && + test_path_is_missing actual_hooks +' + +test_expect_success '--no-verify with succeeding hook (merge)' ' + test_when_finished "rm -f \"$PREMERGE\" actual_hooks" && + cp "$HOOKDIR/success.sample" "$PREMERGE" && + git branch -f side side-orig && + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master && + test_path_is_missing actual_hooks +' + +test_expect_success 'with failing hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" && + cp "$HOOKDIR/fail.sample" "$PRECOMMIT" && + echo "$PRECOMMIT" >expected_hooks && + echo "another" >>file && + git add file && + test_must_fail git commit -m "another" && + test_cmp expected_hooks actual_hooks +' + +test_expect_success '--no-verify with failing hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" && + cp "$HOOKDIR/fail.sample" "$PRECOMMIT" && + echo "stuff" >>file && + git add file && + git commit --no-verify -m "stuff" && + test_path_is_missing actual_hooks +' + +test_expect_success 'with failing hook (merge)' ' + test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" && + cp "$HOOKDIR/fail.sample" "$PREMERGE" && + echo "$PREMERGE" >expected_hooks && + git checkout side && + test_must_fail git merge -m "merge master" master && + git checkout master && + test_cmp expected_hooks actual_hooks +' + +test_expect_success '--no-verify with failing hook (merge)' ' + test_when_finished "rm -f \"$PREMERGE\" actual_hooks" && + cp "$HOOKDIR/fail.sample" "$PREMERGE" && + git branch -f side side-orig && + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master && + test_path_is_missing actual_hooks +' + +test_expect_success POSIXPERM 'with non-executable hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" && + cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" && + echo "content" >>file && + git add file && + git commit -m "content" && + test_path_is_missing actual_hooks +' + +test_expect_success POSIXPERM '--no-verify with non-executable hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" && + cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" && + echo "more content" >>file && + git add file && + git commit --no-verify -m "more content" && + test_path_is_missing actual_hooks +' + +test_expect_success POSIXPERM 'with non-executable hook (merge)' ' + test_when_finished "rm -f \"$PREMERGE\" actual_hooks" && + cp "$HOOKDIR/non-exec.sample" "$PREMERGE" && + git branch -f side side-orig && + git checkout side && + git merge -m "merge master" master && + git checkout master && + test_path_is_missing actual_hooks +' + +test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' ' + test_when_finished "rm -f \"$PREMERGE\" actual_hooks" && + cp "$HOOKDIR/non-exec.sample" "$PREMERGE" && + git branch -f side side-orig && + git checkout side && + git merge --no-verify -m "merge master" master && + git checkout master && + test_path_is_missing actual_hooks +' + +test_expect_success 'with hook requiring GIT_PREFIX' ' + test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks success" && + cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" && + echo "$PRECOMMIT" >expected_hooks && + echo "more content" >>file && + git add file && + mkdir success && + ( + cd success && + git commit -m "hook requires GIT_PREFIX = success/" + ) && + test_cmp expected_hooks actual_hooks +' + +test_expect_success 'with failing hook requiring GIT_PREFIX' ' + test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks fail" && + cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" && + echo "$PRECOMMIT" >expected_hooks && + echo "more content" >>file && + git add file && + mkdir fail && + ( + cd fail && + test_must_fail git commit -m "hook must fail" + ) && + git checkout -- file && + test_cmp expected_hooks actual_hooks +' + +test_expect_success 'check the author in hook' ' + test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" && + cp "$HOOKDIR/check-author.sample" "$PRECOMMIT" && + cat >expected_hooks <<-EOF && + $PRECOMMIT + $PRECOMMIT + $PRECOMMIT + EOF + test_must_fail git commit --allow-empty -m "by a.u.thor" && + ( + GIT_AUTHOR_NAME="New Author" && + GIT_AUTHOR_EMAIL="newauthor@example.com" && + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL && + git commit --allow-empty -m "by new.author via env" && + git show -s + ) && + git commit --author="New Author <newauthor@example.com>" \ + --allow-empty -m "by new.author via command line" && + git show -s && + test_cmp expected_hooks actual_hooks +' + +test_done diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh deleted file mode 100755 index 984889b39d..0000000000 --- a/t/t7503-pre-commit-hook.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/sh - -test_description='pre-commit hook' - -. ./test-lib.sh - -test_expect_success 'with no hook' ' - - echo "foo" > file && - git add file && - git commit -m "first" - -' - -test_expect_success '--no-verify with no hook' ' - - echo "bar" > file && - git add file && - git commit --no-verify -m "bar" - -' - -# now install hook that always succeeds -HOOKDIR="$(git rev-parse --git-dir)/hooks" -HOOK="$HOOKDIR/pre-commit" -mkdir -p "$HOOKDIR" -cat > "$HOOK" <<EOF -#!/bin/sh -exit 0 -EOF -chmod +x "$HOOK" - -test_expect_success 'with succeeding hook' ' - - echo "more" >> file && - git add file && - git commit -m "more" - -' - -test_expect_success '--no-verify with succeeding hook' ' - - echo "even more" >> file && - git add file && - git commit --no-verify -m "even more" - -' - -# now a hook that fails -cat > "$HOOK" <<EOF -#!/bin/sh -exit 1 -EOF - -test_expect_success 'with failing hook' ' - - echo "another" >> file && - git add file && - test_must_fail git commit -m "another" - -' - -test_expect_success '--no-verify with failing hook' ' - - echo "stuff" >> file && - git add file && - git commit --no-verify -m "stuff" - -' - -chmod -x "$HOOK" -test_expect_success POSIXPERM 'with non-executable hook' ' - - echo "content" >> file && - git add file && - git commit -m "content" - -' - -test_expect_success POSIXPERM '--no-verify with non-executable hook' ' - - echo "more content" >> file && - git add file && - git commit --no-verify -m "more content" - -' -chmod +x "$HOOK" - -# a hook that checks $GIT_PREFIX and succeeds inside the -# success/ subdirectory only -cat > "$HOOK" <<EOF -#!/bin/sh -test \$GIT_PREFIX = success/ -EOF - -test_expect_success 'with hook requiring GIT_PREFIX' ' - - echo "more content" >> file && - git add file && - mkdir success && - ( - cd success && - git commit -m "hook requires GIT_PREFIX = success/" - ) && - rmdir success -' - -test_expect_success 'with failing hook requiring GIT_PREFIX' ' - - echo "more content" >> file && - git add file && - mkdir fail && - ( - cd fail && - test_must_fail git commit -m "hook must fail" - ) && - rmdir fail && - git checkout -- file -' - -test_expect_success 'check the author in hook' ' - write_script "$HOOK" <<-\EOF && - test "$GIT_AUTHOR_NAME" = "New Author" && - test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com" - EOF - test_must_fail git commit --allow-empty -m "by a.u.thor" && - ( - GIT_AUTHOR_NAME="New Author" && - GIT_AUTHOR_EMAIL="newauthor@example.com" && - export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL && - git commit --allow-empty -m "by new.author via env" && - git show -s - ) && - git commit --author="New Author <newauthor@example.com>" \ - --allow-empty -m "by new.author via command line" && - git show -s -' - -test_done diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh index e01c285cbf..66d7a62797 100755 --- a/t/t7512-status-help.sh +++ b/t/t7512-status-help.sh @@ -733,6 +733,7 @@ test_expect_success 'status when cherry-picking before resolving conflicts' ' On branch cherry_branch You are currently cherry-picking commit $TO_CHERRY_PICK. (fix conflicts and run "git cherry-pick --continue") + (use "git cherry-pick --skip" to skip this patch) (use "git cherry-pick --abort" to cancel the cherry-pick operation) Unmerged paths: @@ -757,6 +758,7 @@ test_expect_success 'status when cherry-picking after resolving conflicts' ' On branch cherry_branch You are currently cherry-picking commit $TO_CHERRY_PICK. (all conflicts fixed: run "git cherry-pick --continue") + (use "git cherry-pick --skip" to skip this patch) (use "git cherry-pick --abort" to cancel the cherry-pick operation) Changes to be committed: @@ -778,6 +780,7 @@ test_expect_success 'status when cherry-picking after committing conflict resolu On branch cherry_branch Cherry-pick currently in progress. (run "git cherry-pick --continue" to continue) + (use "git cherry-pick --skip" to skip this patch) (use "git cherry-pick --abort" to cancel the cherry-pick operation) nothing to commit (use -u to show untracked files) @@ -835,6 +838,7 @@ test_expect_success 'status while reverting commit (conflicts)' ' On branch master You are currently reverting commit $TO_REVERT. (fix conflicts and run "git revert --continue") + (use "git revert --skip" to skip this patch) (use "git revert --abort" to cancel the revert operation) Unmerged paths: @@ -855,6 +859,7 @@ test_expect_success 'status while reverting commit (conflicts resolved)' ' On branch master You are currently reverting commit $TO_REVERT. (all conflicts fixed: run "git revert --continue") + (use "git revert --skip" to skip this patch) (use "git revert --abort" to cancel the revert operation) Changes to be committed: @@ -887,6 +892,7 @@ test_expect_success 'status while reverting after committing conflict resolution On branch master Revert currently in progress. (run "git revert --continue" to continue) + (use "git revert --skip" to skip this patch) (use "git revert --abort" to cancel the revert operation) nothing to commit (use -u to show untracked files) diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index a11366b4ce..946f91fa57 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -408,4 +408,25 @@ test_expect_success 'grep --recurse-submodules with submodules without .gitmodul test_cmp expect actual ' +reset_and_clean () { + git reset --hard && + git clean -fd && + git submodule foreach --recursive 'git reset --hard' && + git submodule foreach --recursive 'git clean -fd' +} + +test_expect_success 'grep --recurse-submodules without --cached considers worktree modifications' ' + reset_and_clean && + echo "A modified line in submodule" >>submodule/a && + echo "submodule/a:A modified line in submodule" >expect && + git grep --recurse-submodules "A modified line in submodule" >actual && + test_cmp expect actual +' + +test_expect_success 'grep --recurse-submodules with --cached ignores worktree modifications' ' + reset_and_clean && + echo "A modified line in submodule" >>submodule/a && + test_must_fail git grep --recurse-submodules --cached "A modified line in submodule" >actual 2>&1 && + test_must_be_empty actual +' test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 75512c3403..54f8ce18cb 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -28,10 +28,10 @@ complete () # # (2) A test makes sure that common subcommands are included in the # completion for "git <TAB>", and a plumbing is excluded. "add", -# "filter-branch" and "ls-files" are listed for this. +# "rebase" and "ls-files" are listed for this. -GIT_TESTING_ALL_COMMAND_LIST='add checkout check-attr filter-branch ls-files' -GIT_TESTING_PORCELAIN_COMMAND_LIST='add checkout filter-branch' +GIT_TESTING_ALL_COMMAND_LIST='add checkout check-attr rebase ls-files' +GIT_TESTING_PORCELAIN_COMMAND_LIST='add checkout rebase' . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" @@ -1392,12 +1392,12 @@ test_expect_success 'basic' ' # built-in grep -q "^add \$" out && # script - grep -q "^filter-branch \$" out && + grep -q "^rebase \$" out && # plumbing ! grep -q "^ls-files \$" out && - run_completion "git f" && - ! grep -q -v "^f" out + run_completion "git r" && + ! grep -q -v "^r" out ' test_expect_success 'double dash "git" itself' ' @@ -1698,6 +1698,69 @@ do ' done +test_expect_success 'git config - section' ' + test_completion "git config br" <<-\EOF + branch.Z + browser.Z + EOF +' + +test_expect_success 'git config - variable name' ' + test_completion "git config log.d" <<-\EOF + log.date Z + log.decorate Z + EOF +' + +test_expect_success 'git config - value' ' + test_completion "git config color.pager " <<-\EOF + false Z + true Z + EOF +' + +test_expect_success 'git -c - section' ' + test_completion "git -c br" <<-\EOF + branch.Z + browser.Z + EOF +' + +test_expect_success 'git -c - variable name' ' + test_completion "git -c log.d" <<-\EOF + log.date=Z + log.decorate=Z + EOF +' + +test_expect_success 'git -c - value' ' + test_completion "git -c color.pager=" <<-\EOF + false Z + true Z + EOF +' + +test_expect_success 'git clone --config= - section' ' + test_completion "git clone --config=br" <<-\EOF + branch.Z + browser.Z + EOF +' + +test_expect_success 'git clone --config= - variable name' ' + test_completion "git clone --config=log.d" <<-\EOF + log.date=Z + log.decorate=Z + EOF +' + +test_expect_success 'git clone --config= - value' ' + test_completion "git clone --config=color.pager=" <<-\EOF + false Z + true Z + EOF +' + test_expect_success 'sourcing the completion script clears cached commands' ' __git_compute_all_commands && verbose test -n "$__git_all_commands" && diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index e0b3f28d3a..87bf3a2287 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -580,7 +580,7 @@ test_expect_failure () { export test_prereq if ! test_skip "$@" then - say >&3 "checking known breakage: $2" + say >&3 "checking known breakage of $TEST_NUMBER.$test_count '$1': $2" if test_run_ "$2" expecting_failure then test_known_broken_ok_ "$1" @@ -600,7 +600,7 @@ test_expect_success () { export test_prereq if ! test_skip "$@" then - say >&3 "expecting success: $2" + say >&3 "expecting success of $TEST_NUMBER.$test_count '$1': $2" if test_run_ "$2" then test_ok_ "$1" diff --git a/t/test-lib.sh b/t/test-lib.sh index 30b07e310f..ee602c4d9c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -212,6 +212,8 @@ fi TEST_STRESS_JOB_SFX="${GIT_TEST_STRESS_JOB_NR:+.stress-$GIT_TEST_STRESS_JOB_NR}" TEST_NAME="$(basename "$0" .sh)" +TEST_NUMBER="${TEST_NAME%%-*}" +TEST_NUMBER="${TEST_NUMBER#t}" TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/test-results" TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX" TRASH_DIRECTORY="trash directory.$TEST_NAME$TEST_STRESS_JOB_SFX" @@ -507,6 +509,9 @@ EMPTY_BLOB=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 LF=' ' +# Single quote +SQ=\' + # UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores # when case-folding filenames u200c=$(printf '\342\200\214') @@ -212,3 +212,10 @@ int parse_tag(struct tag *item) free(data); return ret; } + +struct object_id *get_tagged_oid(struct tag *tag) +{ + if (!tag->tagged) + die("bad tag"); + return &tag->tagged->oid; +} @@ -19,5 +19,6 @@ struct object *deref_tag(struct repository *r, struct object *, const char *, in struct object *deref_tag_noverify(struct object *); int gpg_verify_tag(const struct object_id *oid, const char *name_to_report, unsigned flags); +struct object_id *get_tagged_oid(struct tag *tag); #endif /* TAG_H */ diff --git a/templates/hooks--pre-merge-commit.sample b/templates/hooks--pre-merge-commit.sample new file mode 100755 index 0000000000..399eab1924 --- /dev/null +++ b/templates/hooks--pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: @@ -88,8 +88,6 @@ static int prepare_trace_line(const char *file, int line, if (!trace_want(key)) return 0; - set_try_to_free_routine(NULL); /* is never reset */ - /* unit tests may want to disable additional trace output */ if (trace_want(&trace_bare)) return 1; diff --git a/trace2/tr2_dst.c b/trace2/tr2_dst.c index 5dda0ca1cd..c69857515f 100644 --- a/trace2/tr2_dst.c +++ b/trace2/tr2_dst.c @@ -215,13 +215,8 @@ connected: static void tr2_dst_malformed_warning(struct tr2_dst *dst, const char *tgt_value) { - struct strbuf buf = STRBUF_INIT; - - strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'", - tr2_sysenv_display_name(dst->sysenv_var), tgt_value); - warning("%s", buf.buf); - - strbuf_release(&buf); + warning("trace2: unknown value for '%s': '%s'", + tr2_sysenv_display_name(dst->sysenv_var), tgt_value); } int tr2_dst_get_trace_fd(struct tr2_dst *dst) diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index c2852d1bd2..9bcac20d1b 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -205,11 +205,6 @@ static void maybe_add_string_va(struct json_writer *jw, const char *field_name, strbuf_release(&buf); return; } - - if (fmt && *fmt) { - jw_object_string(jw, field_name, fmt); - return; - } } static void fn_error_va_fl(const char *file, int line, const char *fmt, diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index 00b116d797..438ed05a40 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -87,7 +87,7 @@ static void fn_start_fl(const char *file, int line, struct strbuf buf_payload = STRBUF_INIT; strbuf_addstr(&buf_payload, "start "); - sq_quote_argv_pretty(&buf_payload, argv); + sq_append_quote_argv_pretty(&buf_payload, argv); normal_io_write_fl(file, line, &buf_payload); strbuf_release(&buf_payload); } @@ -135,11 +135,6 @@ static void maybe_append_string_va(struct strbuf *buf, const char *fmt, va_end(copy_ap); return; } - - if (fmt && *fmt) { - strbuf_addstr(buf, fmt); - return; - } } static void fn_error_va_fl(const char *file, int line, const char *fmt, @@ -147,8 +142,11 @@ static void fn_error_va_fl(const char *file, int line, const char *fmt, { struct strbuf buf_payload = STRBUF_INIT; - strbuf_addstr(&buf_payload, "error "); - maybe_append_string_va(&buf_payload, fmt, ap); + strbuf_addstr(&buf_payload, "error"); + if (fmt && *fmt) { + strbuf_addch(&buf_payload, ' '); + maybe_append_string_va(&buf_payload, fmt, ap); + } normal_io_write_fl(file, line, &buf_payload); strbuf_release(&buf_payload); } @@ -188,8 +186,8 @@ static void fn_alias_fl(const char *file, int line, const char *alias, { struct strbuf buf_payload = STRBUF_INIT; - strbuf_addf(&buf_payload, "alias %s ->", alias); - sq_quote_argv_pretty(&buf_payload, argv); + strbuf_addf(&buf_payload, "alias %s -> ", alias); + sq_append_quote_argv_pretty(&buf_payload, argv); normal_io_write_fl(file, line, &buf_payload); strbuf_release(&buf_payload); } @@ -200,12 +198,12 @@ static void fn_child_start_fl(const char *file, int line, { struct strbuf buf_payload = STRBUF_INIT; - strbuf_addf(&buf_payload, "child_start[%d] ", cmd->trace2_child_id); + strbuf_addf(&buf_payload, "child_start[%d]", cmd->trace2_child_id); if (cmd->dir) { - strbuf_addstr(&buf_payload, " cd"); + strbuf_addstr(&buf_payload, " cd "); sq_quote_buf_pretty(&buf_payload, cmd->dir); - strbuf_addstr(&buf_payload, "; "); + strbuf_addstr(&buf_payload, ";"); } /* @@ -213,9 +211,10 @@ static void fn_child_start_fl(const char *file, int line, * See trace_add_env() in run-command.c as used by original trace.c */ + strbuf_addch(&buf_payload, ' '); if (cmd->git_cmd) - strbuf_addstr(&buf_payload, "git"); - sq_quote_argv_pretty(&buf_payload, cmd->argv); + strbuf_addstr(&buf_payload, "git "); + sq_append_quote_argv_pretty(&buf_payload, cmd->argv); normal_io_write_fl(file, line, &buf_payload); strbuf_release(&buf_payload); @@ -240,9 +239,11 @@ static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, struct strbuf buf_payload = STRBUF_INIT; strbuf_addf(&buf_payload, "exec[%d] ", exec_id); - if (exe) + if (exe) { strbuf_addstr(&buf_payload, exe); - sq_quote_argv_pretty(&buf_payload, argv); + strbuf_addch(&buf_payload, ' '); + } + sq_append_quote_argv_pretty(&buf_payload, argv); normal_io_write_fl(file, line, &buf_payload); strbuf_release(&buf_payload); } diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index ea0cbbe13e..fd979db4ad 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -21,10 +21,10 @@ static struct tr2_dst tr2dst_perf = { TR2_SYSENV_PERF, 0, 0, 0 }; */ static int tr2env_perf_be_brief; -#define TR2FMT_PERF_FL_WIDTH (50) +#define TR2FMT_PERF_FL_WIDTH (28) #define TR2FMT_PERF_MAX_EVENT_NAME (12) -#define TR2FMT_PERF_REPO_WIDTH (4) -#define TR2FMT_PERF_CATEGORY_WIDTH (10) +#define TR2FMT_PERF_REPO_WIDTH (3) +#define TR2FMT_PERF_CATEGORY_WIDTH (12) #define TR2_DOTS_BUFFER_SIZE (100) #define TR2_INDENT (2) @@ -79,17 +79,36 @@ static void perf_fmt_prepare(const char *event_name, if (!tr2env_perf_be_brief) { struct tr2_tbuf tb_now; + size_t fl_end_col; tr2_tbuf_local_time(&tb_now); strbuf_addstr(buf, tb_now.buf); strbuf_addch(buf, ' '); - if (file && *file) - strbuf_addf(buf, "%s:%d ", file, line); - while (buf->len < TR2FMT_PERF_FL_WIDTH) + fl_end_col = buf->len + TR2FMT_PERF_FL_WIDTH; + + if (file && *file) { + struct strbuf buf_fl = STRBUF_INIT; + + strbuf_addf(&buf_fl, "%s:%d", file, line); + + if (buf_fl.len <= TR2FMT_PERF_FL_WIDTH) + strbuf_addbuf(buf, &buf_fl); + else { + size_t avail = TR2FMT_PERF_FL_WIDTH - 3; + strbuf_addstr(buf, "..."); + strbuf_add(buf, + &buf_fl.buf[buf_fl.len - avail], + avail); + } + + strbuf_release(&buf_fl); + } + + while (buf->len < fl_end_col) strbuf_addch(buf, ' '); - strbuf_addstr(buf, "| "); + strbuf_addstr(buf, " | "); } strbuf_addf(buf, "d%d | ", tr2_sid_depth()); @@ -102,7 +121,7 @@ static void perf_fmt_prepare(const char *event_name, strbuf_addf(buf, "r%d ", repo->trace2_repo_id); while (buf->len < len) strbuf_addch(buf, ' '); - strbuf_addstr(buf, "| "); + strbuf_addstr(buf, " | "); if (p_us_elapsed_absolute) strbuf_addf(buf, "%9.6f | ", @@ -116,8 +135,8 @@ static void perf_fmt_prepare(const char *event_name, else strbuf_addf(buf, "%9s | ", " "); - strbuf_addf(buf, "%-*s | ", TR2FMT_PERF_CATEGORY_WIDTH, - (category ? category : "")); + strbuf_addf(buf, "%-*.*s | ", TR2FMT_PERF_CATEGORY_WIDTH, + TR2FMT_PERF_CATEGORY_WIDTH, (category ? category : "")); if (ctx->nr_open_regions > 0) { int len_indent = TR2_INDENT_LENGTH(ctx); @@ -165,7 +184,7 @@ static void fn_start_fl(const char *file, int line, const char *event_name = "start"; struct strbuf buf_payload = STRBUF_INIT; - sq_quote_argv_pretty(&buf_payload, argv); + sq_append_quote_argv_pretty(&buf_payload, argv); perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, NULL, NULL, &buf_payload); @@ -220,11 +239,6 @@ static void maybe_append_string_va(struct strbuf *buf, const char *fmt, va_end(copy_ap); return; } - - if (fmt && *fmt) { - strbuf_addstr(buf, fmt); - return; - } } static void fn_error_va_fl(const char *file, int line, const char *fmt, @@ -285,8 +299,9 @@ static void fn_alias_fl(const char *file, int line, const char *alias, const char *event_name = "alias"; struct strbuf buf_payload = STRBUF_INIT; - strbuf_addf(&buf_payload, "alias:%s argv:", alias); - sq_quote_argv_pretty(&buf_payload, argv); + strbuf_addf(&buf_payload, "alias:%s argv:[", alias); + sq_append_quote_argv_pretty(&buf_payload, argv); + strbuf_addch(&buf_payload, ']'); perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, &buf_payload); @@ -315,10 +330,14 @@ static void fn_child_start_fl(const char *file, int line, sq_quote_buf_pretty(&buf_payload, cmd->dir); } - strbuf_addstr(&buf_payload, " argv:"); - if (cmd->git_cmd) - strbuf_addstr(&buf_payload, " git"); - sq_quote_argv_pretty(&buf_payload, cmd->argv); + strbuf_addstr(&buf_payload, " argv:["); + if (cmd->git_cmd) { + strbuf_addstr(&buf_payload, "git"); + if (cmd->argv[0]) + strbuf_addch(&buf_payload, ' '); + } + sq_append_quote_argv_pretty(&buf_payload, cmd->argv); + strbuf_addch(&buf_payload, ']'); perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, NULL, NULL, &buf_payload); @@ -369,10 +388,14 @@ static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, struct strbuf buf_payload = STRBUF_INIT; strbuf_addf(&buf_payload, "id:%d ", exec_id); - strbuf_addstr(&buf_payload, "argv:"); - if (exe) - strbuf_addf(&buf_payload, " %s", exe); - sq_quote_argv_pretty(&buf_payload, argv); + strbuf_addstr(&buf_payload, "argv:["); + if (exe) { + strbuf_addstr(&buf_payload, exe); + if (argv[0]) + strbuf_addch(&buf_payload, ' '); + } + sq_append_quote_argv_pretty(&buf_payload, argv); + strbuf_addch(&buf_payload, ']'); perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, NULL, NULL, &buf_payload); @@ -433,8 +456,11 @@ static void fn_region_enter_printf_va_fl(const char *file, int line, struct strbuf buf_payload = STRBUF_INIT; if (label) - strbuf_addf(&buf_payload, "label:%s ", label); - maybe_append_string_va(&buf_payload, fmt, ap); + strbuf_addf(&buf_payload, "label:%s", label); + if (fmt && *fmt) { + strbuf_addch(&buf_payload, ' '); + maybe_append_string_va(&buf_payload, fmt, ap); + } perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute, NULL, category, &buf_payload); @@ -450,8 +476,11 @@ static void fn_region_leave_printf_va_fl( struct strbuf buf_payload = STRBUF_INIT; if (label) - strbuf_addf(&buf_payload, "label:%s ", label); - maybe_append_string_va(&buf_payload, fmt, ap); + strbuf_addf(&buf_payload, "label:%s", label); + if (fmt && *fmt) { + strbuf_addch(&buf_payload, ' ' ); + maybe_append_string_va(&buf_payload, fmt, ap); + } perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute, &us_elapsed_region, category, &buf_payload); diff --git a/transport-helper.c b/transport-helper.c index 6b05a88faf..9e1279b928 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -33,6 +33,16 @@ struct helper_data { check_connectivity : 1, no_disconnect_req : 1, no_private_update : 1; + + /* + * As an optimization, the transport code may invoke fetch before + * get_refs_list. If this happens, and if the transport helper doesn't + * support connect or stateless_connect, we need to invoke + * get_refs_list ourselves if we haven't already done so. Keep track of + * whether we have invoked get_refs_list. + */ + unsigned get_refs_list_called : 1; + char *export_marks; char *import_marks; /* These go from remote name (as in "list") to private name */ @@ -652,17 +662,25 @@ static int connect_helper(struct transport *transport, const char *name, return 0; } +static struct ref *get_refs_list_using_list(struct transport *transport, + int for_push); + static int fetch(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct helper_data *data = transport->data; int i, count; + get_helper(transport); + if (process_connect(transport, 0)) { do_take_over(transport); return transport->vtable->fetch(transport, nr_heads, to_fetch); } + if (!data->get_refs_list_called) + get_refs_list_using_list(transport, 0); + count = 0; for (i = 0; i < nr_heads; i++) if (!(to_fetch[i]->status & REF_STATUS_UPTODATE)) @@ -682,13 +700,9 @@ static int fetch(struct transport *transport, set_helper_option(transport, "update-shallow", "true"); if (data->transport_options.filter_options.choice) { - struct strbuf expanded_filter_spec = STRBUF_INIT; - expand_list_objects_filter_spec( - &data->transport_options.filter_options, - &expanded_filter_spec); - set_helper_option(transport, "filter", - expanded_filter_spec.buf); - strbuf_release(&expanded_filter_spec); + const char *spec = expand_list_objects_filter_spec( + &data->transport_options.filter_options); + set_helper_option(transport, "filter", spec); } if (data->transport_options.negotiation_tips) @@ -1059,6 +1073,19 @@ static int has_attribute(const char *attrs, const char *attr) static struct ref *get_refs_list(struct transport *transport, int for_push, const struct argv_array *ref_prefixes) { + get_helper(transport); + + if (process_connect(transport, for_push)) { + do_take_over(transport); + return transport->vtable->get_refs_list(transport, for_push, ref_prefixes); + } + + return get_refs_list_using_list(transport, for_push); +} + +static struct ref *get_refs_list_using_list(struct transport *transport, + int for_push) +{ struct helper_data *data = transport->data; struct child_process *helper; struct ref *ret = NULL; @@ -1066,13 +1093,9 @@ static struct ref *get_refs_list(struct transport *transport, int for_push, struct ref *posn; struct strbuf buf = STRBUF_INIT; + data->get_refs_list_called = 1; helper = get_helper(transport); - if (process_connect(transport, for_push)) { - do_take_over(transport); - return transport->vtable->get_refs_list(transport, for_push, ref_prefixes); - } - if (data->push && for_push) write_str_in_full(helper->in, "list for-push\n"); else @@ -1119,7 +1142,6 @@ static struct ref *get_refs_list(struct transport *transport, int for_push, } static struct transport_vtable vtable = { - 0, set_helper_option, get_refs_list, fetch, diff --git a/transport-internal.h b/transport-internal.h index 004bee5e36..1cde6258a7 100644 --- a/transport-internal.h +++ b/transport-internal.h @@ -7,12 +7,6 @@ struct argv_array; struct transport_vtable { /** - * This transport supports the fetch() function being called - * without get_refs_list() first being called. - */ - unsigned fetch_without_list : 1; - - /** * Returns 0 if successful, positive if the option is not * recognized or is inapplicable, and negative if the option * is applicable but the value is invalid. diff --git a/transport.c b/transport.c index 778c60bf57..ae558af944 100644 --- a/transport.c +++ b/transport.c @@ -122,6 +122,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs, struct bundle_transport_data { int fd; struct bundle_header header; + unsigned get_refs_from_bundle_called : 1; }; static struct ref *get_refs_from_bundle(struct transport *transport, @@ -135,6 +136,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport, if (for_push) return NULL; + data->get_refs_from_bundle_called = 1; + if (data->fd > 0) close(data->fd); data->fd = read_bundle_header(transport->url, &data->header); @@ -154,6 +157,9 @@ static int fetch_refs_from_bundle(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct bundle_transport_data *data = transport->data; + + if (!data->get_refs_from_bundle_called) + get_refs_from_bundle(transport, 0, NULL); return unbundle(the_repository, &data->header, data->fd, transport->progress ? BUNDLE_VERBOSE : 0); } @@ -224,6 +230,7 @@ static int set_git_option(struct git_transport_options *opts, opts->no_dependents = !!value; return 0; } else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) { + list_objects_filter_die_if_populated(&opts->filter_options); parse_list_objects_filter(&opts->filter_options, value); return 0; } @@ -742,7 +749,6 @@ static int disconnect_git(struct transport *transport) } static struct transport_vtable taken_over_vtable = { - 1, NULL, get_refs_via_connect, fetch_refs_via_pack, @@ -892,7 +898,6 @@ void transport_check_allowed(const char *type) } static struct transport_vtable bundle_vtable = { - 0, NULL, get_refs_from_bundle, fetch_refs_from_bundle, @@ -902,7 +907,6 @@ static struct transport_vtable bundle_vtable = { }; static struct transport_vtable builtin_smart_vtable = { - 1, NULL, get_refs_via_connect, fetch_refs_via_pack, @@ -1285,15 +1289,6 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs) struct ref **heads = NULL; struct ref *rm; - if (!transport->vtable->fetch_without_list) - /* - * Some transports (e.g. the built-in bundle transport and the - * transport helper interface) do not work when fetching is - * done immediately after transport creation. List the remote - * refs anyway (if not already listed) as a workaround. - */ - transport_get_remote_refs(transport, NULL); - for (rm = refs; rm; rm = rm->next) { nr_refs++; if (rm->peer_ref && diff --git a/tree-walk.c b/tree-walk.c index c20b62f49e..bea819d826 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -170,40 +170,61 @@ int tree_entry_gently(struct tree_desc *desc, struct name_entry *entry) void setup_traverse_info(struct traverse_info *info, const char *base) { - int pathlen = strlen(base); + size_t pathlen = strlen(base); static struct traverse_info dummy; memset(info, 0, sizeof(*info)); if (pathlen && base[pathlen-1] == '/') pathlen--; info->pathlen = pathlen ? pathlen + 1 : 0; - info->name.path = base; - info->name.pathlen = pathlen; - if (pathlen) { - hashcpy(info->name.oid.hash, (const unsigned char *)base + pathlen + 1); + info->name = base; + info->namelen = pathlen; + if (pathlen) info->prev = &dummy; - } } -char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n) +char *make_traverse_path(char *path, size_t pathlen, + const struct traverse_info *info, + const char *name, size_t namelen) { - int len = tree_entry_len(n); - int pathlen = info->pathlen; + /* Always points to the end of the name we're about to add */ + size_t pos = st_add(info->pathlen, namelen); + + if (pos >= pathlen) + BUG("too small buffer passed to make_traverse_path"); - path[pathlen + len] = 0; + path[pos] = 0; for (;;) { - memcpy(path + pathlen, n->path, len); - if (!pathlen) + if (pos < namelen) + BUG("traverse_info pathlen does not match strings"); + pos -= namelen; + memcpy(path + pos, name, namelen); + + if (!pos) break; - path[--pathlen] = '/'; - n = &info->name; - len = tree_entry_len(n); + path[--pos] = '/'; + + if (!info) + BUG("traverse_info ran out of list items"); + name = info->name; + namelen = info->namelen; info = info->prev; - pathlen -= len; } return path; } +void strbuf_make_traverse_path(struct strbuf *out, + const struct traverse_info *info, + const char *name, size_t namelen) +{ + size_t len = traverse_path_len(info, namelen); + + strbuf_grow(out, len); + make_traverse_path(out->buf + out->len, out->alloc - out->len, + info, name, namelen); + strbuf_setlen(out, out->len + len); +} + struct tree_desc_skip { struct tree_desc_skip *prev; const void *ptr; @@ -400,13 +421,12 @@ int traverse_trees(struct index_state *istate, tx[i].d = t[i]; if (info->prev) { - strbuf_grow(&base, info->pathlen); - make_traverse_path(base.buf, info->prev, &info->name); - base.buf[info->pathlen-1] = '/'; - strbuf_setlen(&base, info->pathlen); - traverse_path = xstrndup(base.buf, info->pathlen); + strbuf_make_traverse_path(&base, info->prev, + info->name, info->namelen); + strbuf_addch(&base, '/'); + traverse_path = xstrndup(base.buf, base.len); } else { - traverse_path = xstrndup(info->name.path, info->pathlen); + traverse_path = xstrndup(info->name, info->pathlen); } info->traverse_path = traverse_path; for (;;) { diff --git a/tree-walk.h b/tree-walk.h index 2a5db29e8f..abe2caf4e0 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -58,8 +58,11 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, struct struct traverse_info { const char *traverse_path; struct traverse_info *prev; - struct name_entry name; - int pathlen; + const char *name; + size_t namelen; + unsigned mode; + + size_t pathlen; struct pathspec *pathspec; unsigned long df_conflicts; @@ -69,12 +72,17 @@ struct traverse_info { }; int get_tree_entry(struct repository *, const struct object_id *, const char *, struct object_id *, unsigned short *); -char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n); +char *make_traverse_path(char *path, size_t pathlen, const struct traverse_info *info, + const char *name, size_t namelen); +void strbuf_make_traverse_path(struct strbuf *out, + const struct traverse_info *info, + const char *name, size_t namelen); void setup_traverse_info(struct traverse_info *info, const char *base); -static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n) +static inline size_t traverse_path_len(const struct traverse_info *info, + size_t namelen) { - return info->pathlen + tree_entry_len(n); + return st_add(info->pathlen, namelen); } /* in general, positive means "kind of interesting" */ @@ -244,19 +244,7 @@ void free_tree_buffer(struct tree *tree) struct tree *parse_tree_indirect(const struct object_id *oid) { - struct object *obj = parse_object(the_repository, oid); - do { - if (!obj) - return NULL; - if (obj->type == OBJ_TREE) - return (struct tree *) obj; - else if (obj->type == OBJ_COMMIT) - obj = &(get_commit_tree(((struct commit *)obj))->object); - else if (obj->type == OBJ_TAG) - obj = ((struct tag *) obj)->tagged; - else - return NULL; - if (!obj->parsed) - parse_object(the_repository, &obj->oid); - } while (1); + struct repository *r = the_repository; + struct object *obj = parse_object(r, oid); + return (struct tree *)repo_peel_to_type(r, NULL, 0, obj, OBJ_TREE); } diff --git a/unpack-trees.c b/unpack-trees.c index 62276d4fef..f0f56d40ac 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -16,7 +16,7 @@ #include "submodule-config.h" #include "fsmonitor.h" #include "object-store.h" -#include "fetch-object.h" +#include "promisor-remote.h" /* * Error messages expected by scripts out of plumbing commands such as @@ -400,7 +400,7 @@ static int check_updates(struct unpack_trees_options *o) load_gitmodules_file(index, &state); enable_delayed_checkout(&state); - if (repository_format_partial_clone && o->update && !o->dry_run) { + if (has_promisor_remote() && o->update && !o->dry_run) { /* * Prefetch the objects that are to be checked out in the loop * below. @@ -419,8 +419,8 @@ static int check_updates(struct unpack_trees_options *o) oid_array_append(&to_fetch, &ce->oid); } if (to_fetch.nr) - fetch_objects(repository_format_partial_clone, - to_fetch.oid, to_fetch.nr); + promisor_remote_get_direct(the_repository, + to_fetch.oid, to_fetch.nr); oid_array_clear(&to_fetch); } for (i = 0; i < index->cache_nr; i++) { @@ -632,7 +632,7 @@ static int unpack_index_entry(struct cache_entry *ce, return ret; } -static int find_cache_pos(struct traverse_info *, const struct name_entry *); +static int find_cache_pos(struct traverse_info *, const char *p, size_t len); static void restore_cache_bottom(struct traverse_info *info, int bottom) { @@ -651,7 +651,7 @@ static int switch_cache_bottom(struct traverse_info *info) if (o->diff_index_cached) return 0; ret = o->cache_bottom; - pos = find_cache_pos(info->prev, &info->name); + pos = find_cache_pos(info->prev, info->name, info->namelen); if (pos < -1) o->cache_bottom = -2 - pos; @@ -686,21 +686,19 @@ static int index_pos_by_traverse_info(struct name_entry *names, struct traverse_info *info) { struct unpack_trees_options *o = info->data; - int len = traverse_path_len(info, names); - char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */); + struct strbuf name = STRBUF_INIT; int pos; - make_traverse_path(name, info, names); - name[len++] = '/'; - name[len] = '\0'; - pos = index_name_pos(o->src_index, name, len); + strbuf_make_traverse_path(&name, info, names->path, names->pathlen); + strbuf_addch(&name, '/'); + pos = index_name_pos(o->src_index, name.buf, name.len); if (pos >= 0) BUG("This is a directory and should not exist in index"); pos = -pos - 1; - if (!starts_with(o->src_index->cache[pos]->name, name) || - (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name))) + if (!starts_with(o->src_index->cache[pos]->name, name.buf) || + (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf))) BUG("pos must point at the first entry in this directory"); - free(name); + strbuf_release(&name); return pos; } @@ -811,8 +809,10 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, newinfo = *info; newinfo.prev = info; newinfo.pathspec = info->pathspec; - newinfo.name = *p; - newinfo.pathlen += tree_entry_len(p) + 1; + newinfo.name = p->path; + newinfo.namelen = p->pathlen; + newinfo.mode = p->mode; + newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1); newinfo.df_conflicts |= df_conflicts; /* @@ -863,14 +863,18 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, * itself - the caller needs to do the final check for the cache * entry having more data at the end! */ -static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) +static int do_compare_entry_piecewise(const struct cache_entry *ce, + const struct traverse_info *info, + const char *name, size_t namelen, + unsigned mode) { - int len, pathlen, ce_len; + int pathlen, ce_len; const char *ce_name; if (info->prev) { int cmp = do_compare_entry_piecewise(ce, info->prev, - &info->name); + info->name, info->namelen, + info->mode); if (cmp) return cmp; } @@ -884,15 +888,15 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct ce_len -= pathlen; ce_name = ce->name + pathlen; - len = tree_entry_len(n); - return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); + return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode); } static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, - const struct name_entry *n) + const char *name, size_t namelen, + unsigned mode) { - int len, pathlen, ce_len; + int pathlen, ce_len; const char *ce_name; int cmp; @@ -902,7 +906,7 @@ static int do_compare_entry(const struct cache_entry *ce, * it is quicker to use the precomputed version. */ if (!info->traverse_path) - return do_compare_entry_piecewise(ce, info, n); + return do_compare_entry_piecewise(ce, info, name, namelen, mode); cmp = strncmp(ce->name, info->traverse_path, info->pathlen); if (cmp) @@ -917,13 +921,12 @@ static int do_compare_entry(const struct cache_entry *ce, ce_len -= pathlen; ce_name = ce->name + pathlen; - len = tree_entry_len(n); - return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode); + return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode); } static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n) { - int cmp = do_compare_entry(ce, info, n); + int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode); if (cmp) return cmp; @@ -931,7 +934,7 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf * Even if the beginning compared identically, the ce should * compare as bigger than a directory leading up to it! */ - return ce_namelen(ce) > traverse_path_len(info, n); + return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n)); } static int ce_in_traverse_path(const struct cache_entry *ce, @@ -939,7 +942,8 @@ static int ce_in_traverse_path(const struct cache_entry *ce, { if (!info->prev) return 1; - if (do_compare_entry(ce, info->prev, &info->name)) + if (do_compare_entry(ce, info->prev, + info->name, info->namelen, info->mode)) return 0; /* * If ce (blob) is the same name as the path (which is a tree @@ -954,7 +958,7 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, struct index_state *istate, int is_transient) { - int len = traverse_path_len(info, n); + size_t len = traverse_path_len(info, tree_entry_len(n)); struct cache_entry *ce = is_transient ? make_empty_transient_cache_entry(len) : @@ -964,7 +968,8 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = len; oidcpy(&ce->oid, &n->oid); - make_traverse_path(ce->name, info, n); + /* len+1 because the cache_entry allocates space for NUL */ + make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen); return ce; } @@ -1057,13 +1062,12 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message) * the directory. */ static int find_cache_pos(struct traverse_info *info, - const struct name_entry *p) + const char *p, size_t p_len) { int pos; struct unpack_trees_options *o = info->data; struct index_state *index = o->src_index; int pfxlen = info->pathlen; - int p_len = tree_entry_len(p); for (pos = o->cache_bottom; pos < index->cache_nr; pos++) { const struct cache_entry *ce = index->cache[pos]; @@ -1099,7 +1103,7 @@ static int find_cache_pos(struct traverse_info *info, ce_len = ce_slash - ce_name; else ce_len = ce_namelen(ce) - pfxlen; - cmp = name_compare(p->path, p_len, ce_name, ce_len); + cmp = name_compare(p, p_len, ce_name, ce_len); /* * Exact match; if we have a directory we need to * delay returning it. @@ -1114,7 +1118,7 @@ static int find_cache_pos(struct traverse_info *info, * E.g. ce_name == "t-i", and p->path == "t"; we may * have "t/a" in the index. */ - if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) && + if (p_len < ce_len && !memcmp(ce_name, p, p_len) && ce_name[p_len] < '/') continue; /* keep looking */ break; @@ -1125,7 +1129,7 @@ static int find_cache_pos(struct traverse_info *info, static struct cache_entry *find_cache_entry(struct traverse_info *info, const struct name_entry *p) { - int pos = find_cache_pos(info, p); + int pos = find_cache_pos(info, p->path, p->pathlen); struct unpack_trees_options *o = info->data; if (0 <= pos) @@ -1138,10 +1142,10 @@ static void debug_path(struct traverse_info *info) { if (info->prev) { debug_path(info->prev); - if (*info->prev->name.path) + if (*info->prev->name) putchar('/'); } - printf("%s", info->name.path); + printf("%s", info->name); } static void debug_name_entry(int i, struct name_entry *n) @@ -1265,7 +1269,8 @@ static int clear_ce_flags_1(struct index_state *istate, struct cache_entry **cache, int nr, struct strbuf *prefix, int select_mask, int clear_mask, - struct exclude_list *el, int defval); + struct pattern_list *pl, + enum pattern_match_result default_match); /* Whole directory matching */ static int clear_ce_flags_dir(struct index_state *istate, @@ -1273,19 +1278,21 @@ static int clear_ce_flags_dir(struct index_state *istate, struct strbuf *prefix, char *basename, int select_mask, int clear_mask, - struct exclude_list *el, int defval) + struct pattern_list *pl, + enum pattern_match_result default_match) { struct cache_entry **cache_end; int dtype = DT_DIR; - int ret = is_excluded_from_list(prefix->buf, prefix->len, - basename, &dtype, el, istate); int rc; + enum pattern_match_result ret; + ret = path_matches_pattern_list(prefix->buf, prefix->len, + basename, &dtype, pl, istate); strbuf_addch(prefix, '/'); /* If undecided, use matching result of parent dir in defval */ - if (ret < 0) - ret = defval; + if (ret == UNDECIDED) + ret = default_match; for (cache_end = cache; cache_end != cache + nr; cache_end++) { struct cache_entry *ce = *cache_end; @@ -1294,23 +1301,23 @@ static int clear_ce_flags_dir(struct index_state *istate, } /* - * TODO: check el, if there are no patterns that may conflict + * TODO: check pl, if there are no patterns that may conflict * with ret (iow, we know in advance the incl/excl * decision for the entire directory), clear flag here without * calling clear_ce_flags_1(). That function will call - * the expensive is_excluded_from_list() on every entry. + * the expensive path_matches_pattern_list() on every entry. */ rc = clear_ce_flags_1(istate, cache, cache_end - cache, prefix, select_mask, clear_mask, - el, ret); + pl, ret); strbuf_setlen(prefix, prefix->len - 1); return rc; } /* * Traverse the index, find every entry that matches according to - * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the + * o->pl. Do "ce_flags &= ~clear_mask" on those entries. Return the * number of traversed entries. * * If select_mask is non-zero, only entries whose ce_flags has on of @@ -1327,7 +1334,8 @@ static int clear_ce_flags_1(struct index_state *istate, struct cache_entry **cache, int nr, struct strbuf *prefix, int select_mask, int clear_mask, - struct exclude_list *el, int defval) + struct pattern_list *pl, + enum pattern_match_result default_match) { struct cache_entry **cache_end = cache + nr; @@ -1338,7 +1346,8 @@ static int clear_ce_flags_1(struct index_state *istate, while(cache != cache_end) { struct cache_entry *ce = *cache; const char *name, *slash; - int len, dtype, ret; + int len, dtype; + enum pattern_match_result ret; if (select_mask && !(ce->ce_flags & select_mask)) { cache++; @@ -1362,7 +1371,7 @@ static int clear_ce_flags_1(struct index_state *istate, prefix, prefix->buf + prefix->len - len, select_mask, clear_mask, - el, defval); + pl, default_match); /* clear_c_f_dir eats a whole dir already? */ if (processed) { @@ -1374,18 +1383,20 @@ static int clear_ce_flags_1(struct index_state *istate, strbuf_addch(prefix, '/'); cache += clear_ce_flags_1(istate, cache, cache_end - cache, prefix, - select_mask, clear_mask, el, defval); + select_mask, clear_mask, pl, + default_match); strbuf_setlen(prefix, prefix->len - len - 1); continue; } /* Non-directory */ dtype = ce_to_dtype(ce); - ret = is_excluded_from_list(ce->name, ce_namelen(ce), - name, &dtype, el, istate); - if (ret < 0) - ret = defval; - if (ret > 0) + ret = path_matches_pattern_list(ce->name, + ce_namelen(ce), + name, &dtype, pl, istate); + if (ret == UNDECIDED) + ret = default_match; + if (ret == MATCHED) ce->ce_flags &= ~clear_mask; cache++; } @@ -1394,7 +1405,7 @@ static int clear_ce_flags_1(struct index_state *istate, static int clear_ce_flags(struct index_state *istate, int select_mask, int clear_mask, - struct exclude_list *el) + struct pattern_list *pl) { static struct strbuf prefix = STRBUF_INIT; @@ -1405,13 +1416,13 @@ static int clear_ce_flags(struct index_state *istate, istate->cache_nr, &prefix, select_mask, clear_mask, - el, 0); + pl, 0); } /* * Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout */ -static void mark_new_skip_worktree(struct exclude_list *el, +static void mark_new_skip_worktree(struct pattern_list *pl, struct index_state *istate, int select_flag, int skip_wt_flag) { @@ -1437,7 +1448,7 @@ static void mark_new_skip_worktree(struct exclude_list *el, * 2. Widen worktree according to sparse-checkout file. * Matched entries will have skip_wt_flag cleared (i.e. "in") */ - clear_ce_flags(istate, select_flag, skip_wt_flag, el); + clear_ce_flags(istate, select_flag, skip_wt_flag, pl); } static int verify_absent(const struct cache_entry *, @@ -1453,21 +1464,21 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options { int i, ret; static struct cache_entry *dfc; - struct exclude_list el; + struct pattern_list pl; if (len > MAX_UNPACK_TREES) die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES); trace_performance_enter(); - memset(&el, 0, sizeof(el)); + memset(&pl, 0, sizeof(pl)); if (!core_apply_sparse_checkout || !o->update) o->skip_sparse_checkout = 1; if (!o->skip_sparse_checkout) { char *sparse = git_pathdup("info/sparse-checkout"); - if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0) + if (add_patterns_from_file_to_list(sparse, "", 0, &pl, NULL) < 0) o->skip_sparse_checkout = 1; else - o->el = ⪙ + o->pl = &pl; free(sparse); } @@ -1498,7 +1509,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries */ if (!o->skip_sparse_checkout) - mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE); + mark_new_skip_worktree(o->pl, o->src_index, 0, CE_NEW_SKIP_WORKTREE); if (!dfc) dfc = xcalloc(1, cache_entry_size(0)); @@ -1563,7 +1574,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE * so apply_sparse_checkout() won't attempt to remove it from worktree */ - mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE); + mark_new_skip_worktree(o->pl, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE); ret = 0; for (i = 0; i < o->result.cache_nr; i++) { @@ -1631,7 +1642,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options done: trace_performance_leave("unpack_trees"); - clear_exclude_list(&el); + clear_pattern_list(&pl); return ret; return_failed: diff --git a/unpack-trees.h b/unpack-trees.h index d344d7d296..f2eee0c7c5 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -10,7 +10,7 @@ struct cache_entry; struct unpack_trees_options; -struct exclude_list; +struct pattern_list; typedef int (*merge_fn_t)(const struct cache_entry * const *src, struct unpack_trees_options *options); @@ -83,7 +83,7 @@ struct unpack_trees_options { struct index_state *src_index; struct index_state result; - struct exclude_list *el; /* for internal use */ + struct pattern_list *pl; /* for internal use */ }; int unpack_trees(unsigned n, struct tree_desc *t, diff --git a/upload-pack.c b/upload-pack.c index 222cd3ad89..875db92996 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -140,18 +140,17 @@ static void create_pack_file(const struct object_array *have_obj, argv_array_push(&pack_objects.args, "--delta-base-offset"); if (use_include_tag) argv_array_push(&pack_objects.args, "--include-tag"); - if (filter_options.filter_spec) { - struct strbuf expanded_filter_spec = STRBUF_INIT; - expand_list_objects_filter_spec(&filter_options, - &expanded_filter_spec); + if (filter_options.choice) { + const char *spec = + expand_list_objects_filter_spec(&filter_options); if (pack_objects.use_shell) { struct strbuf buf = STRBUF_INIT; - sq_quote_buf(&buf, expanded_filter_spec.buf); + sq_quote_buf(&buf, spec); argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf); strbuf_release(&buf); } else { argv_array_pushf(&pack_objects.args, "--filter=%s", - expanded_filter_spec.buf); + spec); } } @@ -884,6 +883,7 @@ static void receive_needs(struct packet_reader *reader, struct object_array *wan if (skip_prefix(reader->line, "filter ", &arg)) { if (!filter_capability_requested) die("git upload-pack: filtering capability not negotiated"); + list_objects_filter_die_if_populated(&filter_options); parse_list_objects_filter(&filter_options, arg); continue; } @@ -1305,6 +1305,7 @@ static void process_args(struct packet_reader *request, } if (allow_filter && skip_prefix(arg, "filter ", &p)) { + list_objects_filter_die_if_populated(&filter_options); parse_list_objects_filter(&filter_options, p); continue; } @@ -86,6 +86,12 @@ char *url_decode_mem(const char *url, int len) return url_decode_internal(&url, len, NULL, &out, 0); } +char *url_percent_decode(const char *encoded) +{ + struct strbuf out = STRBUF_INIT; + return url_decode_internal(&encoded, strlen(encoded), NULL, &out, 0); +} + char *url_decode_parameter_name(const char **query) { struct strbuf out = STRBUF_INIT; @@ -7,6 +7,14 @@ int is_url(const char *url); int is_urlschemechar(int first_flag, int ch); char *url_decode(const char *url); char *url_decode_mem(const char *url, int len); + +/* + * Similar to the url_decode_{,mem} methods above, but doesn't assume there + * is a scheme followed by a : at the start of the string. Instead, %-sequences + * before any : are also parsed. + */ +char *url_percent_decode(const char *encoded); + char *url_decode_parameter_name(const char **query); char *url_decode_parameter_value(const char **query); diff --git a/userdiff.c b/userdiff.c index e74a6d4022..86e3244e15 100644 --- a/userdiff.c +++ b/userdiff.c @@ -23,6 +23,14 @@ IPATTERN("ada", "[a-zA-Z][a-zA-Z0-9_]*" "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?" "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"), +PATTERNS("dts", + "!;\n" + /* lines beginning with a word optionally preceded by '&' or the root */ + "^[ \t]*((/|&?[a-zA-Z_]).*)", + /* -- */ + /* Property names and math operators */ + "[a-zA-Z0-9,._+?#-]+" + "|[-+*/%&^|!~]|>>|<<|&&|\\|\\|"), IPATTERN("fortran", "!^([C*]|[ \t]*!)\n" "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n" @@ -4,12 +4,6 @@ #include "cache.h" #include "config.h" -static void do_nothing(size_t size) -{ -} - -static void (*try_to_free_routine)(size_t size) = do_nothing; - static int memory_limit_check(size_t size, int gentle) { static size_t limit = 0; @@ -30,24 +24,11 @@ static int memory_limit_check(size_t size, int gentle) return 0; } -try_to_free_t set_try_to_free_routine(try_to_free_t routine) -{ - try_to_free_t old = try_to_free_routine; - if (!routine) - routine = do_nothing; - try_to_free_routine = routine; - return old; -} - char *xstrdup(const char *str) { char *ret = strdup(str); - if (!ret) { - try_to_free_routine(strlen(str) + 1); - ret = strdup(str); - if (!ret) - die("Out of memory, strdup failed"); - } + if (!ret) + die("Out of memory, strdup failed"); return ret; } @@ -61,19 +42,13 @@ static void *do_xmalloc(size_t size, int gentle) if (!ret && !size) ret = malloc(1); if (!ret) { - try_to_free_routine(size); - ret = malloc(size); - if (!ret && !size) - ret = malloc(1); - if (!ret) { - if (!gentle) - die("Out of memory, malloc failed (tried to allocate %lu bytes)", - (unsigned long)size); - else { - error("Out of memory, malloc failed (tried to allocate %lu bytes)", - (unsigned long)size); - return NULL; - } + if (!gentle) + die("Out of memory, malloc failed (tried to allocate %lu bytes)", + (unsigned long)size); + else { + error("Out of memory, malloc failed (tried to allocate %lu bytes)", + (unsigned long)size); + return NULL; } } #ifdef XMALLOC_POISON @@ -138,14 +113,8 @@ void *xrealloc(void *ptr, size_t size) ret = realloc(ptr, size); if (!ret && !size) ret = realloc(ptr, 1); - if (!ret) { - try_to_free_routine(size); - ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) - die("Out of memory, realloc failed"); - } + if (!ret) + die("Out of memory, realloc failed"); return ret; } @@ -160,14 +129,8 @@ void *xcalloc(size_t nmemb, size_t size) ret = calloc(nmemb, size); if (!ret && (!nmemb || !size)) ret = calloc(1, 1); - if (!ret) { - try_to_free_routine(nmemb * size); - ret = calloc(nmemb, size); - if (!ret && (!nmemb || !size)) - ret = calloc(1, 1); - if (!ret) - die("Out of memory, calloc failed"); - } + if (!ret) + die("Out of memory, calloc failed"); return ret; } diff --git a/wt-status.c b/wt-status.c index 9f6c65a580..ad6282c7f8 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1434,6 +1434,8 @@ static void show_cherry_pick_in_progress(struct wt_status *s, status_printf_ln(s, color, _(" (all conflicts fixed: run \"git cherry-pick --continue\")")); status_printf_ln(s, color, + _(" (use \"git cherry-pick --skip\" to skip this patch)")); + status_printf_ln(s, color, _(" (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)")); } wt_longstatus_print_trailer(s); @@ -1461,6 +1463,8 @@ static void show_revert_in_progress(struct wt_status *s, status_printf_ln(s, color, _(" (all conflicts fixed: run \"git revert --continue\")")); status_printf_ln(s, color, + _(" (use \"git revert --skip\" to skip this patch)")); + status_printf_ln(s, color, _(" (use \"git revert --abort\" to cancel the revert operation)")); } wt_longstatus_print_trailer(s); |