diff options
227 files changed, 7953 insertions, 2661 deletions
diff --git a/.gitignore b/.gitignore index 6b1fd1bfb0..66199edd4a 100644 --- a/.gitignore +++ b/.gitignore @@ -202,6 +202,7 @@ /test-string-list /test-subprocess /test-svn-fe +/test-urlmatch-normalization /test-wildmatch /common-cmds.h *.tar.gz diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 559d5f9ebf..e5ca3b75d3 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -242,6 +242,14 @@ Writing Documentation: processed into HTML and manpages (e.g. git.html and git.1 in the same directory). + The documentation liberally mixes US and UK English (en_US/UK) + norms for spelling and grammar, which is somewhat unfortunate. + In an ideal world, it would have been better if it consistently + used only one and not the other, and we would have picked en_US + (if you wish to correct the English of some of the existing + documentation, please see the documentation-related advice in the + Documentation/SubmittingPatches file). + Every user-visible change should be reflected in the documentation. The same general rule as for code applies -- imitate the existing conventions. A few commented examples follow to provide reference diff --git a/Documentation/RelNotes/1.8.5.txt b/Documentation/RelNotes/1.8.5.txt new file mode 100644 index 0000000000..a142eee0c3 --- /dev/null +++ b/Documentation/RelNotes/1.8.5.txt @@ -0,0 +1,221 @@ +Git v1.8.5 Release Notes +======================== + +Backward compatibility notes (for Git 2.0) +------------------------------------------ + +When "git push [$there]" does not say what to push, we have used the +traditional "matching" semantics so far (all your branches were sent +to the remote as long as there already are branches of the same name +over there). In Git 2.0, the default will change to the "simple" +semantics that pushes: + + - only the current branch to the branch with the same name, and only + when the current branch is set to integrate with that remote + branch, if you are pushing to the same remote as you fetch from; or + + - only the current branch to the branch with the same name, if you + are pushing to a remote that is not where you usually fetch from. + +Use the user preference configuration variable "push.default" to +change this. If you are an old-timer who is used to the "matching" +semantics, you can set the variable to "matching" to keep the +traditional behaviour. If you want to live in the future early, you +can set it to "simple" today without waiting for Git 2.0. + +When "git add -u" (and "git add -A") is run inside a subdirectory and +does not specify which paths to add on the command line, it +will operate on the entire tree in Git 2.0 for consistency +with "git commit -a" and other commands. There will be no +mechanism to make plain "git add -u" behave like "git add -u .". +Current users of "git add -u" (without a pathspec) should start +training their fingers to explicitly say "git add -u ." +before Git 2.0 comes. A warning is issued when these commands are +run without a pathspec and when you have local changes outside the +current directory, because the behaviour in Git 2.0 will be different +from today's version in such a situation. + +In Git 2.0, "git add <path>" will behave as "git add -A <path>", so +that "git add dir/" will notice paths you removed from the directory +and record the removal. Versions before Git 2.0, including this +release, will keep ignoring removals, but the users who rely on this +behaviour are encouraged to start using "git add --ignore-removal <path>" +now before 2.0 is released. + + +Updates since v1.8.4 +-------------------- + +Foreign interfaces, subsystems and ports. + + * remote-hg remote helper misbehaved when interacting with a local Hg + repository relative to the home directory, e.g. "clone hg::~/there". + + * imap-send ported to OS X uses Apple's security framework instead of + OpenSSL one. + + * Subversion 1.8.0 that was recently released breaks older subversion + clients coming over http/https in various ways. + + * "git fast-import" treats an empty path given to "ls" as the root of + the tree. + + +UI, Workflows & Features + + * "git pull --rebase" always chose to do the bog-standard flattening + rebase. You can tell it to run "rebase --preserve-merges" by + setting "pull.rebase" configuration to "preserve". + + * "git push --no-thin" actually disables the "thin pack transfer" + optimization. + + * Magic pathspecs like ":(icase)makefile" that matches both + Makefile and makefile can be used in more places. + + * The "http.*" variables can now be specified per URL that the + configuration applies. For example, + + [http] + sslVerify = true + [http "https://weak.example.com/"] + sslVerify = false + + would flip http.sslVerify off only when talking to that specified + site. + + * "git mv A B" when moving a submodule A has been taught to + relocate its working tree and to adjust the paths in the + .gitmodules file. + + * "git blame" can now take more than one -L option to discover the + origin of multiple blocks of the lines. + + * The http transport clients can optionally ask to save cookies + with http.savecookies configuration variable. + + * "git push" learned a more fine grained control over a blunt + "--force" when requesting a non-fast-forward update with the + "--force-with-lease=<refname>:<expected object name>" option. + + * "git diff --diff-filter=<classes of changes>" can now take + lowercase letters (e.g. "--diff-filter=d") to mean "show + everything but these classes". "git diff-files -q" is now a + deprecated synonym for "git diff-files --diff-filter=d". + + * "git fetch" (hence "git pull" as well) learned to check + "fetch.prune" and "remote.*.prune" configuration variables and + to behave as if the "--prune" command line option was given. + + * "git check-ignore -z" applied the NUL termination to both its input + (with --stdin) and its output, but "git check-attr -z" ignored the + option on the output side. Make both honor -z on the input and + output side the same way. + + * "git whatchanged" may still be used by old timers, but mention of + it in documents meant for new users will only waste readers' time + wonderig what the difference is between it and "git log". Make it + less prominent in the general part of the documentation and explain + that it is merely a "git log" with different default behaviour in + its own document. + + +Performance, Internal Implementation, etc. + + * Many commands use --dashed-option as a operation mode selector + (e.g. "git tag --delete") that the user can use at most one + (e.g. "git tag --delete --verify" is a nonsense) and you cannot + negate (e.g. "git tag --no-delete" is a nonsense). parse-options + API learned a new OPT_CMDMODE macro to make it easier to implement + such a set of options. + + * OPT_BOOLEAN() in parse-options API was misdesigned to be "counting + up" but many subcommands expect it to behave as "on/off". Update + them to use OPT_BOOL() which is a proper boolean. + + * "git gc" exits early without doing a double-work when it detects + that another instance of itself is already running. + + * Under memory pressure and/or file descriptor pressure, we used to + close pack windows that are not used and also closed filehandle to + an open but unused packfiles. These are now controlled separately + to better cope with the load. + +Also contains various documentation updates and code clean-ups. + + +Fixes since v1.8.4 +------------------ + +Unless otherwise noted, all the fixes since v1.8.4 in the maintenance +track are contained in this release (see release notes to them for +details). + + * "git ls-files -k" needs to crawl only the part of the working tree + that may overlap the paths in the index to find killed files, but + shared code with the logic to find all the untracked files, which + made it unnecessarily inefficient. + (merge 680be04 jc/ls-files-killed-optim later to maint). + + * The commit object names in the insn sheet that was prepared at the + beginning of "rebase -i" session can become ambiguous as the + rebasing progresses and the repository gains more commits. Make + sure the internal record is kept with full 40-hex object names. + (merge 75c6976 es/rebase-i-no-abbrev later to maint). + + * "git rebase --preserve-merges" internally used the merge machinery + and as a side effect, left merge summary message in the log, but + when rebasing, there should not be a need for merge summary. + (merge a9f739c rt/rebase-p-no-merge-summary later to maint). + + * A call to xread() was used without a loop around to cope with short + read in the codepath to stream new contents to a pack. + (merge e92527c js/xread-in-full later to maint). + + * "git rebase -i" forgot that the comment character can be + configurable while reading its insn sheet. + (merge 7bca7af es/rebase-i-respect-core-commentchar later to maint). + + * The mailmap support code read past the allocated buffer when the + mailmap file ended with an incomplete line. + (merge f972a16 jk/mailmap-incomplete-line later to maint). + + * We used to send a large request to read(2)/write(2) as a single + system call, which was bad from the latency point of view when + the operation needs to be killed, and also triggered an error on + broken 64-bit systems that refuse to take more than 2GB read or + write in one go. + (merge a487916 sp/clip-read-write-to-8mb later to maint). + + * "git fetch" that auto-followed tags incorrectly reused the + connection with Git-aware transport helper (like the sample "ext::" + helper shipped with Git). + (merge 0f73f8b jc/transport-do-not-use-connect-twice-in-fetch later to maint). + + * "git log --full-diff -- <pathspec>" showed a huge diff for paths + outside the given <pathspec> for each commit, instead of showing + the change relative to the parent of the commit. "git reflog -p" + had a similar problem. + (merge 838f9a1 tr/log-full-diff-keep-true-parents later to maint). + + * Setting submodule.*.path configuration variable to true (without + giving "= value") caused Git to segfault. + (merge 4b05440 jl/some-submodule-config-are-not-boolean later to maint). + + * "git rebase -i" (there could be others, as the root cause is pretty + generic) fed a random, data dependeant string to 'echo' and + expects it to come out literally, corrupting its error message. + (merge 89b0230 mm/no-shell-escape-in-die-message later to maint). + + * Some people still use rather old versions of bash, which cannot + grok some constructs like 'printf -v varname' the prompt and + completion code started to use recently. + (merge a44aa69 bc/completion-for-bash-3.0 later to maint). + + * Code to read configuration from a blob object did not compile on + platforms with fgetc() etc. implemented as macros. + (merge 49d6cfa hv/config-from-blob later to maint-1.8.3). + + * The recent "short-cut clone connectivity check" topic broke a + shallow repository when a fetch operation tries to auto-follow tags. + (merge 6da8bdc nd/fetch-pack-shallow-fix later to maint-1.8.3). diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index d0a4733e45..705557689d 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -65,7 +65,20 @@ feature does not trigger when it shouldn't. Also make sure that the test suite passes after your commit. Do not forget to update the documentation to describe the updated behaviour. -Oh, another thing. I am picky about whitespaces. Make sure your +Speaking of the documentation, it is currently a liberal mixture of US +and UK English norms for spelling and grammar, which is somewhat +unfortunate. A huge patch that touches the files all over the place +only to correct the inconsistency is not welcome, though. Potential +clashes with other changes that can result from such a patch are not +worth it. We prefer to gradually reconcile the inconsistencies in +favor of US English, with small and easily digestible patches, as a +side effect of doing some other real work in the vicinity (e.g. +rewriting a paragraph for clarity, while turning en_UK spelling to +en_US). Obvious typographical fixes are much more welcomed ("teh -> +"the"), preferably submitted as independent patches separate from +other documentation changes. + +Oh, another thing. We are picky about whitespaces. Make sure your changes do not trigger errors with the sample pre-commit hook shipped in templates/hooks--pre-commit. To help ensure this does not happen, run git diff --check on your changes before you commit. diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index 4e55b1564e..0cebc4f692 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -11,12 +11,12 @@ -L <start>,<end>:: -L :<regex>:: - Annotate only the given line range. <start> and <end> are optional. - ``-L <start>'' or ``-L <start>,'' spans from <start> to end of file. - ``-L ,<end>'' spans from start of file to <end>. + Annotate only the given line range. May be specified multiple times. + Overlapping ranges are allowed. ++ +<start> and <end> are optional. ``-L <start>'' or ``-L <start>,'' spans from +<start> to end of file. ``-L ,<end>'' spans from start of file to <end>. + -<start> and <end> can take one of these forms: - include::line-range-format.txt[] -l:: diff --git a/Documentation/config.txt b/Documentation/config.txt index ec57a15ac5..9d101a9032 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -766,6 +766,10 @@ branch.<name>.rebase:: "git pull" is run. See "pull.rebase" for doing this in a non branch-specific manner. + + When preserve, also pass `--preserve-merges` along to 'git rebase' + so that locally committed merge commits will not be flattened + by running 'git pull'. ++ *NOTE*: this is a possibly dangerous operation; do *not* use it unless you understand the implications (see linkgit:git-rebase[1] for details). @@ -1061,6 +1065,10 @@ fetch.unpackLimit:: especially on slow filesystems. If not set, the value of `transfer.unpackLimit` is used instead. +fetch.prune:: + If true, fetch will automatically behave as if the `--prune` + option was given on the command line. See also `remote.<name>.prune`. + format.attach:: Enable multipart/mixed attachments as the default for 'format-patch'. The value can also be a double quoted string @@ -1445,7 +1453,11 @@ http.cookiefile:: of the file to read cookies from should be plain HTTP headers or the Netscape/Mozilla cookie file format (see linkgit:curl[1]). NOTE that the file specified with http.cookiefile is only used as - input. No cookies will be stored in the file. + input unless http.saveCookies is set. + +http.savecookies:: + If set, store cookies received during requests to the file specified by + http.cookiefile. Has no effect if http.cookiefile is unset. http.sslVerify:: Whether to verify the SSL certificate when fetching or pushing @@ -1525,6 +1537,51 @@ http.useragent:: of common USER_AGENT strings (but not including those like git/1.7.1). Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable. +http.<url>.*:: + Any of the http.* options above can be applied selectively to some urls. + For a config key to match a URL, each element of the config key is + compared to that of the URL, in the following order: ++ +-- +. Scheme (e.g., `https` in `https://example.com/`). This field + must match exactly between the config key and the URL. + +. Host/domain name (e.g., `example.com` in `https://example.com/`). + This field must match exactly between the config key and the URL. + +. Port number (e.g., `8080` in `http://example.com:8080/`). + This field must match exactly between the config key and the URL. + Omitted port numbers are automatically converted to the correct + default for the scheme before matching. + +. Path (e.g., `repo.git` in `https://example.com/repo.git`). The + path field of the config key must match the path field of the URL + either exactly or as a prefix of slash-delimited path elements. This means + a config key with path `foo/` matches URL path `foo/bar`. A prefix can only + match on a slash (`/`) boundary. Longer matches take precedence (so a config + key with path `foo/bar` is a better match to URL path `foo/bar` than a config + key with just path `foo/`). + +. User name (e.g., `user` in `https://user@example.com/repo.git`). If + the config key has a user name it must match the user name in the + URL exactly. If the config key does not have a user name, that + config key will match a URL with any user name (including none), + but at a lower precedence than a config key with a user name. +-- ++ +The list above is ordered by decreasing precedence; a URL that matches +a config key's path is preferred to one that matches its user name. For example, +if the URL is `https://user@example.com/foo/bar` a config key match of +`https://example.com/foo` will be preferred over a config key match of +`https://user@example.com`. ++ +All URLs are normalized before attempting any matching (the password part, +if embedded in the URL, is always ignored for matching purposes) so that +equivalent urls that are simply spelled differently will match properly. +Environment variable settings always override any matches. The urls that are +matched against are those given directly to Git commands. This means any URLs +visited as a result of a redirection do not participate in matching. + i18n.commitEncoding:: Character encoding the commit messages are stored in; Git itself does not care per se, but this information is necessary e.g. when @@ -1826,6 +1883,10 @@ pull.rebase:: pull" is run. See "branch.<name>.rebase" for setting this on a per-branch basis. + + When preserve, also pass `--preserve-merges` along to 'git rebase' + so that locally committed merge commits will not be flattened + by running 'git pull'. ++ *NOTE*: this is a possibly dangerous operation; do *not* use it unless you understand the implications (see linkgit:git-rebase[1] for details). @@ -2024,6 +2085,12 @@ remote.<name>.vcs:: Setting this to a value <vcs> will cause Git to interact with the remote with the git-remote-<vcs> helper. +remote.<name>.prune:: + When set to true, fetching from this remote by default will also + remove any remote-tracking branches which no longer exist on the + remote (as if the `--prune` option was give on the command line). + Overrides `fetch.prune` settings, if any. + remotes.<group>:: The list of remotes which are fetched by "git remote update <group>". See linkgit:git-remote[1]. diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt index 6cea7f1ce1..f2c85cc633 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] - [-L n,m | -L :fn] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] + [-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--] <file> DESCRIPTION @@ -18,7 +18,8 @@ DESCRIPTION Annotates each line in the given file with information from the revision which last modified the line. Optionally, start annotating from the given revision. -The command can also limit the range of lines annotated. +When specified one or more times, `-L` restricts annotation to the requested +lines. The origin of lines is automatically followed across whole-file renames (currently there is no option to turn the rename-following @@ -130,7 +131,10 @@ SPECIFYING RANGES Unlike 'git blame' and 'git annotate' in older versions of git, the extent of the annotation can be limited to both line ranges and revision -ranges. When you are interested in finding the origin for +ranges. The `-L` option, which limits annotation to a range of lines, may be +specified multiple times. + +When you are interested in finding the origin for lines 40-60 for file `foo`, you can use the `-L` option like so (they mean the same thing -- both ask for 21 lines starting at line 40): diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 10fbc6a373..21cffe2bcd 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -86,10 +86,9 @@ BATCH OUTPUT ------------ If `--batch` or `--batch-check` is given, `cat-file` will read objects -from stdin, one per line, and print information about them. - -Each line is considered as a whole object name, and is parsed as if -given to linkgit:git-rev-parse[1]. +from stdin, one per line, and print information about them. By default, +the whole line is considered as an object, as if it were fed to +linkgit:git-rev-parse[1]. You can specify the information shown for each object by using a custom `<format>`. The `<format>` is copied literally to stdout for each @@ -110,6 +109,13 @@ newline. The available atoms are: The size, in bytes, that the object takes up on disk. See the note about on-disk sizes in the `CAVEATS` section below. +`rest`:: + If this atom is used in the output string, input lines are split + at the first whitespace boundary. All characters before that + whitespace are considered to be the object name; characters + after that first run of whitespace (i.e., the "rest" of the + line) are output in place of the `%(rest)` atom. + If no format is specified, the default format is `%(objectname) %(objecttype) %(objectsize)`. diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt index a7be80d48b..00e2aa2df2 100644 --- a/Documentation/git-check-attr.txt +++ b/Documentation/git-check-attr.txt @@ -31,8 +31,9 @@ OPTIONS Read file names from stdin instead of from the command-line. -z:: - Only meaningful with `--stdin`; paths are separated with a - NUL character instead of a linefeed character. + The output format is modified to be machine-parseable. + If `--stdin` is also given, input paths are separated + with a NUL character instead of a linefeed character. \--:: Interpret all preceding arguments as attributes and all following @@ -48,6 +49,10 @@ OUTPUT The output is of the form: <path> COLON SP <attribute> COLON SP <info> LF +unless `-z` is in effect, in which case NUL is used as delimiter: +<path> NUL <attribute> NUL <info> NUL + + <path> is the path of a file being queried, <attribute> is an attribute being queried and <info> can be either: diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 2dbe486eb1..e9917b89a9 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -15,6 +15,7 @@ SYNOPSIS 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex] +'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL 'git config' [<file-option>] --unset name [value_regex] 'git config' [<file-option>] --unset-all name [value_regex] 'git config' [<file-option>] --rename-section old_name new_name @@ -95,6 +96,14 @@ OPTIONS in which section and variable names are lowercased, but subsection names are not. +--get-urlmatch name URL:: + When given a two-part name section.key, the value for + section.<url>.key whose <url> part matches the best to the + given URL is returned (if no such key exists, the value for + section.key is used as a fallback). When given just the + section as name, do so for all the keys in the section and + list them. + --global:: For writing options: write to global `~/.gitconfig` file rather than the repository `.git/config`, write to @@ -295,6 +304,13 @@ Given a .git/config like this: gitproxy=proxy-command for kernel.org gitproxy=default-proxy ; for all the rest + ; HTTP + [http] + sslVerify + [http "https://weak.example.com"] + sslVerify = false + cookieFile = /tmp/cookie.txt + you can set the filemode to true with ------------ @@ -380,6 +396,19 @@ RESET=$(git config --get-color "" "reset") echo "${WS}your whitespace color or blue reverse${RESET}" ------------ +For URLs in `https://weak.example.com`, `http.sslVerify` is set to +false, while it is set to `true` for all others: + +------------ +% git config --bool --get-urlmatch http.sslverify https://good.example.com +true +% git config --bool --get-urlmatch http.sslverify https://weak.example.com +false +% git config --get-urlmatch http https://weak.example.com +http.cookiefile /tmp/cookie.txt +http.sslverify false +------------ + include::config.txt[] GIT diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 1e71754347..444b805d35 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -90,6 +90,10 @@ be in a separate packet, and the list must end with a flush packet. --no-progress:: Do not show the progress. +--check-self-contained-and-connected:: + Output "connectivity-ok" if the received pack is + self-contained and connected. + -v:: Run verbosely. diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index e394276b1a..9e0ef0eaf3 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -242,6 +242,7 @@ configuration options in linkgit:git-notes[1] to use this workflow). Note that the leading character does not have to be a dot; for example, you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`. +-q:: --quiet:: Do not print the names of the generated files to standard output. diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index 2402ed6828..e158a3b31f 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- [verse] -'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] +'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] DESCRIPTION ----------- @@ -72,6 +72,10 @@ automatic consolidation of packs. --quiet:: Suppress all progress reports. +--force:: + Force `git gc` to run even if there may be another `git gc` + instance running on this repository. + Configuration ------------- diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index ac2694d04c..34097efea7 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -62,7 +62,8 @@ produced by --stat etc. Note that only message is considered, if also a diff is shown its size is not included. --L <start>,<end>:<file>, -L :<regex>:<file>:: +-L <start>,<end>:<file>:: +-L :<regex>:<file>:: Trace the evolution of the line range given by "<start>,<end>" (or the funcname regex <regex>) within the <file>. You may @@ -71,8 +72,6 @@ produced by --stat etc. give zero or one positive revision arguments. You can specify this option more than once. + -<start> and <end> can take one of these forms: - include::line-range-format.txt[] <revision range>:: diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt index d7db2a3737..d2fc12ec77 100644 --- a/Documentation/git-merge-file.txt +++ b/Documentation/git-merge-file.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]] [--ours|--theirs|--union] [-p|--stdout] [-q|--quiet] [--marker-size=<n>] - <current-file> <base-file> <other-file> + [--[no-]diff3] <current-file> <base-file> <other-file> DESCRIPTION @@ -66,6 +66,9 @@ OPTIONS -q:: Quiet; do not warn about conflicts. +--diff3:: + Show conflicts in "diff3" style. + --ours:: --theirs:: --union:: diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 8c7f2f66d8..a74c3713c6 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -186,11 +186,11 @@ In such a case, you can "unwrap" the tag yourself before feeding it to `git merge`, or pass `--ff-only` when you do not have any work on your own. e.g. ---- +---- git fetch origin git merge v1.2.3^0 git merge --ff-only v1.2.3 ---- +---- HOW CONFLICTS ARE PRESENTED diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index e93fcb49fd..b1f79881ef 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -13,7 +13,7 @@ SYNOPSIS DESCRIPTION ----------- -This script is used to move or rename a file, directory or symlink. +Move or rename a file, directory or symlink. git mv [-v] [-f] [-n] [-k] <source> <destination> git mv [-v] [-f] [-n] [-k] <source> ... <destination directory> @@ -44,6 +44,14 @@ OPTIONS --verbose:: Report the names of files as they are moved. +SUBMODULES +---------- +Moving a submodule using a gitfile (which means they were cloned +with a Git version 1.7.8 or newer) will update the gitfile and +core.worktree setting to make the submodule work in the new location. +It also will attempt to update the submodule.<name>.path setting in +the linkgit:gitmodules[5] file and stage that file (unless -n is used). + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 6ef8d599d3..beea10b148 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -102,12 +102,18 @@ include::merge-options.txt[] :git-pull: 1 -r:: ---rebase:: - Rebase the current branch on top of the upstream branch after - fetching. If there is a remote-tracking branch corresponding to - the upstream branch and the upstream branch was rebased since last - fetched, the rebase uses that information to avoid rebasing - non-local changes. +--rebase[=false|true|preserve]:: + When true, rebase the current branch on top of the upstream + branch after fetching. If there is a remote-tracking branch + corresponding to the upstream branch and the upstream branch + was rebased since last fetched, the rebase uses that information + to avoid rebasing non-local changes. ++ +When preserve, also rebase the current branch on top of the upstream +branch, but pass `--preserve-merges` along to `git rebase` so that +locally created merge commits will not be flattened. ++ +When false, merge the current branch into the upstream branch. + See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in linkgit:git-config[1] if you want to make `git pull` always use diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index f7dfe48d28..e2992f17a0 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream] + [--force-with-lease[=<refname>[:<expect>]]] [--no-verify] [<repository> [<refspec>...]] DESCRIPTION @@ -130,21 +131,75 @@ already exists on the remote side. repository over ssh, and you do not have the program in a directory on the default $PATH. +--[no-]force-with-lease:: +--force-with-lease=<refname>:: +--force-with-lease=<refname>:<expect>:: + Usually, "git push" refuses to update a remote ref that is + not an ancestor of the local ref used to overwrite it. ++ +This option bypasses the check, but instead requires that the +current value of the ref to be the expected value. "git push" +fails otherwise. ++ +Imagine that you have to rebase what you have already published. +You will have to bypass the "must fast-forward" rule in order to +replace the history you originally published with the rebased history. +If somebody else built on top of your original history while you are +rebasing, the tip of the branch at the remote may advance with her +commit, and blindly pushing with `--force` will lose her work. ++ +This option allows you to say that you expect the history you are +updating is what you rebased and want to replace. If the remote ref +still points at the commit you specified, you can be sure that no +other people did anything to the ref (it is like taking a "lease" on +the ref without explicitly locking it, and you update the ref while +making sure that your earlier "lease" is still valid). ++ +`--force-with-lease` alone, without specifying the details, will protect +all remote refs that are going to be updated by requiring their +current value to be the same as the remote-tracking branch we have +for them, unless specified with a `--force-with-lease=<refname>:<expect>` +option that explicitly states what the expected value is. ++ +`--force-with-lease=<refname>`, without specifying the expected value, will +protect the named ref (alone), if it is going to be updated, by +requiring its current value to be the same as the remote-tracking +branch we have for it. ++ +`--force-with-lease=<refname>:<expect>` will protect the named ref (alone), +if it is going to be updated, by requiring its current value to be +the same as the specified value <expect> (which is allowed to be +different from the remote-tracking branch we have for the refname, +or we do not even have to have such a remote-tracking branch when +this form is used). ++ +Note that all forms other than `--force-with-lease=<refname>:<expect>` +that specifies the expected current value of the ref explicitly are +still experimental and their semantics may change as we gain experience +with this feature. ++ +"--no-force-with-lease" will cancel all the previous --force-with-lease on the +command line. + -f:: --force:: Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. - This flag disables the check. This can cause the - remote repository to lose commits; use it with care. - Note that `--force` applies to all the refs that are pushed, - hence using it with `push.default` set to `matching` or with - multiple push destinations configured with `remote.*.push` - may overwrite refs other than the current branch (including - local refs that are strictly behind their remote counterpart). - To force a push to only one branch, use a `+` in front of the - refspec to push (e.g `git push origin +master` to force a push - to the `master` branch). See the `<refspec>...` section above - for details. + Also, when `--force-with-lease` option is used, the command refuses + to update a remote ref whose current value does not match + what is expected. ++ +This flag disables these checks, and can cause the remote repository +to lose commits; use it with care. ++ +Note that `--force` applies to all the refs that are pushed, hence +using it with `push.default` set to `matching` or with multiple push +destinations configured with `remote.*.push` may overwrite refs +other than the current branch (including local refs that are +strictly behind their remote counterpart). To force a push to only +one branch, use a `+` in front of the refspec to push (e.g `git push +origin +master` to force a push to the `master` branch). See the +`<refspec>...` section above for details. --repo=<repository>:: This option is only relevant if no <repository> argument is diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 2b126c0a77..d068a65377 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -24,9 +24,23 @@ distinguish between them. OPTIONS ------- + +Operation Modes +~~~~~~~~~~~~~~~ + +Each of these options must appear first on the command line. + --parseopt:: Use 'git rev-parse' in option parsing mode (see PARSEOPT section below). +--sq-quote:: + Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE + section below). In contrast to the `--sq` option below, this + mode does only quoting. Nothing else is done to command input. + +Options for --parseopt +~~~~~~~~~~~~~~~~~~~~~~ + --keep-dashdash:: Only meaningful in `--parseopt` mode. Tells the option parser to echo out the first `--` met instead of skipping it. @@ -36,10 +50,8 @@ OPTIONS the first non-option argument. This can be used to parse sub-commands that take options themselves. ---sq-quote:: - Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE - section below). In contrast to the `--sq` option below, this - mode does only quoting. Nothing else is done to command input. +Options for Filtering +~~~~~~~~~~~~~~~~~~~~~ --revs-only:: Do not output flags and parameters not meant for @@ -55,6 +67,9 @@ OPTIONS --no-flags:: Do not output flag parameters. +Options for Output +~~~~~~~~~~~~~~~~~~ + --default <arg>:: If there is no parameter given by the user, use `<arg>` instead. @@ -110,6 +125,17 @@ can be used. strip '{caret}' prefix from the object names that already have one. +--abbrev-ref[=(strict|loose)]:: + A non-ambiguous short name of the objects name. + The option core.warnAmbiguousRefs is used to select the strict + abbreviation mode. + +--short:: +--short=number:: + Instead of outputting the full SHA-1 values of object names try to + abbreviate them to a shorter unique name. When no length is specified + 7 is used. The minimum length is 4. + --symbolic:: Usually the object names are output in SHA-1 form (with possible '{caret}' prefix); this option makes them output in a @@ -123,16 +149,8 @@ can be used. unfortunately named tag "master"), and show them as full refnames (e.g. "refs/heads/master"). ---abbrev-ref[=(strict|loose)]:: - A non-ambiguous short name of the objects name. - The option core.warnAmbiguousRefs is used to select the strict - abbreviation mode. - ---disambiguate=<prefix>:: - Show every object whose name begins with the given prefix. - The <prefix> must be at least 4 hexadecimal digits long to - avoid listing each and every object in the repository by - mistake. +Options for Objects +~~~~~~~~~~~~~~~~~~~ --all:: Show all refs found in `refs/`. @@ -155,18 +173,20 @@ shown. If the pattern does not contain a globbing character (`?`, character (`?`, `*`, or `[`), it is turned into a prefix match by appending `/*`. ---show-toplevel:: - Show the absolute path of the top-level directory. +--disambiguate=<prefix>:: + Show every object whose name begins with the given prefix. + The <prefix> must be at least 4 hexadecimal digits long to + avoid listing each and every object in the repository by + mistake. ---show-prefix:: - When the command is invoked from a subdirectory, show the - path of the current directory relative to the top-level - directory. +Options for Files +~~~~~~~~~~~~~~~~~ ---show-cdup:: - When the command is invoked from a subdirectory, show the - path of the top-level directory relative to the current - directory (typically a sequence of "../", or an empty string). +--local-env-vars:: + List the GIT_* environment variables that are local to the + repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR). + Only the names of the variables are listed, not their value, + even if they are set. --git-dir:: Show `$GIT_DIR` if defined. Otherwise show the path to @@ -188,17 +208,27 @@ print a message to stderr and exit with nonzero status. --is-bare-repository:: When the repository is bare print "true", otherwise "false". ---local-env-vars:: - List the GIT_* environment variables that are local to the - repository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR). - Only the names of the variables are listed, not their value, - even if they are set. +--resolve-git-dir <path>:: + Check if <path> is a valid repository or a gitfile that + points at a valid repository, and print the location of the + repository. If <path> is a gitfile then the resolved path + to the real repository is printed. ---short:: ---short=number:: - Instead of outputting the full SHA-1 values of object names try to - abbreviate them to a shorter unique name. When no length is specified - 7 is used. The minimum length is 4. +--show-cdup:: + When the command is invoked from a subdirectory, show the + path of the top-level directory relative to the current + directory (typically a sequence of "../", or an empty string). + +--show-prefix:: + When the command is invoked from a subdirectory, show the + path of the current directory relative to the top-level + directory. + +--show-toplevel:: + Show the absolute path of the top-level directory. + +Other Options +~~~~~~~~~~~~~ --since=datestring:: --after=datestring:: @@ -213,12 +243,6 @@ print a message to stderr and exit with nonzero status. <args>...:: Flags and parameters to be parsed. ---resolve-git-dir <path>:: - Check if <path> is a valid repository or a gitfile that - points at a valid repository, and print the location of the - repository. If <path> is a gitfile then the resolved path - to the real repository is printed. - include::revisions.txt[] diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index 1d876c2619..9d731b453d 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -134,14 +134,16 @@ use the following command: git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached ---------------- -Submodules -~~~~~~~~~~ +SUBMODULES +---------- Only submodules using a gitfile (which means they were cloned with a Git version 1.7.8 or newer) will be removed from the work tree, as their repository lives inside the .git directory of the superproject. If a submodule (or one of those nested inside it) still uses a .git directory, `git rm` will fail - no matter if forced -or not - to protect the submodule's history. +or not - to protect the submodule's history. If it exists the +submodule.<name> section in the linkgit:gitmodules[5] file will also +be removed and that file will be staged (unless --cached or -n are used). A submodule is considered up-to-date when the HEAD is the same as recorded in the index, no tracked files are modified and no untracked diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt index c600b61e2b..8b63ceb00e 100644 --- a/Documentation/git-whatchanged.txt +++ b/Documentation/git-whatchanged.txt @@ -13,43 +13,17 @@ SYNOPSIS DESCRIPTION ----------- -Shows commit logs and diff output each commit introduces. The -command internally invokes 'git rev-list' piped to -'git diff-tree', and takes command line options for both of -these commands. -This manual page describes only the most frequently used options. +Shows commit logs and diff output each commit introduces. +New users are encouraged to use linkgit:git-log[1] instead. The +`whatchanged` command is essentially the same as linkgit:git-log[1] +but defaults to show the raw format diff output and to skip merges. -OPTIONS -------- --p:: - Show textual diffs, instead of the Git internal diff - output format that is useful only to tell the changed - paths and their nature of changes. +The command is kept primarily for historical reasons; fingers of +many people who learned Git long before `git log` was invented by +reading Linux kernel mailing list are trained to type it. --<n>:: - Limit output to <n> commits. - -<since>..<until>:: - Limit output to between the two named commits (bottom - exclusive, top inclusive). - --r:: - Show Git internal diff output, but for the whole tree, - not just the top level. - --m:: - By default, differences for merge commits are not shown. - With this flag, show differences to that commit from all - of its parents. -+ -However, it is not very useful in general, although it -*is* useful on a file-by-file basis. - -include::pretty-options.txt[] - -include::pretty-formats.txt[] Examples -------- diff --git a/Documentation/git.txt b/Documentation/git.txt index dca11cc96e..c4f0ed5957 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -457,10 +457,25 @@ help ...`. linkgit:git-replace[1] for more information. --literal-pathspecs:: - Treat pathspecs literally, rather than as glob patterns. This is - equivalent to setting the `GIT_LITERAL_PATHSPECS` environment + Treat pathspecs literally (i.e. no globbing, no pathspec magic). + This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment variable to `1`. +--glob-pathspecs: + Add "glob" magic to all pathspec. This is equivalent to setting + the `GIT_GLOB_PATHSPECS` environment variable to `1`. Disabling + globbing on individual pathspecs can be done using pathspec + magic ":(literal)" + +--noglob-pathspecs: + Add "literal" magic to all pathspec. This is equivalent to setting + the `GIT_NOGLOB_PATHSPECS` environment variable to `1`. Enabling + globbing on individual pathspecs can be done using pathspec + magic ":(glob)" + +--icase-pathspecs: + Add "icase" magic to all pathspec. This is equivalent to setting + the `GIT_ICASE_PATHSPECS` environment variable to `1`. GIT COMMANDS ------------ @@ -823,7 +838,7 @@ for further details. 'GIT_FLUSH':: If this environment variable is set to "1", then commands such as 'git blame' (in incremental mode), 'git rev-list', 'git log', - 'git check-attr', 'git check-ignore', and 'git whatchanged' will + 'git check-attr' and 'git check-ignore' will force a flush of the output stream after each record have been flushed. If this variable is set to "0", the output of these commands will be done @@ -867,6 +882,18 @@ GIT_LITERAL_PATHSPECS:: literal paths to Git (e.g., paths previously given to you by `git ls-tree`, `--raw` diff output, etc). +GIT_GLOB_PATHSPECS:: + Setting this variable to `1` will cause Git to treat all + pathspecs as glob patterns (aka "glob" magic). + +GIT_NOGLOB_PATHSPECS:: + Setting this variable to `1` will cause Git to treat all + pathspecs as literal (aka "literal" magic). + +GIT_ICASE_PATHSPECS:: + Setting this variable to `1` will cause Git to treat all + pathspecs as case-insensitive. + Discussion[[Discussion]] ------------------------ diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt index f538a870c7..058a352980 100644 --- a/Documentation/gitcore-tutorial.txt +++ b/Documentation/gitcore-tutorial.txt @@ -534,42 +534,9 @@ all, but just show the actual commit message. In fact, together with the 'git rev-list' program (which generates a list of revisions), 'git diff-tree' ends up being a veritable fount of -changes. A trivial (but very useful) script called 'git whatchanged' is -included with Git which does exactly this, and shows a log of recent -activities. - -To see the whole history of our pitiful little git-tutorial project, you -can do - ----------------- -$ git log ----------------- - -which shows just the log messages, or if we want to see the log together -with the associated patches use the more complex (and much more -powerful) - ----------------- -$ git whatchanged -p ----------------- - -and you will see exactly what has changed in the repository over its -short history. - -[NOTE] -When using the above two commands, the initial commit will be shown. -If this is a problem because it is huge, you can hide it by setting -the log.showroot configuration variable to false. Having this, you -can still show it for each command just adding the `--root` option, -which is a flag for 'git diff-tree' accepted by both commands. - -With that, you should now be having some inkling of what Git does, and -can explore on your own. - -[NOTE] -Most likely, you are not directly using the core -Git Plumbing commands, but using Porcelain such as 'git add', `git-rm' -and `git-commit'. +changes. You can emulate `git log`, `git log -p`, etc. with a trivial +script that pipes the output of `git rev-list` to `git diff-tree --stdin`, +which was exactly how early versions of `git log` were implemented. Tagging a version diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt index 0827f69139..bc069c23df 100644 --- a/Documentation/gitremote-helpers.txt +++ b/Documentation/gitremote-helpers.txt @@ -143,6 +143,10 @@ Supported commands: 'list', 'fetch'. + Supported commands: 'list', 'import'. +'check-connectivity':: + Can guarantee that when a clone is requested, the received + pack is self contained and is connected. + If a helper advertises 'connect', Git will use it if possible and fall back to another capability if the helper requests so when connecting (see the 'connect' command under COMMANDS). @@ -270,6 +274,9 @@ 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. + +If option 'check-connectivity' is requested, the helper must output +'connectivity-ok' if the clone is self-contained and connected. ++ Supported if the helper has the "fetch" capability. 'push' +<src>:<dst>:: @@ -416,6 +423,9 @@ set by Git if the remote helper has the 'option' capability. must not rely on this option being set before connect request occurs. +'option check-connectivity' \{'true'|'false'\}:: + Request the helper to check connectivity of a clone. + SEE ALSO -------- linkgit:git-remote[1] diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index dba5062b37..13a64d3aac 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -322,10 +322,54 @@ and a close parentheses `)`, and the remainder is the pattern to match against the path. + The "magic signature" consists of an ASCII symbol that is not -alphanumeric. Currently only the slash `/` is recognized as a -"magic signature": it makes the pattern match from the root of -the working tree, even when you are running the command from -inside a subdirectory. +alphanumeric. ++ +-- +top `/`;; + The magic word `top` (mnemonic: `/`) makes the pattern match + from the root of the working tree, even when you are running + the command from inside a subdirectory. + +literal;; + Wildcards in the pattern such as `*` or `?` are treated + as literal characters. + +icase;; + Case insensitive match. + +glob;; + Git treats the pattern as a shell glob suitable for + consumption by fnmatch(3) with the FNM_PATHNAME flag: + wildcards in the pattern will not match a / in the pathname. + For example, "Documentation/{asterisk}.html" matches + "Documentation/git.html" but not "Documentation/ppc/ppc.html" + or "tools/perf/Documentation/perf.html". ++ +Two consecutive asterisks ("`**`") in patterns matched against +full pathname may have special meaning: + + - A leading "`**`" followed by a slash means match in all + directories. For example, "`**/foo`" matches file or directory + "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar" + matches file or directory "`bar`" anywhere that is directly + under directory "`foo`". + + - A trailing "/**" matches everything inside. For example, + "abc/**" matches all files inside directory "abc", relative + to the location of the `.gitignore` file, with infinite depth. + + - A slash followed by two consecutive asterisks then a slash + matches zero or more directories. For example, "`a/**/b`" + matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on. + + - Other consecutive asterisks are considered invalid. ++ +Glob magic is incompatible with literal magic. +-- ++ +Currently only the slash `/` is recognized as the "magic signature", +but it is envisioned that we will support more types of magic in later +versions of Git. + A pathspec with only a colon means "there is no pathspec". This form should not be combined with other pathspec. diff --git a/Documentation/line-range-format.txt b/Documentation/line-range-format.txt index 3e7ce72daa..d7f26039ca 100644 --- a/Documentation/line-range-format.txt +++ b/Documentation/line-range-format.txt @@ -1,3 +1,5 @@ +<start> and <end> can take one of these forms: + - number + If <start> or <end> is a number, it specifies an @@ -7,7 +9,10 @@ absolute line number (lines count from 1). - /regex/ + This form will use the first line matching the given -POSIX regex. If <end> is a regex, it will search +POSIX regex. If <start> is a regex, it will search from the end of +the previous `-L` range, if any, otherwise from the start of file. +If <start> is ``^/regex/'', it will search from the start of file. +If <end> is a regex, it will search starting at the line given by <start>. + @@ -15,11 +20,10 @@ starting at the line given by <start>. + This is only valid for <end> and will specify a number of lines before or after the line given by <start>. -+ -- :regex + -If the option's argument is of the form :regex, it denotes the range +If ``:<regex>'' is given in place of <start> and <end>, it denotes the range from the first funcname line that matches <regex>, up to the next -funcname line. -+ +funcname line. ``:<regex>'' searches from the end of the previous `-L` range, +if any, otherwise from the start of file. +``^:<regex>'' searches from the start of file. diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt index 4f63a04d7d..540e455689 100644 --- a/Documentation/technical/api-setup.txt +++ b/Documentation/technical/api-setup.txt @@ -8,6 +8,42 @@ Talk about * is_inside_git_dir() * is_inside_work_tree() * setup_work_tree() -* get_pathspec() (Dscho) + +Pathspec +-------- + +See glossary-context.txt for the syntax of pathspec. In memory, a +pathspec set is represented by "struct pathspec" and is prepared by +parse_pathspec(). This function takes several arguments: + +- magic_mask specifies what features that are NOT supported by the + following code. If a user attempts to use such a feature, + parse_pathspec() can reject it early. + +- flags specifies other things that the caller wants parse_pathspec to + perform. + +- prefix and args come from cmd_* functions + +get_pathspec() is obsolete and should never be used in new code. + +parse_pathspec() helps catch unsupported features and reject them +politely. At a lower level, different pathspec-related functions may +not support the same set of features. Such pathspec-sensitive +functions are guarded with GUARD_PATHSPEC(), which will die in an +unfriendly way when an unsupported feature is requested. + +The command designers are supposed to make sure that GUARD_PATHSPEC() +never dies. They have to make sure all unsupported features are caught +by parse_pathspec(), not by GUARD_PATHSPEC. grepping GUARD_PATHSPEC() +should give the designers all pathspec-sensitive codepaths and what +features they support. + +A similar process is applied when a new pathspec magic is added. The +designer lifts the GUARD_PATHSPEC restriction in the functions that +support the new magic. At the same time (s)he has to make sure this +new feature will be caught at parse_pathspec() in commands that cannot +handle the new magic in some cases. grepping parse_pathspec() should +help. diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt new file mode 100644 index 0000000000..a1173ee266 --- /dev/null +++ b/Documentation/technical/http-protocol.txt @@ -0,0 +1,503 @@ +HTTP transfer protocols +======================= + +Git supports two HTTP based transfer protocols. A "dumb" protocol +which requires only a standard HTTP server on the server end of the +connection, and a "smart" protocol which requires a Git aware CGI +(or server module). This document describes both protocols. + +As a design feature smart clients can automatically upgrade "dumb" +protocol URLs to smart URLs. This permits all users to have the +same published URL, and the peers automatically select the most +efficient transport available to them. + + +URL Format +---------- + +URLs for Git repositories accessed by HTTP use the standard HTTP +URL syntax documented by RFC 1738, so they are of the form: + + http://<host>:<port>/<path>?<searchpart> + +Within this documentation the placeholder $GIT_URL will stand for +the http:// repository URL entered by the end-user. + +Servers SHOULD handle all requests to locations matching $GIT_URL, as +both the "smart" and "dumb" HTTP protocols used by Git operate +by appending additional path components onto the end of the user +supplied $GIT_URL string. + +An example of a dumb client requesting for a loose object: + + $GIT_URL: http://example.com:8080/git/repo.git + URL request: http://example.com:8080/git/repo.git/objects/d0/49f6c27a2244e12041955e262a404c7faba355 + +An example of a smart request to a catch-all gateway: + + $GIT_URL: http://example.com/daemon.cgi?svc=git&q= + URL request: http://example.com/daemon.cgi?svc=git&q=/info/refs&service=git-receive-pack + +An example of a request to a submodule: + + $GIT_URL: http://example.com/git/repo.git/path/submodule.git + URL request: http://example.com/git/repo.git/path/submodule.git/info/refs + +Clients MUST strip a trailing '/', if present, from the user supplied +$GIT_URL string to prevent empty path tokens ('//') from appearing +in any URL sent to a server. Compatible clients MUST expand +'$GIT_URL/info/refs' as 'foo/info/refs' and not 'foo//info/refs'. + + +Authentication +-------------- + +Standard HTTP authentication is used if authentication is required +to access a repository, and MAY be configured and enforced by the +HTTP server software. + +Because Git repositories are accessed by standard path components +server administrators MAY use directory based permissions within +their HTTP server to control repository access. + +Clients SHOULD support Basic authentication as described by RFC 2616. +Servers SHOULD support Basic authentication by relying upon the +HTTP server placed in front of the Git server software. + +Servers SHOULD NOT require HTTP cookies for the purposes of +authentication or access control. + +Clients and servers MAY support other common forms of HTTP based +authentication, such as Digest authentication. + + +SSL +--- + +Clients and servers SHOULD support SSL, particularly to protect +passwords when relying on Basic HTTP authentication. + + +Session State +------------- + +The Git over HTTP protocol (much like HTTP itself) is stateless +from the perspective of the HTTP server side. All state MUST be +retained and managed by the client process. This permits simple +round-robin load-balancing on the server side, without needing to +worry about state management. + +Clients MUST NOT require state management on the server side in +order to function correctly. + +Servers MUST NOT require HTTP cookies in order to function correctly. +Clients MAY store and forward HTTP cookies during request processing +as described by RFC 2616 (HTTP/1.1). Servers SHOULD ignore any +cookies sent by a client. + + +General Request Processing +-------------------------- + +Except where noted, all standard HTTP behavior SHOULD be assumed +by both client and server. This includes (but is not necessarily +limited to): + +If there is no repository at $GIT_URL, or the resource pointed to by a +location matching $GIT_URL does not exist, the server MUST NOT respond +with '200 OK' response. A server SHOULD respond with +'404 Not Found', '410 Gone', or any other suitable HTTP status code +which does not imply the resource exists as requested. + +If there is a repository at $GIT_URL, but access is not currently +permitted, the server MUST respond with the '403 Forbidden' HTTP +status code. + +Servers SHOULD support both HTTP 1.0 and HTTP 1.1. +Servers SHOULD support chunked encoding for both request and response +bodies. + +Clients SHOULD support both HTTP 1.0 and HTTP 1.1. +Clients SHOULD support chunked encoding for both request and response +bodies. + +Servers MAY return ETag and/or Last-Modified headers. + +Clients MAY revalidate cached entities by including If-Modified-Since +and/or If-None-Match request headers. + +Servers MAY return '304 Not Modified' if the relevant headers appear +in the request and the entity has not changed. Clients MUST treat +'304 Not Modified' identical to '200 OK' by reusing the cached entity. + +Clients MAY reuse a cached entity without revalidation if the +Cache-Control and/or Expires header permits caching. Clients and +servers MUST follow RFC 2616 for cache controls. + + +Discovering References +---------------------- + +All HTTP clients MUST begin either a fetch or a push exchange by +discovering the references available on the remote repository. + +Dumb Clients +~~~~~~~~~~~~ + +HTTP clients that only support the "dumb" protocol MUST discover +references by making a request for the special info/refs file of +the repository. + +Dumb HTTP clients MUST make a GET request to $GIT_URL/info/refs, +without any search/query parameters. + + C: GET $GIT_URL/info/refs HTTP/1.0 + + S: 200 OK + S: + S: 95dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint + S: d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master + S: 2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0 + S: a3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{} + +The Content-Type of the returned info/refs entity SHOULD be +"text/plain; charset=utf-8", but MAY be any content type. +Clients MUST NOT attempt to validate the returned Content-Type. +Dumb servers MUST NOT return a return type starting with +"application/x-git-". + +Cache-Control headers MAY be returned to disable caching of the +returned entity. + +When examining the response clients SHOULD only examine the HTTP +status code. Valid responses are '200 OK', or '304 Not Modified'. + +The returned content is a UNIX formatted text file describing +each ref and its known value. The file SHOULD be sorted by name +according to the C locale ordering. The file SHOULD NOT include +the default ref named 'HEAD'. + + info_refs = *( ref_record ) + ref_record = any_ref / peeled_ref + + any_ref = obj-id HTAB refname LF + peeled_ref = obj-id HTAB refname LF + obj-id HTAB refname "^{}" LF + +Smart Clients +~~~~~~~~~~~~~ + +HTTP clients that support the "smart" protocol (or both the +"smart" and "dumb" protocols) MUST discover references by making +a parameterized request for the info/refs file of the repository. + +The request MUST contain exactly one query parameter, +'service=$servicename', where $servicename MUST be the service +name the client wishes to contact to complete the operation. +The request MUST NOT contain additional query parameters. + + C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0 + + dumb server reply: + S: 200 OK + S: + S: 95dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint + S: d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master + S: 2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0 + S: a3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{} + + smart server reply: + S: 200 OK + S: Content-Type: application/x-git-upload-pack-advertisement + S: Cache-Control: no-cache + S: + S: 001e# service=git-upload-pack\n + S: 004895dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint\0multi_ack\n + S: 0042d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master\n + S: 003c2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0\n + S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}\n + +Dumb Server Response +^^^^^^^^^^^^^^^^^^^^ +Dumb servers MUST respond with the dumb server reply format. + +See the prior section under dumb clients for a more detailed +description of the dumb server response. + +Smart Server Response +^^^^^^^^^^^^^^^^^^^^^ +If the server does not recognize the requested service name, or the +requested service name has been disabled by the server administrator, +the server MUST respond with the '403 Forbidden' HTTP status code. + +Otherwise, smart servers MUST respond with the smart server reply +format for the requested service name. + +Cache-Control headers SHOULD be used to disable caching of the +returned entity. + +The Content-Type MUST be 'application/x-$servicename-advertisement'. +Clients SHOULD fall back to the dumb protocol if another content +type is returned. When falling back to the dumb protocol clients +SHOULD NOT make an additional request to $GIT_URL/info/refs, but +instead SHOULD use the response already in hand. Clients MUST NOT +continue if they do not support the dumb protocol. + +Clients MUST validate the status code is either '200 OK' or +'304 Not Modified'. + +Clients MUST validate the first five bytes of the response entity +matches the regex "^[0-9a-f]{4}#". If this test fails, clients +MUST NOT continue. + +Clients MUST parse the entire response as a sequence of pkt-line +records. + +Clients MUST verify the first pkt-line is "# service=$servicename". +Servers MUST set $servicename to be the request parameter value. +Servers SHOULD include an LF at the end of this line. +Clients MUST ignore an LF at the end of the line. + +Servers MUST terminate the response with the magic "0000" end +pkt-line marker. + +The returned response is a pkt-line stream describing each ref and +its known value. The stream SHOULD be sorted by name according to +the C locale ordering. The stream SHOULD include the default ref +named 'HEAD' as the first ref. The stream MUST include capability +declarations behind a NUL on the first ref. + + smart_reply = PKT-LINE("# service=$servicename" LF) + ref_list + "0000" + ref_list = empty_list / non_empty_list + + empty_list = PKT-LINE(zero-id SP "capabilities^{}" NUL cap-list LF) + + non_empty_list = PKT-LINE(obj-id SP name NUL cap_list LF) + *ref_record + + cap-list = capability *(SP capability) + capability = 1*(LC_ALPHA / DIGIT / "-" / "_") + LC_ALPHA = %x61-7A + + ref_record = any_ref / peeled_ref + any_ref = PKT-LINE(obj-id SP name LF) + peeled_ref = PKT-LINE(obj-id SP name LF) + PKT-LINE(obj-id SP name "^{}" LF + +Smart Service git-upload-pack +------------------------------ +This service reads from the repository pointed to by $GIT_URL. + +Clients MUST first perform ref discovery with +'$GIT_URL/info/refs?service=git-upload-pack'. + + C: POST $GIT_URL/git-upload-pack HTTP/1.0 + C: Content-Type: application/x-git-upload-pack-request + C: + C: 0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7\n + C: 0032have 441b40d833fdfa93eb2908e52742248faf0ee993\n + C: 0000 + + S: 200 OK + S: Content-Type: application/x-git-upload-pack-result + S: Cache-Control: no-cache + S: + S: ....ACK %s, continue + S: ....NAK + +Clients MUST NOT reuse or revalidate a cached reponse. +Servers MUST include sufficient Cache-Control headers +to prevent caching of the response. + +Servers SHOULD support all capabilities defined here. + +Clients MUST send at least one 'want' command in the request body. +Clients MUST NOT reference an id in a 'want' command which did not +appear in the response obtained through ref discovery unless the +server advertises capability "allow-tip-sha1-in-want". + + compute_request = want_list + have_list + request_end + request_end = "0000" / "done" + + want_list = PKT-LINE(want NUL cap_list LF) + *(want_pkt) + want_pkt = PKT-LINE(want LF) + want = "want" SP id + cap_list = *(SP capability) SP + + have_list = *PKT-LINE("have" SP id LF) + +TODO: Document this further. +TODO: Don't use uppercase for variable names below. + +The Negotiation Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~ +The computation to select the minimal pack proceeds as follows +(c = client, s = server): + + init step: + (c) Use ref discovery to obtain the advertised refs. + (c) Place any object seen into set ADVERTISED. + + (c) Build an empty set, COMMON, to hold the objects that are later + determined to be on both ends. + (c) Build a set, WANT, of the objects from ADVERTISED the client + wants to fetch, based on what it saw during ref discovery. + + (c) Start a queue, C_PENDING, ordered by commit time (popping newest + first). Add all client refs. When a commit is popped from + the queue its parents SHOULD be automatically inserted back. + Commits MUST only enter the queue once. + + one compute step: + (c) Send one $GIT_URL/git-upload-pack request: + + C: 0032want <WANT #1>............................... + C: 0032want <WANT #2>............................... + .... + C: 0032have <COMMON #1>............................. + C: 0032have <COMMON #2>............................. + .... + C: 0032have <HAVE #1>............................... + C: 0032have <HAVE #2>............................... + .... + C: 0000 + + The stream is organized into "commands", with each command + appearing by itself in a pkt-line. Within a command line + the text leading up to the first space is the command name, + and the remainder of the line to the first LF is the value. + Command lines are terminated with an LF as the last byte of + the pkt-line value. + + Commands MUST appear in the following order, if they appear + at all in the request stream: + + * want + * have + + The stream is terminated by a pkt-line flush ("0000"). + + A single "want" or "have" command MUST have one hex formatted + SHA-1 as its value. Multiple SHA-1s MUST be sent by sending + multiple commands. + + The HAVE list is created by popping the first 32 commits + from C_PENDING. Less can be supplied if C_PENDING empties. + + If the client has sent 256 HAVE commits and has not yet + received one of those back from S_COMMON, or the client has + emptied C_PENDING it SHOULD include a "done" command to let + the server know it won't proceed: + + C: 0009done + + (s) Parse the git-upload-pack request: + + Verify all objects in WANT are directly reachable from refs. + + The server MAY walk backwards through history or through + the reflog to permit slightly stale requests. + + If no WANT objects are received, send an error: + +TODO: Define error if no want lines are requested. + + If any WANT object is not reachable, send an error: + +TODO: Define error if an invalid want is requested. + + Create an empty list, S_COMMON. + + If 'have' was sent: + + Loop through the objects in the order supplied by the client. + For each object, if the server has the object reachable from + a ref, add it to S_COMMON. If a commit is added to S_COMMON, + do not add any ancestors, even if they also appear in HAVE. + + (s) Send the git-upload-pack response: + + If the server has found a closed set of objects to pack or the + request ends with "done", it replies with the pack. + +TODO: Document the pack based response + S: PACK... + + The returned stream is the side-band-64k protocol supported + by the git-upload-pack service, and the pack is embedded into + stream 1. Progress messages from the server side MAY appear + in stream 2. + + Here a "closed set of objects" is defined to have at least + one path from every WANT to at least one COMMON object. + + If the server needs more information, it replies with a + status continue response: + +TODO: Document the non-pack response + + (c) Parse the upload-pack response: + +TODO: Document parsing response + + Do another compute step. + + +Smart Service git-receive-pack +------------------------------ +This service reads from the repository pointed to by $GIT_URL. + +Clients MUST first perform ref discovery with +'$GIT_URL/info/refs?service=git-receive-pack'. + + C: POST $GIT_URL/git-receive-pack HTTP/1.0 + C: Content-Type: application/x-git-receive-pack-request + C: + C: ....0a53e9ddeaddad63ad106860237bbf53411d11a7 441b40d833fdfa93eb2908e52742248faf0ee993 refs/heads/maint\0 report-status + C: 0000 + C: PACK.... + + S: 200 OK + S: Content-Type: application/x-git-receive-pack-result + S: Cache-Control: no-cache + S: + S: .... + +Clients MUST NOT reuse or revalidate a cached reponse. +Servers MUST include sufficient Cache-Control headers +to prevent caching of the response. + +Servers SHOULD support all capabilities defined here. + +Clients MUST send at least one command in the request body. +Within the command portion of the request body clients SHOULD send +the id obtained through ref discovery as old_id. + + update_request = command_list + "PACK" <binary data> + + command_list = PKT-LINE(command NUL cap_list LF) + *(command_pkt) + command_pkt = PKT-LINE(command LF) + cap_list = *(SP capability) SP + + command = create / delete / update + create = zero-id SP new_id SP name + delete = old_id SP zero-id SP name + update = old_id SP new_id SP name + +TODO: Document this further. + + +References +---------- + +link:http://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)] +link:http://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1] +link:technical/pack-protocol.txt +link:technical/protocol-capabilities.txt diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 06026eaed7..b444c18f17 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.8.4 +DEF_VER=v1.8.4.GIT LF=' ' @@ -69,9 +69,6 @@ all:: # Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt # doesn't support GNU extensions like --check and --statistics # -# Define NEEDS_CLIPPED_WRITE if your write(2) cannot write more than -# INT_MAX bytes at once (e.g. MacOS X). -# # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH # it specifies. # @@ -580,6 +577,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-string-list TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe +TEST_PROGRAMS_NEED_X += test-urlmatch-normalization TEST_PROGRAMS_NEED_X += test-wildmatch TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) @@ -736,6 +734,7 @@ LIB_H += tree-walk.h LIB_H += tree.h LIB_H += unpack-trees.h LIB_H += url.h +LIB_H += urlmatch.h LIB_H += userdiff.h LIB_H += utf8.h LIB_H += varint.h @@ -886,6 +885,7 @@ LIB_OBJS += tree.o LIB_OBJS += tree-walk.o LIB_OBJS += unpack-trees.o LIB_OBJS += url.o +LIB_OBJS += urlmatch.o LIB_OBJS += usage.o LIB_OBJS += userdiff.o LIB_OBJS += utf8.o @@ -1182,6 +1182,9 @@ ifdef NEEDS_SSL_WITH_CRYPTO else LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto endif +ifdef APPLE_COMMON_CRYPTO + LIB_4_CRYPTO += -framework Security -framework CoreFoundation +endif endif ifdef NEEDS_LIBICONV ifdef ICONVDIR @@ -1493,11 +1496,6 @@ ifndef NO_MSGFMT_EXTENDED_OPTIONS MSGFMT += --check --statistics endif -ifdef NEEDS_CLIPPED_WRITE - BASIC_CFLAGS += -DNEEDS_CLIPPED_WRITE - COMPAT_OBJS += compat/clipped-write.o -endif - ifneq (,$(XDL_FAST_HASH)) BASIC_CFLAGS += -DXDL_FAST_HASH endif @@ -1 +1 @@ -Documentation/RelNotes/1.8.4.txt
\ No newline at end of file +Documentation/RelNotes/1.8.5.txt
\ No newline at end of file @@ -151,7 +151,6 @@ int write_archive_entries(struct archiver_args *args, struct archiver_context context; struct unpack_trees_options opts; struct tree_desc t; - struct pathspec pathspec; int err; if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { @@ -186,10 +185,8 @@ int write_archive_entries(struct archiver_args *args, git_attr_set_direction(GIT_ATTR_INDEX, &the_index); } - init_pathspec(&pathspec, args->pathspec); - err = read_tree_recursive(args->tree, "", 0, 0, &pathspec, + err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec, write_archive_entry, &context); - free_pathspec(&pathspec); if (err == READ_TREE_RECURSIVE) err = 0; return err; @@ -222,7 +219,7 @@ static int path_exists(struct tree *tree, const char *path) struct pathspec pathspec; int ret; - init_pathspec(&pathspec, paths); + parse_pathspec(&pathspec, 0, 0, "", paths); ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL); free_pathspec(&pathspec); return ret != 0; @@ -231,11 +228,18 @@ static int path_exists(struct tree *tree, const char *path) static void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) { - ar_args->pathspec = pathspec = get_pathspec("", pathspec); + /* + * must be consistent with parse_pathspec in path_exists() + * Also if pathspec patterns are dependent, we're in big + * trouble as we test each one separately + */ + parse_pathspec(&ar_args->pathspec, 0, + PATHSPEC_PREFER_FULL, + "", pathspec); if (pathspec) { while (*pathspec) { if (**pathspec && !path_exists(ar_args->tree, *pathspec)) - die("path not found: %s", *pathspec); + die(_("pathspec '%s' did not match any files"), *pathspec); pathspec++; } } @@ -1,6 +1,8 @@ #ifndef ARCHIVE_H #define ARCHIVE_H +#include "pathspec.h" + struct archiver_args { const char *base; size_t baselen; @@ -8,7 +10,7 @@ struct archiver_args { const unsigned char *commit_sha1; const struct commit *commit; time_t time; - const char **pathspec; + struct pathspec pathspec; unsigned int verbose : 1; unsigned int worktree_attributes : 1; unsigned int convert : 1; diff --git a/builtin/add.c b/builtin/add.c index 8266a9cb70..31ddabd994 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -166,14 +166,16 @@ static void update_callback(struct diff_queue_struct *q, } } -static void update_files_in_cache(const char *prefix, const char **pathspec, +static void update_files_in_cache(const char *prefix, + const struct pathspec *pathspec, struct update_callback_data *data) { struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - init_pathspec(&rev.prune_data, pathspec); + if (pathspec) + copy_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = data; @@ -181,7 +183,8 @@ static void update_files_in_cache(const char *prefix, const char **pathspec, run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } -int add_files_to_cache(const char *prefix, const char **pathspec, int flags) +int add_files_to_cache(const char *prefix, + const struct pathspec *pathspec, int flags) { struct update_callback_data data; @@ -192,23 +195,21 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags) } #define WARN_IMPLICIT_DOT (1u << 0) -static char *prune_directory(struct dir_struct *dir, const char **pathspec, +static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix, unsigned flag) { char *seen; - int i, specs; + int i; struct dir_entry **src, **dst; - for (specs = 0; pathspec[specs]; specs++) - /* nothing */; - seen = xcalloc(specs, 1); + seen = xcalloc(pathspec->nr, 1); src = dst = dir->entries; i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; - if (match_pathspec(pathspec, entry->name, entry->len, - prefix, seen)) + if (match_pathspec_depth(pathspec, entry->name, entry->len, + prefix, seen)) *dst++ = entry; else if (flag & WARN_IMPLICIT_DOT) /* @@ -222,72 +223,33 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, warn_pathless_add(); } dir->nr = dst - dir->entries; - add_pathspec_matches_against_index(pathspec, seen, specs); + add_pathspec_matches_against_index(pathspec, seen); return seen; } -/* - * Checks the index to see whether any path in pathspec refers to - * something inside a submodule. If so, dies with an error message. - */ -static void treat_gitlinks(const char **pathspec) -{ - int i; - - if (!pathspec || !*pathspec) - return; - - for (i = 0; pathspec[i]; i++) - pathspec[i] = check_path_for_gitlink(pathspec[i]); -} - -static void refresh(int verbose, const char **pathspec) +static void refresh(int verbose, const struct pathspec *pathspec) { char *seen; - int i, specs; + int i; - for (specs = 0; pathspec[specs]; specs++) - /* nothing */; - seen = xcalloc(specs, 1); + seen = xcalloc(pathspec->nr, 1); refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET, pathspec, seen, _("Unstaged changes after refreshing the index:")); - for (i = 0; i < specs; i++) { + for (i = 0; i < pathspec->nr; i++) { if (!seen[i]) - die(_("pathspec '%s' did not match any files"), pathspec[i]); + die(_("pathspec '%s' did not match any files"), + pathspec->items[i].match); } free(seen); } -/* - * Normalizes argv relative to prefix, via get_pathspec(), and then - * runs die_if_path_beyond_symlink() on each path in the normalized - * list. - */ -static const char **validate_pathspec(const char **argv, const char *prefix) -{ - const char **pathspec = get_pathspec(prefix, argv); - - if (pathspec) { - const char **p; - for (p = pathspec; *p; p++) { - die_if_path_beyond_symlink(*p, prefix); - } - } - - return pathspec; -} - int run_add_interactive(const char *revision, const char *patch_mode, - const char **pathspec) + const struct pathspec *pathspec) { - int status, ac, pc = 0; + int status, ac, i; const char **args; - if (pathspec) - while (pathspec[pc]) - pc++; - - args = xcalloc(sizeof(const char *), (pc + 5)); + args = xcalloc(sizeof(const char *), (pathspec->nr + 6)); ac = 0; args[ac++] = "add--interactive"; if (patch_mode) @@ -295,11 +257,9 @@ int run_add_interactive(const char *revision, const char *patch_mode, if (revision) args[ac++] = revision; args[ac++] = "--"; - if (pc) { - memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc); - ac += pc; - } - args[ac] = NULL; + for (i = 0; i < pathspec->nr; i++) + /* pass original pathspec, to be re-parsed */ + args[ac++] = pathspec->items[i].original; status = run_command_v_opt(args, RUN_GIT_CMD); free(args); @@ -308,17 +268,17 @@ int run_add_interactive(const char *revision, const char *patch_mode, int interactive_add(int argc, const char **argv, const char *prefix, int patch) { - const char **pathspec = NULL; + struct pathspec pathspec; - if (argc) { - pathspec = validate_pathspec(argv, prefix); - if (!pathspec) - return -1; - } + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_PREFIX_ORIGIN, + prefix, argv); return run_add_interactive(NULL, patch ? "--patch" : NULL, - pathspec); + &pathspec); } static int edit_patch(int argc, const char **argv, const char *prefix) @@ -446,7 +406,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; int newfd; - const char **pathspec; + struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; @@ -527,14 +487,23 @@ int cmd_add(int argc, const char **argv, const char *prefix) fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } - pathspec = validate_pathspec(argv, prefix); if (read_cache() < 0) die(_("index file corrupt")); - treat_gitlinks(pathspec); + + /* + * Check the "pathspec '%s' did not match any files" block + * below before enabling new magic. + */ + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, + prefix, argv); if (add_new_files) { int baselen; + struct pathspec empty_pathspec; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); @@ -543,35 +512,49 @@ int cmd_add(int argc, const char **argv, const char *prefix) setup_standard_excludes(&dir); } + memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ - baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec); - if (pathspec) - seen = prune_directory(&dir, pathspec, baselen, + baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); + if (pathspec.nr) + seen = prune_directory(&dir, &pathspec, baselen, implicit_dot ? WARN_IMPLICIT_DOT : 0); } if (refresh_only) { - refresh(verbose, pathspec); + refresh(verbose, &pathspec); goto finish; } if (implicit_dot && prefix) refresh_cache(REFRESH_QUIET); - if (pathspec) { + if (pathspec.nr) { int i; if (!seen) - seen = find_pathspecs_matching_against_index(pathspec); - for (i = 0; pathspec[i]; i++) { - if (!seen[i] && pathspec[i][0] - && !file_exists(pathspec[i])) { + seen = find_pathspecs_matching_against_index(&pathspec); + + /* + * file_exists() assumes exact match + */ + GUARD_PATHSPEC(&pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ICASE); + + for (i = 0; i < pathspec.nr; i++) { + const char *path = pathspec.items[i].match; + if (!seen[i] && + ((pathspec.items[i].magic & + (PATHSPEC_GLOB | PATHSPEC_ICASE)) || + !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (is_excluded(&dir, pathspec[i], &dtype)) - dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); + if (is_excluded(&dir, path, &dtype)) + dir_add_ignored(&dir, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), - pathspec[i]); + pathspec.items[i].original); } } free(seen); @@ -587,10 +570,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) */ update_data.implicit_dot = prefix; update_data.implicit_dot_len = strlen(prefix); - pathspec = NULL; + free_pathspec(&pathspec); + memset(&pathspec, 0, sizeof(pathspec)); } update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; - update_files_in_cache(prefix, pathspec, &update_data); + update_files_in_cache(prefix, &pathspec, &update_data); exit_status |= !!update_data.add_errors; if (add_new_files) diff --git a/builtin/apply.c b/builtin/apply.c index 50912c928f..ef32e4f624 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4363,23 +4363,23 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), N_("remove <num> leading slashes from traditional diff paths"), 0, option_parse_p }, - OPT_BOOLEAN(0, "no-add", &no_add, + OPT_BOOL(0, "no-add", &no_add, N_("ignore additions made by the patch")), - OPT_BOOLEAN(0, "stat", &diffstat, + OPT_BOOL(0, "stat", &diffstat, N_("instead of applying the patch, output diffstat for the input")), OPT_NOOP_NOARG(0, "allow-binary-replacement"), OPT_NOOP_NOARG(0, "binary"), - OPT_BOOLEAN(0, "numstat", &numstat, + OPT_BOOL(0, "numstat", &numstat, N_("show number of added and deleted lines in decimal notation")), - OPT_BOOLEAN(0, "summary", &summary, + OPT_BOOL(0, "summary", &summary, N_("instead of applying the patch, output a summary for the input")), - OPT_BOOLEAN(0, "check", &check, + OPT_BOOL(0, "check", &check, N_("instead of applying the patch, see if the patch is applicable")), - OPT_BOOLEAN(0, "index", &check_index, + OPT_BOOL(0, "index", &check_index, N_("make sure the patch is applicable to the current index")), - OPT_BOOLEAN(0, "cached", &cached, + OPT_BOOL(0, "cached", &cached, N_("apply a patch without touching the working tree")), - OPT_BOOLEAN(0, "apply", &force_apply, + OPT_BOOL(0, "apply", &force_apply, N_("also apply the patch (use with --stat/--summary/--check)")), OPT_BOOL('3', "3way", &threeway, N_( "attempt three-way merge if a patch does not apply")), @@ -4399,13 +4399,13 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL, N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, - OPT_BOOLEAN('R', "reverse", &apply_in_reverse, + OPT_BOOL('R', "reverse", &apply_in_reverse, N_("apply the patch in reverse")), - OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero, + OPT_BOOL(0, "unidiff-zero", &unidiff_zero, N_("don't expect at least one line of context")), - OPT_BOOLEAN(0, "reject", &apply_with_reject, + OPT_BOOL(0, "reject", &apply_with_reject, N_("leave the rejected hunks in corresponding *.rej files")), - OPT_BOOLEAN(0, "allow-overlap", &allow_overlap, + OPT_BOOL(0, "allow-overlap", &allow_overlap, N_("allow overlapping hunks")), OPT__VERBOSE(&apply_verbosely, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index e3884e3bb6..3324229025 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -13,10 +13,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) int next_all = 0; int no_checkout = 0; struct option options[] = { - OPT_BOOLEAN(0, "next-all", &next_all, - N_("perform 'git bisect next'")), - OPT_BOOLEAN(0, "no-checkout", &no_checkout, - N_("update BISECT_HEAD instead of checking out the current commit")), + OPT_BOOL(0, "next-all", &next_all, + N_("perform 'git bisect next'")), + OPT_BOOL(0, "no-checkout", &no_checkout, + N_("update BISECT_HEAD instead of checking out the current commit")), OPT_END() }; diff --git a/builtin/blame.c b/builtin/blame.c index 079dcd3407..6da7233968 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -22,6 +22,7 @@ #include "utf8.h" #include "userdiff.h" #include "line-range.h" +#include "line-log.h" static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file"); @@ -408,7 +409,7 @@ static struct origin *find_origin(struct scoreboard *sb, paths[0] = origin->path; paths[1] = NULL; - diff_tree_setup_paths(paths, &diff_opts); + parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC, 0, "", paths); diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) @@ -458,7 +459,7 @@ static struct origin *find_origin(struct scoreboard *sb, } } diff_flush(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); if (porigin) { /* * Create a freestanding copy that is not part of @@ -486,15 +487,12 @@ static struct origin *find_rename(struct scoreboard *sb, struct origin *porigin = NULL; struct diff_options diff_opts; int i; - const char *paths[2]; diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; - paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); diff_setup_done(&diff_opts); if (is_null_sha1(origin->commit->object.sha1)) @@ -516,7 +514,7 @@ static struct origin *find_rename(struct scoreboard *sb, } } diff_flush(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); return porigin; } @@ -1064,7 +1062,6 @@ static int find_copy_in_parent(struct scoreboard *sb, int opt) { struct diff_options diff_opts; - const char *paths[1]; int i, j; int retval; struct blame_list *blame_list; @@ -1078,8 +1075,6 @@ static int find_copy_in_parent(struct scoreboard *sb, DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; @@ -1162,7 +1157,7 @@ static int find_copy_in_parent(struct scoreboard *sb, } reset_scanned_flag(sb); diff_flush(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); return retval; } @@ -1937,18 +1932,6 @@ static const char *add_prefix(const char *prefix, const char *path) return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } -/* - * Parsing of -L option - */ -static void prepare_blame_range(struct scoreboard *sb, - const char *bottomtop, - long lno, - long *bottom, long *top) -{ - if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path)) - usage(blame_usage); -} - static int git_blame_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "blame.showroot")) { @@ -2245,38 +2228,27 @@ static int blame_move_callback(const struct option *option, const char *arg, int return 0; } -static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset) -{ - const char **bottomtop = option->value; - if (!arg) - return -1; - if (*bottomtop) - die("More than one '-L n,m' option given"); - *bottomtop = arg; - return 0; -} - int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; const char *path; struct scoreboard sb; struct origin *o; - struct blame_entry *ent; - long dashdash_pos, bottom, top, lno; + struct blame_entry *ent = NULL; + long dashdash_pos, lno; const char *final_commit_name = NULL; enum object_type type; - static const char *bottomtop = NULL; + static struct string_list range_list; static int output_option = 0, opt = 0; static int show_stats = 0; static const char *revs_file = NULL; static const char *contents_from = NULL; static const struct option options[] = { - OPT_BOOLEAN(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), - OPT_BOOLEAN('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), - OPT_BOOLEAN(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), - OPT_BOOLEAN(0, "show-stats", &show_stats, N_("Show work cost statistics")), + OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), + OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), + OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), + OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")), OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE), OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME), OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER), @@ -2293,13 +2265,16 @@ int cmd_blame(int argc, const char **argv, const char *prefix) OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")), { OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback }, { OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback }, - OPT_CALLBACK('L', NULL, &bottomtop, N_("n,m"), N_("Process only line range n,m, counting from 1"), blame_bottomtop_callback), + OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")), OPT__ABBREV(&abbrev), OPT_END() }; struct parse_opt_ctx_t ctx; int cmd_is_annotate = !strcmp(argv[0], "annotate"); + struct range_set ranges; + unsigned int range_i; + long anchor; git_config(git_blame_config, NULL); init_revisions(&revs, NULL); @@ -2492,22 +2467,48 @@ parse_done: num_read_blob++; lno = prepare_lines(&sb); - bottom = top = 0; - if (bottomtop) - prepare_blame_range(&sb, bottomtop, lno, &bottom, &top); - if (bottom < 1) - bottom = 1; - if (top < 1) - top = lno; - bottom--; - if (lno < top || lno < bottom) - die("file %s has only %lu lines", path, lno); - - ent = xcalloc(1, sizeof(*ent)); - ent->lno = bottom; - ent->num_lines = top - bottom; - ent->suspect = o; - ent->s_lno = bottom; + if (lno && !range_list.nr) + string_list_append(&range_list, xstrdup("1")); + + anchor = 1; + range_set_init(&ranges, range_list.nr); + for (range_i = 0; range_i < range_list.nr; ++range_i) { + long bottom, top; + if (parse_range_arg(range_list.items[range_i].string, + nth_line_cb, &sb, lno, anchor, + &bottom, &top, sb.path)) + usage(blame_usage); + if (lno < top || ((lno || bottom) && lno < bottom)) + die("file %s has only %lu lines", path, lno); + if (bottom < 1) + bottom = 1; + if (top < 1) + top = lno; + bottom--; + range_set_append_unsafe(&ranges, bottom, top); + anchor = top + 1; + } + sort_and_merge_range_set(&ranges); + + for (range_i = ranges.nr; range_i > 0; --range_i) { + const struct range *r = &ranges.ranges[range_i - 1]; + long bottom = r->start; + long top = r->end; + struct blame_entry *next = ent; + ent = xcalloc(1, sizeof(*ent)); + ent->lno = bottom; + ent->num_lines = top - bottom; + ent->suspect = o; + ent->s_lno = bottom; + ent->next = next; + if (next) + next->prev = ent; + origin_incref(o); + } + origin_decref(o); + + range_set_release(&ranges); + string_list_clear(&range_list, 0); sb.ent = ent; sb.path = path; diff --git a/builtin/branch.c b/builtin/branch.c index 083689063f..0903763702 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -797,7 +797,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_SET_INT( 0, "set-upstream", &track, N_("change upstream info"), BRANCH_TRACK_OVERRIDE), OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"), - OPT_BOOLEAN(0, "unset-upstream", &unset_upstream, "Unset the upstream info"), + OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"), OPT__COLOR(&branch_use_color, N_("use colored output")), OPT_SET_INT('r', "remotes", &kinds, N_("act on remote-tracking branches"), REF_REMOTE_BRANCH), @@ -822,10 +822,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2), - OPT_BOOLEAN(0, "list", &list, N_("list branch names")), - OPT_BOOLEAN('l', "create-reflog", &reflog, N_("create the branch's reflog")), - OPT_BOOLEAN(0, "edit-description", &edit_description, - N_("edit the description for the branch")), + OPT_BOOL(0, "list", &list, N_("list branch names")), + OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")), + OPT_BOOL(0, "edit-description", &edit_description, + N_("edit the description for the branch")), OPT__FORCE(&force_create, N_("force creation (when already exists)")), { OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, @@ -872,7 +872,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (with_commit || merge_filter != NO_FILTER) list = 1; - if (!!delete + !!rename + !!force_create + !!list + !!new_upstream + !!unset_upstream > 1) + if (!!delete + !!rename + !!force_create + !!new_upstream + + list + unset_upstream > 1) usage_with_options(builtin_branch_usage, options); if (abbrev == -1) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 425346048b..41afaa534b 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -119,6 +119,7 @@ struct expand_data { enum object_type type; unsigned long size; unsigned long disk_size; + const char *rest; /* * If mark_query is true, we do not expand anything, but rather @@ -127,6 +128,13 @@ struct expand_data { int mark_query; /* + * Whether to split the input on whitespace before feeding it to + * get_sha1; this is decided during the mark_query phase based on + * whether we have a %(rest) token in our format. + */ + int split_on_whitespace; + + /* * After a mark_query run, this object_info is set up to be * passed to sha1_object_info_extended. It will point to the data * elements above, so you can retrieve the response from there. @@ -163,6 +171,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, data->info.disk_sizep = &data->disk_size; else strbuf_addf(sb, "%lu", data->disk_size); + } else if (is_atom("rest", atom, len)) { + if (data->mark_query) + data->split_on_whitespace = 1; + else if (data->rest) + strbuf_addstr(sb, data->rest); } else die("unknown format element: %.*s", len, atom); } @@ -273,7 +286,23 @@ static int batch_objects(struct batch_options *opt) warn_on_object_refname_ambiguity = 0; while (strbuf_getline(&buf, stdin, '\n') != EOF) { - int error = batch_one_object(buf.buf, opt, &data); + int error; + + if (data.split_on_whitespace) { + /* + * Split at first whitespace, tying off the beginning + * of the string and saving the remainder (or NULL) in + * data.rest. + */ + char *p = strpbrk(buf.buf, " \t"); + if (p) { + while (*p && strchr(" \t", *p)) + *p++ = '\0'; + } + data.rest = p; + } + + error = batch_one_object(buf.buf, opt, &data); if (error) return error; } diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 075d01d30c..e9af7b2bfb 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -13,14 +13,14 @@ N_("git check-attr --stdin [-z] [-a | --all | attr...] < <list-of-paths>"), NULL }; -static int null_term_line; +static int nul_term_line; static const struct option check_attr_options[] = { - OPT_BOOLEAN('a', "all", &all_attrs, N_("report all attributes set on file")), - OPT_BOOLEAN(0, "cached", &cached_attrs, N_("use .gitattributes only from the index")), - OPT_BOOLEAN(0 , "stdin", &stdin_paths, N_("read file names from stdin")), - OPT_BOOLEAN('z', NULL, &null_term_line, - N_("input paths are terminated by a null character")), + OPT_BOOL('a', "all", &all_attrs, N_("report all attributes set on file")), + OPT_BOOL(0, "cached", &cached_attrs, N_("use .gitattributes only from the index")), + OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")), + OPT_BOOL('z', NULL, &nul_term_line, + N_("terminate input and output records by a NUL character")), OPT_END() }; @@ -38,8 +38,16 @@ static void output_attr(int cnt, struct git_attr_check *check, else if (ATTR_UNSET(value)) value = "unspecified"; - quote_c_style(file, NULL, stdout, 0); - printf(": %s: %s\n", git_attr_name(check[j].attr), value); + if (nul_term_line) { + printf("%s%c" /* path */ + "%s%c" /* attrname */ + "%s%c" /* attrvalue */, + file, 0, git_attr_name(check[j].attr), 0, value, 0); + } else { + quote_c_style(file, NULL, stdout, 0); + printf(": %s: %s\n", git_attr_name(check[j].attr), value); + } + } } @@ -65,7 +73,7 @@ static void check_attr_stdin_paths(const char *prefix, int cnt, struct git_attr_check *check) { struct strbuf buf, nbuf; - int line_termination = null_term_line ? 0 : '\n'; + int line_termination = nul_term_line ? 0 : '\n'; strbuf_init(&buf, 0); strbuf_init(&nbuf, 0); diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 4a8fc707c7..e2a1cef684 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -12,18 +12,18 @@ static const char * const check_ignore_usage[] = { NULL }; -static int null_term_line; +static int nul_term_line; static const struct option check_ignore_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), OPT__VERBOSE(&verbose, N_("be verbose")), OPT_GROUP(""), - OPT_BOOLEAN(0, "stdin", &stdin_paths, - N_("read file names from stdin")), - OPT_BOOLEAN('z', NULL, &null_term_line, - N_("input paths are terminated by a null character")), - OPT_BOOLEAN('n', "non-matching", &show_non_matching, - N_("show non-matching input paths")), + OPT_BOOL(0, "stdin", &stdin_paths, + N_("read file names from stdin")), + OPT_BOOL('z', NULL, &nul_term_line, + N_("terminate input and output records by a NUL character")), + OPT_BOOL('n', "non-matching", &show_non_matching, + N_("show non-matching input paths")), OPT_END() }; @@ -31,7 +31,7 @@ static void output_exclude(const char *path, struct exclude *exclude) { char *bang = (exclude && exclude->flags & EXC_FLAG_NEGATIVE) ? "!" : ""; char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : ""; - if (!null_term_line) { + if (!nul_term_line) { if (!verbose) { write_name_quoted(path, stdout, '\n'); } else { @@ -64,37 +64,45 @@ static void output_exclude(const char *path, struct exclude *exclude) } static int check_ignore(struct dir_struct *dir, - const char *prefix, const char **pathspec) + const char *prefix, int argc, const char **argv) { - const char *path, *full_path; + const char *full_path; char *seen; int num_ignored = 0, dtype = DT_UNKNOWN, i; struct exclude *exclude; + struct pathspec pathspec; - if (!pathspec || !*pathspec) { + if (!argc) { if (!quiet) fprintf(stderr, "no pathspec given.\n"); return 0; } /* + * check-ignore just needs paths. Magic beyond :/ is really + * irrelevant. + */ + parse_pathspec(&pathspec, + PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE | + PATHSPEC_KEEP_ORDER, + prefix, argv); + + /* * look for pathspecs matching entries in the index, since these * should not be ignored, in order to be consistent with * 'git status', 'git add' etc. */ - seen = find_pathspecs_matching_against_index(pathspec); - for (i = 0; pathspec[i]; i++) { - path = pathspec[i]; - full_path = prefix_path(prefix, prefix - ? strlen(prefix) : 0, path); - full_path = check_path_for_gitlink(full_path); - die_if_path_beyond_symlink(full_path, prefix); + seen = find_pathspecs_matching_against_index(&pathspec); + for (i = 0; i < pathspec.nr; i++) { + full_path = pathspec.items[i].match; exclude = NULL; if (!seen[i]) { exclude = last_exclude_matching(dir, full_path, &dtype); } if (!quiet && (exclude || show_non_matching)) - output_exclude(path, exclude); + output_exclude(pathspec.items[i].original, exclude); if (exclude) num_ignored++; } @@ -107,7 +115,7 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix) { struct strbuf buf, nbuf; char *pathspec[2] = { NULL, NULL }; - int line_termination = null_term_line ? 0 : '\n'; + int line_termination = nul_term_line ? 0 : '\n'; int num_ignored = 0; strbuf_init(&buf, 0); @@ -120,7 +128,8 @@ static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix) strbuf_swap(&buf, &nbuf); } pathspec[0] = buf.buf; - num_ignored += check_ignore(dir, prefix, (const char **)pathspec); + num_ignored += check_ignore(dir, prefix, + 1, (const char **)pathspec); maybe_flush_or_die(stdout, "check-ignore to stdout"); } strbuf_release(&buf); @@ -142,7 +151,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) if (argc > 0) die(_("cannot specify pathnames with --stdin")); } else { - if (null_term_line) + if (nul_term_line) die(_("-z only makes sense with --stdin")); if (argc == 0) die(_("no path specified")); @@ -166,7 +175,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) if (stdin_paths) { num_ignored = check_ignore_stdin_paths(&dir, prefix); } else { - num_ignored = check_ignore(&dir, prefix, argv); + num_ignored = check_ignore(&dir, prefix, argc, argv); maybe_flush_or_die(stdout, "ignore to stdout"); } diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index b1feda7d5e..69e167b16c 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -183,12 +183,12 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) int prefix_length; int force = 0, quiet = 0, not_new = 0; struct option builtin_checkout_index_options[] = { - OPT_BOOLEAN('a', "all", &all, + OPT_BOOL('a', "all", &all, N_("check out all files in the index")), OPT__FORCE(&force, N_("force overwrite of existing files")), OPT__QUIET(&quiet, N_("no warning for existing files and files not in index")), - OPT_BOOLEAN('n', "no-create", ¬_new, + OPT_BOOL('n', "no-create", ¬_new, N_("don't checkout new files")), { OPTION_CALLBACK, 'u', "index", &newfd, NULL, N_("update stat information in the index file"), @@ -196,9 +196,9 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 'z', NULL, NULL, NULL, N_("paths are separated with NUL character"), PARSE_OPT_NOARG, option_parse_z }, - OPT_BOOLEAN(0, "stdin", &read_from_stdin, + OPT_BOOL(0, "stdin", &read_from_stdin, N_("read list of paths from the standard input")), - OPT_BOOLEAN(0, "temp", &to_tempfile, + OPT_BOOL(0, "temp", &to_tempfile, N_("write the content to temporary files")), OPT_CALLBACK(0, "prefix", NULL, N_("string"), N_("when creating files, prepend <string>"), diff --git a/builtin/checkout.c b/builtin/checkout.c index 7025938ae3..0f57397037 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -46,7 +46,7 @@ struct checkout_opts { int branch_exists; const char *prefix; - const char **pathspec; + struct pathspec pathspec; struct tree *source_tree; }; @@ -83,12 +83,9 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, return 0; } -static int read_tree_some(struct tree *tree, const char **pathspec) +static int read_tree_some(struct tree *tree, const struct pathspec *pathspec) { - struct pathspec ps; - init_pathspec(&ps, pathspec); - read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL); - free_pathspec(&ps); + read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL); /* update the index with the given tree's info * for all args, expanding wildcards, and exit @@ -228,8 +225,6 @@ static int checkout_paths(const struct checkout_opts *opts, int flag; struct commit *head; int errs = 0; - int stage = opts->writeout_stage; - int merge = opts->merge; int newfd; struct lock_file *lock_file; @@ -257,20 +252,18 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) return run_add_interactive(revision, "--patch=checkout", - opts->pathspec); + &opts->pathspec); lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 1); - if (read_cache_preload(opts->pathspec) < 0) + if (read_cache_preload(&opts->pathspec) < 0) return error(_("corrupt index file")); if (opts->source_tree) - read_tree_some(opts->source_tree, opts->pathspec); + read_tree_some(opts->source_tree, &opts->pathspec); - for (pos = 0; opts->pathspec[pos]; pos++) - ; - ps_matched = xcalloc(1, pos); + ps_matched = xcalloc(1, opts->pathspec.nr); /* * Make sure all pathspecs participated in locating the paths @@ -304,12 +297,12 @@ static int checkout_paths(const struct checkout_opts *opts, * match_pathspec() for _all_ entries when * opts->source_tree != NULL. */ - if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), + if (match_pathspec_depth(&opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched)) ce->ce_flags |= CE_MATCHED; } - if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) { + if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) { free(ps_matched); return 1; } @@ -327,8 +320,8 @@ static int checkout_paths(const struct checkout_opts *opts, continue; if (opts->force) { warning(_("path '%s' is unmerged"), ce->name); - } else if (stage) { - errs |= check_stage(stage, ce, pos); + } else if (opts->writeout_stage) { + errs |= check_stage(opts->writeout_stage, ce, pos); } else if (opts->merge) { errs |= check_stages((1<<2) | (1<<3), ce, pos); } else { @@ -352,9 +345,9 @@ static int checkout_paths(const struct checkout_opts *opts, errs |= checkout_entry(ce, &state, NULL); continue; } - if (stage) - errs |= checkout_stage(stage, ce, pos, &state); - else if (merge) + if (opts->writeout_stage) + errs |= checkout_stage(opts->writeout_stage, ce, pos, &state); + else if (opts->merge) errs |= checkout_merged(pos, &state); pos = skip_same_name(ce, pos) - 1; } @@ -1002,7 +995,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts) static int checkout_branch(struct checkout_opts *opts, struct branch_info *new) { - if (opts->pathspec) + if (opts->pathspec.nr) die(_("paths cannot be used with switching branches")); if (opts->patch_mode) @@ -1056,8 +1049,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) N_("create and checkout a new branch")), OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"), N_("create/reset and checkout a branch")), - OPT_BOOLEAN('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")), - OPT_BOOLEAN(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")), + OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")), + OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")), OPT_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"), BRANCH_TRACK_EXPLICIT), OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")), @@ -1066,16 +1059,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), 3), OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")), - OPT_BOOLEAN('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), - OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")), + OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), + OPT_BOOL(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")), OPT_STRING(0, "conflict", &conflict_style, N_("style"), N_("conflict style (merge or diff3)")), - OPT_BOOLEAN('p', "patch", &opts.patch_mode, N_("select hunks interactively")), + OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")), OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree, N_("do not limit pathspecs to sparse entries only")), - { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL, - N_("second guess 'git checkout no-such-branch'"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, + OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch, + N_("second guess 'git checkout no-such-branch'")), OPT_END(), }; @@ -1154,9 +1146,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) } if (argc) { - opts.pathspec = get_pathspec(prefix, argv); + parse_pathspec(&opts.pathspec, 0, + opts.patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0, + prefix, argv); - if (!opts.pathspec) + if (!opts.pathspec.nr) die(_("invalid path specification")); /* @@ -1188,7 +1182,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) strbuf_release(&buf); } - if (opts.patch_mode || opts.pathspec) + if (opts.patch_mode || opts.pathspec.nr) return checkout_paths(&opts, new.name); else return checkout_branch(&opts, &new); diff --git a/builtin/clean.c b/builtin/clean.c index 3c85e152e1..615cd57caf 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -15,6 +15,7 @@ #include "quote.h" #include "column.h" #include "color.h" +#include "pathspec.h" static int force = -1; /* unset */ static int interactive; @@ -863,24 +864,23 @@ int cmd_clean(int argc, const char **argv, const char *prefix) int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf abs_path = STRBUF_INIT; struct dir_struct dir; - static const char **pathspec; + struct pathspec pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct exclude_list *el; struct string_list_item *item; const char *qname; - char *seen = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("do not print names of files removed")), OPT__DRY_RUN(&dry_run, N_("dry run")), OPT__FORCE(&force, N_("force")), OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")), - OPT_BOOLEAN('d', NULL, &remove_directories, + OPT_BOOL('d', NULL, &remove_directories, N_("remove whole directories")), { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"), N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb }, - OPT_BOOLEAN('x', NULL, &ignored, N_("remove ignored files, too")), - OPT_BOOLEAN('X', NULL, &ignored_only, + OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")), + OPT_BOOL('X', NULL, &ignored_only, N_("remove only ignored files")), OPT_END() }; @@ -925,12 +925,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix) for (i = 0; i < exclude_list.nr; i++) add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1)); - pathspec = get_pathspec(prefix, argv); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, argv); - fill_directory(&dir, pathspec); - - if (pathspec) - seen = xmalloc(argc > 0 ? argc : 1); + fill_directory(&dir, &pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; @@ -961,11 +960,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (lstat(ent->name, &st)) die_errno("Cannot lstat '%s'", ent->name); - if (pathspec) { - memset(seen, 0, argc > 0 ? argc : 1); - matches = match_pathspec(pathspec, ent->name, len, - 0, seen); - } + if (pathspec.nr) + matches = match_pathspec_depth(&pathspec, ent->name, + len, 0, NULL); if (S_ISDIR(st.st_mode)) { if (remove_directories || (matches == MATCHED_EXACTLY)) { @@ -973,7 +970,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) string_list_append(&del_list, rel); } } else { - if (pathspec && !matches) + if (pathspec.nr && !matches) continue; rel = relative_path(ent->name, prefix, &buf); string_list_append(&del_list, rel); @@ -1019,7 +1016,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix) } strbuf_reset(&abs_path); } - free(seen); strbuf_release(&abs_path); strbuf_release(&buf); diff --git a/builtin/clone.c b/builtin/clone.c index 430307b298..ca3eb68d72 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -62,23 +62,22 @@ static struct option builtin_clone_options[] = { OPT__VERBOSITY(&option_verbosity), OPT_BOOL(0, "progress", &option_progress, N_("force progress reporting")), - OPT_BOOLEAN('n', "no-checkout", &option_no_checkout, - N_("don't create a checkout")), - OPT_BOOLEAN(0, "bare", &option_bare, N_("create a bare repository")), - { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL, - N_("create a bare repository"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, - OPT_BOOLEAN(0, "mirror", &option_mirror, - N_("create a mirror repository (implies bare)")), + OPT_BOOL('n', "no-checkout", &option_no_checkout, + N_("don't create a checkout")), + OPT_BOOL(0, "bare", &option_bare, N_("create a bare repository")), + OPT_HIDDEN_BOOL(0, "naked", &option_bare, + N_("create a bare repository")), + OPT_BOOL(0, "mirror", &option_mirror, + N_("create a mirror repository (implies bare)")), OPT_BOOL('l', "local", &option_local, N_("to clone from a local repository")), - OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks, + OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks, N_("don't use local hardlinks, always copy")), - OPT_BOOLEAN('s', "shared", &option_shared, + OPT_BOOL('s', "shared", &option_shared, N_("setup as shared repository")), - OPT_BOOLEAN(0, "recursive", &option_recursive, + OPT_BOOL(0, "recursive", &option_recursive, N_("initialize submodules in the clone")), - OPT_BOOLEAN(0, "recurse-submodules", &option_recursive, + OPT_BOOL(0, "recurse-submodules", &option_recursive, N_("initialize submodules in the clone")), OPT_STRING(0, "template", &option_template, N_("template-directory"), N_("directory from which templates will be used")), diff --git a/builtin/commit.c b/builtin/commit.c index 10acc53f80..80d886ab3a 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -202,17 +202,15 @@ static int commit_index_files(void) * and return the paths that match the given pattern in list. */ static int list_paths(struct string_list *list, const char *with_tree, - const char *prefix, const char **pattern) + const char *prefix, const struct pathspec *pattern) { int i; char *m; - if (!pattern) + if (!pattern->nr) return 0; - for (i = 0; pattern[i]; i++) - ; - m = xcalloc(1, i); + m = xcalloc(1, pattern->nr); if (with_tree) { char *max_prefix = common_prefix(pattern); @@ -226,7 +224,7 @@ static int list_paths(struct string_list *list, const char *with_tree, if (ce->ce_flags & CE_UPDATE) continue; - if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m)) + if (!match_pathspec_depth(pattern, ce->name, ce_namelen(ce), 0, m)) continue; item = string_list_insert(list, ce->name); if (ce_skip_worktree(ce)) @@ -298,17 +296,17 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, { int fd; struct string_list partial; - const char **pathspec = NULL; + struct pathspec pathspec; char *old_index_env = NULL; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, argv); - if (*argv) - pathspec = get_pathspec(prefix, argv); - - if (read_cache_preload(pathspec) < 0) + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); if (interactive) { @@ -350,9 +348,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ - if (all || (also && pathspec && *pathspec)) { + if (all || (also && pathspec.nr)) { fd = hold_locked_index(&index_lock, 1); - add_files_to_cache(also ? prefix : NULL, pathspec, 0); + add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || @@ -371,7 +369,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, * and create commit from the_index. * We still need to refresh the index here. */ - if (!only && (!pathspec || !*pathspec)) { + if (!only && !pathspec.nr) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { @@ -416,7 +414,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; - if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, pathspec)) + if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec)) exit(1); discard_cache(); @@ -1091,7 +1089,7 @@ static int parse_and_validate_options(int argc, const char *argv[], if (patch_interactive) interactive = 1; - if (!!also + !!only + !!all + !!interactive > 1) + if (also + only + all + interactive > 1) die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); if (argc == 0 && (also || (only && !amend))) die(_("No paths with --include/--only does not make sense.")); @@ -1228,14 +1226,14 @@ int cmd_status(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), - OPT_BOOLEAN('z', "null", &s.null_termination, - N_("terminate entries with NUL")), + OPT_BOOL('z', "null", &s.null_termination, + N_("terminate entries with NUL")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, - OPT_BOOLEAN(0, "ignored", &show_ignored_in_status, - N_("show ignored files")), + OPT_BOOL(0, "ignored", &show_ignored_in_status, + N_("show ignored files")), { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"), N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, @@ -1259,11 +1257,12 @@ int cmd_status(int argc, const char **argv, const char *prefix) handle_untracked_files_arg(&s); if (show_ignored_in_status) s.show_ignored_files = 1; - if (*argv) - s.pathspec = get_pathspec(prefix, argv); + parse_pathspec(&s.pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, argv); - read_cache_preload(s.pathspec); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL); + read_cache_preload(&s.pathspec); + refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) @@ -1434,24 +1433,24 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")), OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), - OPT_BOOLEAN(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), - OPT_BOOLEAN('s', "signoff", &signoff, N_("add Signed-off-by:")), + OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), + OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")), - OPT_BOOLEAN(0, "status", &include_status, N_("include status in commit message template")), + OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, /* end commit message options */ OPT_GROUP(N_("Commit contents options")), - OPT_BOOLEAN('a', "all", &all, N_("commit all changed files")), - OPT_BOOLEAN('i', "include", &also, N_("add specified files to index for commit")), - OPT_BOOLEAN(0, "interactive", &interactive, N_("interactively add files")), - OPT_BOOLEAN('p', "patch", &patch_interactive, N_("interactively add changes")), - OPT_BOOLEAN('o', "only", &only, N_("commit only specified files")), - OPT_BOOLEAN('n', "no-verify", &no_verify, N_("bypass pre-commit hook")), - OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")), + OPT_BOOL('a', "all", &all, N_("commit all changed files")), + OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), + OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), + OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), + OPT_BOOL('o', "only", &only, N_("commit only specified files")), + OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")), + OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), OPT_SET_INT(0, "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")), @@ -1460,19 +1459,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), - OPT_BOOLEAN('z', "null", &s.null_termination, - N_("terminate entries with NUL")), - OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")), - OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), + OPT_BOOL('z', "null", &s.null_termination, + N_("terminate entries with NUL")), + OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), + OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, /* end commit contents options */ - { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL, - N_("ok to record an empty change"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, - { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL, - N_("ok to record a change with an empty message"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, + OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, + N_("ok to record an empty change")), + OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message, + N_("ok to record a change with an empty message")), OPT_END() }; diff --git a/builtin/config.c b/builtin/config.c index 4010c4320a..20e89fe4e0 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -2,6 +2,7 @@ #include "cache.h" #include "color.h" #include "parse-options.h" +#include "urlmatch.h" static const char *const builtin_config_usage[] = { N_("git config [options]"), @@ -42,6 +43,7 @@ static int respect_includes = -1; #define ACTION_SET_ALL (1<<12) #define ACTION_GET_COLOR (1<<13) #define ACTION_GET_COLORBOOL (1<<14) +#define ACTION_GET_URLMATCH (1<<15) #define TYPE_BOOL (1<<0) #define TYPE_INT (1<<1) @@ -50,15 +52,16 @@ static int respect_includes = -1; static struct option builtin_config_options[] = { OPT_GROUP(N_("Config file location")), - OPT_BOOLEAN(0, "global", &use_global_config, N_("use global config file")), - OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")), - OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")), + OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), + OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), + OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")), OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP), + OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH), OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL), OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD), OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET), @@ -75,7 +78,7 @@ static struct option builtin_config_options[] = { OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH), OPT_GROUP(N_("Other")), - OPT_BOOLEAN('z', "null", &end_null, N_("terminate values with NUL byte")), + OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")), OPT_END(), }; @@ -102,25 +105,13 @@ struct strbuf_list { int alloc; }; -static int collect_config(const char *key_, const char *value_, void *cb) +static int format_config(struct strbuf *buf, const char *key_, const char *value_) { - struct strbuf_list *values = cb; - struct strbuf *buf; - char value[256]; - const char *vptr = value; int must_free_vptr = 0; int must_print_delim = 0; + char value[256]; + const char *vptr = value; - if (!use_key_regexp && strcmp(key_, key)) - return 0; - if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) - return 0; - if (regexp != NULL && - (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0))) - return 0; - - ALLOC_GROW(values->items, values->nr + 1, values->alloc); - buf = &values->items[values->nr++]; strbuf_init(buf, 0); if (show_keys) { @@ -128,7 +119,8 @@ static int collect_config(const char *key_, const char *value_, void *cb) must_print_delim = 1; } if (types == TYPE_INT) - sprintf(value, "%d", git_config_int(key_, value_?value_:"")); + sprintf(value, "%"PRId64, + git_config_int64(key_, value_ ? value_ : "")); else if (types == TYPE_BOOL) vptr = git_config_bool(key_, value_) ? "true" : "false"; else if (types == TYPE_BOOL_OR_INT) { @@ -156,15 +148,27 @@ static int collect_config(const char *key_, const char *value_, void *cb) strbuf_addch(buf, term); if (must_free_vptr) - /* If vptr must be freed, it's a pointer to a - * dynamically allocated buffer, it's safe to cast to - * const. - */ free((char *)vptr); - return 0; } +static int collect_config(const char *key_, const char *value_, void *cb) +{ + struct strbuf_list *values = cb; + + if (!use_key_regexp && strcmp(key_, key)) + return 0; + if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) + return 0; + if (regexp != NULL && + (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0))) + return 0; + + ALLOC_GROW(values->items, values->nr + 1, values->alloc); + + return format_config(&values->items[values->nr++], key_, value_); +} + static int get_value(const char *key_, const char *regex_) { int ret = CONFIG_GENERIC_ERROR; @@ -265,8 +269,8 @@ static char *normalize_value(const char *key, const char *value) else { normalized = xmalloc(64); if (types == TYPE_INT) { - int v = git_config_int(key, value); - sprintf(normalized, "%d", v); + int64_t v = git_config_int64(key, value); + sprintf(normalized, "%"PRId64, v); } else if (types == TYPE_BOOL) sprintf(normalized, "%s", @@ -364,6 +368,97 @@ static void check_blob_write(void) die("writing config blobs is not supported"); } +struct urlmatch_current_candidate_value { + char value_is_null; + struct strbuf value; +}; + +static int urlmatch_collect_fn(const char *var, const char *value, void *cb) +{ + struct string_list *values = cb; + struct string_list_item *item = string_list_insert(values, var); + struct urlmatch_current_candidate_value *matched = item->util; + + if (!matched) { + matched = xmalloc(sizeof(*matched)); + strbuf_init(&matched->value, 0); + item->util = matched; + } else { + strbuf_reset(&matched->value); + } + + if (value) { + strbuf_addstr(&matched->value, value); + matched->value_is_null = 0; + } else { + matched->value_is_null = 1; + } + return 0; +} + +static char *dup_downcase(const char *string) +{ + char *result; + size_t len, i; + + len = strlen(string); + result = xmalloc(len + 1); + for (i = 0; i < len; i++) + result[i] = tolower(string[i]); + result[i] = '\0'; + return result; +} + +static int get_urlmatch(const char *var, const char *url) +{ + char *section_tail; + struct string_list_item *item; + struct urlmatch_config config = { STRING_LIST_INIT_DUP }; + struct string_list values = STRING_LIST_INIT_DUP; + + config.collect_fn = urlmatch_collect_fn; + config.cascade_fn = NULL; + config.cb = &values; + + if (!url_normalize(url, &config.url)) + die("%s", config.url.err); + + config.section = dup_downcase(var); + section_tail = strchr(config.section, '.'); + if (section_tail) { + *section_tail = '\0'; + config.key = section_tail + 1; + show_keys = 0; + } else { + config.key = NULL; + show_keys = 1; + } + + git_config_with_options(urlmatch_config_entry, &config, + given_config_file, NULL, respect_includes); + + for_each_string_list_item(item, &values) { + struct urlmatch_current_candidate_value *matched = item->util; + struct strbuf key = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + + strbuf_addstr(&key, item->string); + format_config(&buf, key.buf, + matched->value_is_null ? NULL : matched->value.buf); + fwrite(buf.buf, 1, buf.len, stdout); + strbuf_release(&key); + strbuf_release(&buf); + + strbuf_release(&matched->value); + } + string_list_clear(&config.vars, 1); + string_list_clear(&values, 1); + free(config.url.url); + + free((void *)config.section); + return 0; +} + int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = !startup_info->have_repository; @@ -523,6 +618,10 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 1, 2); return get_value(argv[0], argv[1]); } + else if (actions == ACTION_GET_URLMATCH) { + check_argc(argc, 2, 2); + return get_urlmatch(argv[0], argv[1]); + } else if (actions == ACTION_UNSET) { check_blob_write(); check_argc(argc, 1, 2); diff --git a/builtin/describe.c b/builtin/describe.c index 7d73722f59..c94e5c30d0 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -406,12 +406,12 @@ int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; struct option options[] = { - OPT_BOOLEAN(0, "contains", &contains, N_("find the tag that comes after the commit")), - OPT_BOOLEAN(0, "debug", &debug, N_("debug search strategy on stderr")), - OPT_BOOLEAN(0, "all", &all, N_("use any ref")), - OPT_BOOLEAN(0, "tags", &tags, N_("use any tag, even unannotated")), - OPT_BOOLEAN(0, "long", &longformat, N_("always use long format")), - OPT_BOOLEAN(0, "first-parent", &first_parent, N_("only follow first parent")), + OPT_BOOL(0, "contains", &contains, N_("find the tag that comes after the commit")), + OPT_BOOL(0, "debug", &debug, N_("debug search strategy on stderr")), + OPT_BOOL(0, "all", &all, N_("use any ref")), + OPT_BOOL(0, "tags", &tags, N_("use any tag, even unannotated")), + OPT_BOOL(0, "long", &longformat, N_("always use long format")), + OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")), OPT__ABBREV(&abbrev), OPT_SET_INT(0, "exact-match", &max_candidates, N_("only output exact matches"), 0), @@ -419,11 +419,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix) N_("consider <n> most recent tags (default: 10)")), OPT_STRING(0, "match", &pattern, N_("pattern"), N_("only consider tags matching <pattern>")), - OPT_BOOLEAN(0, "always", &always, - N_("show abbreviated commit object as fallback")), + OPT_BOOL(0, "always", &always, + N_("show abbreviated commit object as fallback")), {OPTION_STRING, 0, "dirty", &dirty, N_("mark"), - N_("append <mark> on dirty working tree (default: \"-dirty\")"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"}, + N_("append <mark> on dirty working tree (default: \"-dirty\")"), + PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"}, OPT_END(), }; diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 46085f862f..9200069363 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; - if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 1c737f7921..ce15b23042 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -43,7 +43,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) usage(diff_cache_usage); if (!cached) { setup_work_tree(); - if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/diff.c b/builtin/diff.c index 9fc273d8cd..2fb8c5dc0b 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -140,7 +140,7 @@ static int builtin_diff_index(struct rev_info *revs, usage(builtin_diff_usage); if (!cached) { setup_work_tree(); - if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&revs->diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } @@ -242,7 +242,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); - if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { + if (read_cache_preload(&revs->diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } @@ -367,6 +367,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } } if (rev.prune_data.nr) { + /* builtin_diff_b_f() */ + GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); if (!path) path = rev.prune_data.items[0].match; paths += rev.prune_data.nr; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 8e19058744..b1b9b5e52a 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -674,11 +674,11 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) N_("Dump marks to this file")), OPT_STRING(0, "import-marks", &import_filename, N_("file"), N_("Import marks from this file")), - OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger, - N_("Fake a tagger when tags lack one")), - OPT_BOOLEAN(0, "full-tree", &full_tree, - N_("Output full tree for each commit")), - OPT_BOOLEAN(0, "use-done-feature", &use_done_feature, + OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger, + N_("Fake a tagger when tags lack one")), + OPT_BOOL(0, "full-tree", &full_tree, + N_("Output full tree for each commit")), + OPT_BOOL(0, "use-done-feature", &use_done_feature, N_("Use the done feature to terminate the stream")), OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")), OPT_END() diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index aba4465552..c8e858232a 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -1,6 +1,8 @@ #include "builtin.h" #include "pkt-line.h" #include "fetch-pack.h" +#include "remote.h" +#include "connect.h" static const char fetch_pack_usage[] = "git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] " @@ -100,6 +102,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) pack_lockfile_ptr = &pack_lockfile; continue; } + if (!strcmp("--check-self-contained-and-connected", arg)) { + args.check_self_contained_and_connected = 1; + continue; + } usage(fetch_pack_usage); } @@ -152,6 +158,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) printf("lock %s\n", pack_lockfile); fflush(stdout); } + if (args.check_self_contained_and_connected && + args.self_contained_and_connected) { + printf("connectivity-ok\n"); + fflush(stdout); + } close(fd[0]); close(fd[1]); if (finish_connect(conn)) diff --git a/builtin/fetch.c b/builtin/fetch.c index d784b2e694..9e654efa3b 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -30,13 +30,18 @@ enum { TAGS_SET = 2 }; -static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity; +static int fetch_prune_config = -1; /* unspecified */ +static int prune = -1; /* unspecified */ +#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ + +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int tags = TAGS_DEFAULT, unshallow; static const char *depth; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; -static struct transport *transport; +static struct transport *gtransport; +static struct transport *gsecondary; static const char *submodule_prefix = ""; static const char *recurse_submodules_default; @@ -54,30 +59,39 @@ static int option_parse_recurse_submodules(const struct option *opt, return 0; } +static int git_fetch_config(const char *k, const char *v, void *cb) +{ + if (!strcmp(k, "fetch.prune")) { + fetch_prune_config = git_config_bool(k, v); + return 0; + } + return 0; +} + static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), - OPT_BOOLEAN(0, "all", &all, - N_("fetch from all remotes")), - OPT_BOOLEAN('a', "append", &append, - N_("append to .git/FETCH_HEAD instead of overwriting")), + OPT_BOOL(0, "all", &all, + N_("fetch from all remotes")), + OPT_BOOL('a', "append", &append, + N_("append to .git/FETCH_HEAD instead of overwriting")), OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), N_("path to upload pack on remote end")), OPT__FORCE(&force, N_("force overwrite of local branch")), - OPT_BOOLEAN('m', "multiple", &multiple, - N_("fetch from multiple remotes")), + OPT_BOOL('m', "multiple", &multiple, + N_("fetch from multiple remotes")), OPT_SET_INT('t', "tags", &tags, N_("fetch all tags and associated objects"), TAGS_SET), OPT_SET_INT('n', NULL, &tags, N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), - OPT_BOOLEAN('p', "prune", &prune, - N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('p', "prune", &prune, + N_("prune remote-tracking branches no longer on remote")), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, - OPT_BOOLEAN(0, "dry-run", &dry_run, - N_("dry run")), - OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")), - OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, + OPT_BOOL(0, "dry-run", &dry_run, + N_("dry run")), + OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), + OPT_BOOL('u', "update-head-ok", &update_head_ok, N_("allow updating of HEAD ref")), OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), OPT_STRING(0, "depth", &depth, N_("depth"), @@ -95,8 +109,10 @@ static struct option builtin_fetch_options[] = { static void unlock_pack(void) { - if (transport) - transport_unlock_pack(transport); + if (gtransport) + transport_unlock_pack(gtransport); + if (gsecondary) + transport_unlock_pack(gsecondary); } static void unlock_pack_on_signal(int signo) @@ -720,6 +736,48 @@ static int truncate_fetch_head(void) return 0; } +static void set_option(struct transport *transport, const char *name, const char *value) +{ + int r = transport_set_option(transport, name, value); + if (r < 0) + die(_("Option \"%s\" value \"%s\" is not valid for %s"), + name, value, transport->url); + if (r > 0) + warning(_("Option \"%s\" is ignored for %s\n"), + name, transport->url); +} + +static struct transport *prepare_transport(struct remote *remote) +{ + struct transport *transport; + transport = transport_get(remote, NULL); + transport_set_verbosity(transport, verbosity, progress); + if (upload_pack) + set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack); + if (keep) + set_option(transport, TRANS_OPT_KEEP, "yes"); + if (depth) + set_option(transport, TRANS_OPT_DEPTH, depth); + return transport; +} + +static void backfill_tags(struct transport *transport, struct ref *ref_map) +{ + if (transport->cannot_reuse) { + gsecondary = prepare_transport(transport->remote); + transport = gsecondary; + } + + transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); + transport_set_option(transport, TRANS_OPT_DEPTH, "0"); + fetch_refs(transport, ref_map); + + if (gsecondary) { + transport_disconnect(gsecondary); + gsecondary = NULL; + } +} + static int do_fetch(struct transport *transport, struct refspec *refs, int ref_count) { @@ -771,7 +829,10 @@ static int do_fetch(struct transport *transport, goto cleanup; } if (prune) { - /* If --tags was specified, pretend the user gave us the canonical tags refspec */ + /* + * If --tags was specified, pretend that the user gave us + * the canonical tags refspec + */ if (tags == TAGS_SET) { const char *tags_str = "refs/tags/*:refs/tags/*"; struct refspec *tags_refspec, *refspec; @@ -803,11 +864,8 @@ static int do_fetch(struct transport *transport, struct ref **tail = &ref_map; ref_map = NULL; find_non_local_tags(transport, &ref_map, &tail); - if (ref_map) { - transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); - transport_set_option(transport, TRANS_OPT_DEPTH, "0"); - fetch_refs(transport, ref_map); - } + if (ref_map) + backfill_tags(transport, ref_map); free_refs(ref_map); } @@ -816,17 +874,6 @@ static int do_fetch(struct transport *transport, return retcode; } -static void set_option(const char *name, const char *value) -{ - int r = transport_set_option(transport, name, value); - if (r < 0) - die(_("Option \"%s\" value \"%s\" is not valid for %s"), - name, value, transport->url); - if (r > 0) - warning(_("Option \"%s\" is ignored for %s\n"), - name, transport->url); -} - static int get_one_remote_for_fetch(struct remote *remote, void *priv) { struct string_list *list = priv; @@ -882,7 +929,7 @@ static void add_options_to_argv(struct argv_array *argv) { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune) + if (prune > 0) argv_array_push(argv, "--prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); @@ -949,14 +996,17 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) die(_("No remote repository specified. Please, specify either a URL or a\n" "remote name from which new revisions should be fetched.")); - transport = transport_get(remote, NULL); - transport_set_verbosity(transport, verbosity, progress); - if (upload_pack) - set_option(TRANS_OPT_UPLOADPACK, upload_pack); - if (keep) - set_option(TRANS_OPT_KEEP, "yes"); - if (depth) - set_option(TRANS_OPT_DEPTH, depth); + gtransport = prepare_transport(remote); + + if (prune < 0) { + /* no command line request */ + if (0 <= gtransport->remote->prune) + prune = gtransport->remote->prune; + else if (0 <= fetch_prune_config) + prune = fetch_prune_config; + else + prune = PRUNE_BY_DEFAULT; + } if (argc > 0) { int j = 0; @@ -983,10 +1033,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); refspec = parse_fetch_refspec(ref_nr, refs); - exit_code = do_fetch(transport, refspec, ref_nr); + exit_code = do_fetch(gtransport, refspec, ref_nr); free_refspec(ref_nr, refspec); - transport_disconnect(transport); - transport = NULL; + transport_disconnect(gtransport); + gtransport = NULL; return exit_code; } @@ -1007,6 +1057,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; i++) strbuf_addf(&default_rla, " %s", argv[i]); + git_config(git_fetch_config, NULL); + argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 7f059c31df..1d4083c2dd 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -867,24 +867,29 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs static void print_value(struct refinfo *ref, int atom, int quote_style) { struct atom_value *v; + struct strbuf sb = STRBUF_INIT; get_value(ref, atom, &v); switch (quote_style) { case QUOTE_NONE: fputs(v->s, stdout); break; case QUOTE_SHELL: - sq_quote_print(stdout, v->s); + sq_quote_buf(&sb, v->s); break; case QUOTE_PERL: - perl_quote_print(stdout, v->s); + perl_quote_buf(&sb, v->s); break; case QUOTE_PYTHON: - python_quote_print(stdout, v->s); + python_quote_buf(&sb, v->s); break; case QUOTE_TCL: - tcl_quote_print(stdout, v->s); + tcl_quote_buf(&sb, v->s); break; } + if (quote_style != QUOTE_NONE) { + fputs(sb.buf, stdout); + strbuf_release(&sb); + } } static int hex1(char ch) diff --git a/builtin/fsck.c b/builtin/fsck.c index 9909b6d519..39fa5e86d4 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -611,15 +611,15 @@ static char const * const fsck_usage[] = { static struct option fsck_opts[] = { OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_BOOLEAN(0, "unreachable", &show_unreachable, N_("show unreachable objects")), + OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")), OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")), - OPT_BOOLEAN(0, "tags", &show_tags, N_("report tags")), - OPT_BOOLEAN(0, "root", &show_root, N_("report root nodes")), - OPT_BOOLEAN(0, "cache", &keep_cache_objects, N_("make index objects head nodes")), - OPT_BOOLEAN(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")), - OPT_BOOLEAN(0, "full", &check_full, N_("also consider packs and alternate objects")), - OPT_BOOLEAN(0, "strict", &check_strict, N_("enable more strict checking")), - OPT_BOOLEAN(0, "lost-found", &write_lost_and_found, + OPT_BOOL(0, "tags", &show_tags, N_("report tags")), + OPT_BOOL(0, "root", &show_root, N_("report root nodes")), + OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")), + OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")), + OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")), + OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")), + OPT_BOOL(0, "lost-found", &write_lost_and_found, N_("write dangling objects in .git/lost-found")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_END(), diff --git a/builtin/gc.c b/builtin/gc.c index 6be6c8d65b..891a2c2ecb 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -167,19 +167,78 @@ static int need_to_gc(void) return 1; } +/* return NULL on success, else hostname running the gc */ +static const char *lock_repo_for_gc(int force, pid_t* ret_pid) +{ + static struct lock_file lock; + static char locking_host[128]; + char my_host[128]; + struct strbuf sb = STRBUF_INIT; + struct stat st; + uintmax_t pid; + FILE *fp; + int fd, should_exit; + + if (gethostname(my_host, sizeof(my_host))) + strcpy(my_host, "unknown"); + + fd = hold_lock_file_for_update(&lock, git_path("gc.pid"), + LOCK_DIE_ON_ERROR); + if (!force) { + fp = fopen(git_path("gc.pid"), "r"); + memset(locking_host, 0, sizeof(locking_host)); + should_exit = + fp != NULL && + !fstat(fileno(fp), &st) && + /* + * 12 hour limit is very generous as gc should + * never take that long. On the other hand we + * don't really need a strict limit here, + * running gc --auto one day late is not a big + * problem. --force can be used in manual gc + * after the user verifies that no gc is + * running. + */ + time(NULL) - st.st_mtime <= 12 * 3600 && + fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 && + /* be gentle to concurrent "gc" on remote hosts */ + (strcmp(locking_host, my_host) || !kill(pid, 0)); + if (fp != NULL) + fclose(fp); + if (should_exit) { + if (fd >= 0) + rollback_lock_file(&lock); + *ret_pid = pid; + return locking_host; + } + } + + strbuf_addf(&sb, "%"PRIuMAX" %s", + (uintmax_t) getpid(), my_host); + write_in_full(fd, sb.buf, sb.len); + strbuf_release(&sb); + commit_lock_file(&lock); + + return NULL; +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int aggressive = 0; int auto_gc = 0; int quiet = 0; + int force = 0; + const char *name; + pid_t pid; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), { OPTION_STRING, 0, "prune", &prune_expire, N_("date"), N_("prune unreferenced objects"), PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, - OPT_BOOLEAN(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), - OPT_BOOLEAN(0, "auto", &auto_gc, N_("enable auto-gc mode")), + OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), + OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")), + OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")), OPT_END() }; @@ -225,6 +284,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix) } else add_repack_all_option(); + name = lock_repo_for_gc(force, &pid); + if (name) { + if (auto_gc) + return 0; /* be quiet on --auto */ + die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"), + name, (uintmax_t)pid); + } + if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD)) return error(FAILED_RUN, pack_refs_cmd.argv[0]); diff --git a/builtin/grep.c b/builtin/grep.c index d3b3b1db11..03bc442e3f 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -17,6 +17,7 @@ #include "grep.h" #include "quote.h" #include "dir.h" +#include "pathspec.h" static char const * const grep_usage[] = { N_("git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"), @@ -521,7 +522,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, if (exc_std) setup_standard_excludes(&dir); - fill_directory(&dir, pathspec->raw); + fill_directory(&dir, pathspec); for (i = 0; i < dir.nr; i++) { const char *name = dir.entries[i]->name; int namelen = strlen(name); @@ -629,7 +630,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) const char *show_in_pager = NULL, *default_pager = "dummy"; struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; - const char **paths = NULL; struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; @@ -638,20 +638,20 @@ int cmd_grep(int argc, const char **argv, const char *prefix) int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; struct option options[] = { - OPT_BOOLEAN(0, "cached", &cached, + OPT_BOOL(0, "cached", &cached, N_("search in index instead of in the work tree")), OPT_NEGBIT(0, "no-index", &use_index, N_("find in contents not managed by git"), 1), - OPT_BOOLEAN(0, "untracked", &untracked, + OPT_BOOL(0, "untracked", &untracked, N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, N_("search also in ignored files"), 1), OPT_GROUP(""), - OPT_BOOLEAN('v', "invert-match", &opt.invert, + OPT_BOOL('v', "invert-match", &opt.invert, N_("show non-matching lines")), - OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, + OPT_BOOL('i', "ignore-case", &opt.ignore_case, N_("case insensitive matching")), - OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, + OPT_BOOL('w', "word-regexp", &opt.word_regexp, N_("match patterns only at word boundaries")), OPT_SET_INT('a', "text", &opt.binary, N_("process binary files as text"), GREP_BINARY_TEXT), @@ -675,26 +675,26 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("use Perl-compatible regular expressions"), GREP_PATTERN_TYPE_PCRE), OPT_GROUP(""), - OPT_BOOLEAN('n', "line-number", &opt.linenum, N_("show line numbers")), + OPT_BOOL('n', "line-number", &opt.linenum, N_("show line numbers")), OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1), OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1), OPT_NEGBIT(0, "full-name", &opt.relative, N_("show filenames relative to top directory"), 1), - OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, + OPT_BOOL('l', "files-with-matches", &opt.name_only, N_("show only filenames instead of matching lines")), - OPT_BOOLEAN(0, "name-only", &opt.name_only, + OPT_BOOL(0, "name-only", &opt.name_only, N_("synonym for --files-with-matches")), - OPT_BOOLEAN('L', "files-without-match", + OPT_BOOL('L', "files-without-match", &opt.unmatch_name_only, N_("show only the names of files without match")), - OPT_BOOLEAN('z', "null", &opt.null_following_name, + OPT_BOOL('z', "null", &opt.null_following_name, N_("print NUL after filenames")), - OPT_BOOLEAN('c', "count", &opt.count, + OPT_BOOL('c', "count", &opt.count, N_("show the number of matches instead of matching lines")), OPT__COLOR(&opt.color, N_("highlight matches")), - OPT_BOOLEAN(0, "break", &opt.file_break, + OPT_BOOL(0, "break", &opt.file_break, N_("print empty line between matches from different files")), - OPT_BOOLEAN(0, "heading", &opt.heading, + OPT_BOOL(0, "heading", &opt.heading, N_("show filename only once above matches from same file")), OPT_GROUP(""), OPT_CALLBACK('C', "context", &opt, N_("n"), @@ -706,9 +706,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("show <n> context lines after matches")), OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), - OPT_BOOLEAN('p', "show-function", &opt.funcname, + OPT_BOOL('p', "show-function", &opt.funcname, N_("show a line with the function name before matches")), - OPT_BOOLEAN('W', "function-context", &opt.funcbody, + OPT_BOOL('W', "function-context", &opt.funcbody, N_("show the surrounding function")), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, N_("file"), @@ -718,7 +718,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "and", &opt, NULL, N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, - OPT_BOOLEAN(0, "or", &dummy, ""), + OPT_BOOL(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", @@ -729,7 +729,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) close_callback }, OPT__QUIET(&opt.status_only, N_("indicate hit with exit status without output")), - OPT_BOOLEAN(0, "all-match", &opt.all_match, + OPT_BOOL(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), { OPTION_SET_INT, 0, "debug", &opt.debug, NULL, N_("show parse tree for grep expression"), @@ -738,8 +738,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, - OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored, - N_("allow calling of grep(1) (ignored by this build)")), + OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored, + N_("allow calling of grep(1) (ignored by this build)")), { OPTION_CALLBACK, 0, "help-all", &options, NULL, N_("show usage"), PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() @@ -856,8 +856,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix) verify_filename(prefix, argv[j], j == i); } - paths = get_pathspec(prefix, argv + i); - init_pathspec(&pathspec, paths); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD | + (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), + prefix, argv + i); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 8d184f1a99..d7fcf4c13c 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -70,10 +70,10 @@ static const char *vpath; static const struct option hash_object_options[] = { OPT_STRING('t', NULL, &type, N_("type"), N_("object type")), - OPT_BOOLEAN('w', NULL, &write_object, N_("write the object into the object database")), - OPT_BOOLEAN( 0 , "stdin", &hashstdin, N_("read the object from stdin")), - OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")), - OPT_BOOLEAN( 0 , "no-filters", &no_filters, N_("store file as is without filters")), + OPT_BOOL('w', NULL, &write_object, N_("write the object into the object database")), + OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")), + OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")), + OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")), OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")), OPT_END() }; diff --git a/builtin/log.c b/builtin/log.c index 2625f9881a..77d0f5f3fd 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -121,7 +121,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP}; const struct option builtin_log_options[] = { - OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")), + OPT__QUIET(&quiet, N_("suppress diff output")), OPT_BOOL(0, "source", &source, N_("show source")), OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")), { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"), @@ -503,7 +503,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) init_grep_defaults(); git_config(git_log_config, NULL); - init_pathspec(&match_all, NULL); + memset(&match_all, 0, sizeof(match_all)); init_revisions(&rev, prefix); rev.diff = 1; rev.always_show_header = 1; @@ -1179,13 +1179,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, - OPT_BOOLEAN(0, "no-binary", &no_binary_diff, - N_("don't output binary diffs")), - OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, - N_("don't include a patch matching a commit upstream")), - { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL, + OPT_BOOL(0, "no-binary", &no_binary_diff, + N_("don't output binary diffs")), + OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream, + N_("don't include a patch matching a commit upstream")), + { OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL, N_("show patch format instead of default (patch + stat)"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG }, + PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1}, OPT_GROUP(N_("Messaging")), { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"), N_("add email header"), 0, header_callback }, @@ -1210,8 +1210,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, thread_callback }, OPT_STRING(0, "signature", &signature, N_("signature"), N_("add a signature")), - OPT_BOOLEAN(0, "quiet", &quiet, - N_("don't print the patch filenames")), + OPT__QUIET(&quiet, N_("don't print the patch filenames")), OPT_END() }; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 5cf3e31370..e1cf6d8547 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -13,6 +13,7 @@ #include "parse-options.h" #include "resolve-undo.h" #include "string-list.h" +#include "pathspec.h" static int abbrev; static int show_deleted; @@ -30,7 +31,7 @@ static int debug_mode; static const char *prefix; static int max_prefix_len; static int prefix_len; -static const char **pathspec; +static struct pathspec pathspec; static int error_unmatch; static char *ps_matched; static const char *with_tree; @@ -63,7 +64,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (len >= ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); - if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched)) + if (!match_pathspec_depth(&pathspec, ent->name, ent->len, len, ps_matched)) return; fputs(tag, stdout); @@ -138,7 +139,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git ls-files: internal error - cache entry not superset of prefix"); - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched)) + if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), len, ps_matched)) return; if (tag && *tag && show_valid_bit && @@ -194,7 +195,7 @@ static void show_ru_info(void) len = strlen(path); if (len < max_prefix_len) continue; /* outside of the prefix */ - if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched)) + if (!match_pathspec_depth(&pathspec, path, len, max_prefix_len, ps_matched)) continue; /* uninterested */ for (i = 0; i < 3; i++) { if (!ui->mode[i]) @@ -219,7 +220,9 @@ static void show_files(struct dir_struct *dir) /* For cached/deleted files we don't need to even do the readdir */ if (show_others || show_killed) { - fill_directory(dir, pathspec); + if (!show_others) + dir->flags |= DIR_COLLECT_KILLED_ONLY; + fill_directory(dir, &pathspec); if (show_others) show_other_files(dir); if (show_killed) @@ -287,21 +290,6 @@ static void prune_cache(const char *prefix) active_nr = last; } -static void strip_trailing_slash_from_submodules(void) -{ - const char **p; - - for (p = pathspec; *p != NULL; p++) { - int len = strlen(*p), pos; - - if (len < 1 || (*p)[len - 1] != '/') - continue; - pos = cache_name_pos(*p, len - 1); - if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode)) - *p = xstrndup(*p, len - 1); - } -} - /* * Read the tree specified with --with-tree option * (typically, HEAD) into stage #1 and then @@ -333,13 +321,12 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) } if (prefix) { - static const char *(matchbuf[2]); - matchbuf[0] = prefix; - matchbuf[1] = NULL; - init_pathspec(&pathspec, matchbuf); - pathspec.items[0].nowildcard_len = pathspec.items[0].len; + static const char *(matchbuf[1]); + matchbuf[0] = NULL; + parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, + PATHSPEC_PREFER_CWD, prefix, matchbuf); } else - init_pathspec(&pathspec, NULL); + memset(&pathspec, 0, sizeof(pathspec)); if (read_tree(tree, 1, &pathspec)) die("unable to read tree entries %s", tree_name); @@ -364,15 +351,16 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) } } -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix) +int report_path_error(const char *ps_matched, + const struct pathspec *pathspec, + const char *prefix) { /* * Make sure all pathspec matched; otherwise it is an error. */ struct strbuf sb = STRBUF_INIT; - const char *name; int num, errors = 0; - for (num = 0; pathspec[num]; num++) { + for (num = 0; num < pathspec->nr; num++) { int other, found_dup; if (ps_matched[num]) @@ -380,13 +368,16 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char /* * The caller might have fed identical pathspec * twice. Do not barf on such a mistake. + * FIXME: parse_pathspec should have eliminated + * duplicate pathspec. */ for (found_dup = other = 0; - !found_dup && pathspec[other]; + !found_dup && other < pathspec->nr; other++) { if (other == num || !ps_matched[other]) continue; - if (!strcmp(pathspec[other], pathspec[num])) + if (!strcmp(pathspec->items[other].original, + pathspec->items[num].original)) /* * Ok, we have a match already. */ @@ -395,9 +386,8 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char if (found_dup) continue; - name = quote_path_relative(pathspec[num], prefix, &sb); error("pathspec '%s' did not match any file(s) known to git.", - name); + pathspec->items[num].original); errors++; } strbuf_release(&sb); @@ -461,24 +451,24 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) { OPTION_CALLBACK, 'z', NULL, NULL, NULL, N_("paths are separated with NUL character"), PARSE_OPT_NOARG, option_parse_z }, - OPT_BOOLEAN('t', NULL, &show_tag, + OPT_BOOL('t', NULL, &show_tag, N_("identify the file status with tags")), - OPT_BOOLEAN('v', NULL, &show_valid_bit, + OPT_BOOL('v', NULL, &show_valid_bit, N_("use lowercase letters for 'assume unchanged' files")), - OPT_BOOLEAN('c', "cached", &show_cached, + OPT_BOOL('c', "cached", &show_cached, N_("show cached files in the output (default)")), - OPT_BOOLEAN('d', "deleted", &show_deleted, + OPT_BOOL('d', "deleted", &show_deleted, N_("show deleted files in the output")), - OPT_BOOLEAN('m', "modified", &show_modified, + OPT_BOOL('m', "modified", &show_modified, N_("show modified files in the output")), - OPT_BOOLEAN('o', "others", &show_others, + OPT_BOOL('o', "others", &show_others, N_("show other files in the output")), OPT_BIT('i', "ignored", &dir.flags, N_("show ignored files in the output"), DIR_SHOW_IGNORED), - OPT_BOOLEAN('s', "stage", &show_stage, + OPT_BOOL('s', "stage", &show_stage, N_("show staged contents' object name in the output")), - OPT_BOOLEAN('k', "killed", &show_killed, + OPT_BOOL('k', "killed", &show_killed, N_("show files on the filesystem that need to be removed")), OPT_BIT(0, "directory", &dir.flags, N_("show 'other' directories' name only"), @@ -486,9 +476,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) OPT_NEGBIT(0, "empty-directory", &dir.flags, N_("don't show empty directories"), DIR_HIDE_EMPTY_DIRECTORIES), - OPT_BOOLEAN('u', "unmerged", &show_unmerged, + OPT_BOOL('u', "unmerged", &show_unmerged, N_("show unmerged files in the output")), - OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo, + OPT_BOOL(0, "resolve-undo", &show_resolve_undo, N_("show resolve-undo information")), { OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"), N_("skip files matching pattern"), @@ -504,12 +494,12 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL, N_("make the output relative to the project top directory"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL }, - OPT_BOOLEAN(0, "error-unmatch", &error_unmatch, + OPT_BOOL(0, "error-unmatch", &error_unmatch, N_("if any <file> is not in the index, treat this as an error")), OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"), N_("pretend that paths removed since <tree-ish> are still present")), OPT__ABBREV(&abbrev), - OPT_BOOLEAN(0, "debug", &debug_mode, N_("show debugging data")), + OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), OPT_END() }; @@ -555,23 +545,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); - pathspec = get_pathspec(prefix, argv); - - /* be nice with submodule paths ending in a slash */ - if (pathspec) - strip_trailing_slash_from_submodules(); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD | + PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, + prefix, argv); /* Find common prefix for all pathspec's */ - max_prefix = common_prefix(pathspec); + max_prefix = common_prefix(&pathspec); max_prefix_len = max_prefix ? strlen(max_prefix) : 0; /* Treat unmatching pathspec elements as errors */ - if (pathspec && error_unmatch) { - int num; - for (num = 0; pathspec[num]; num++) - ; - ps_matched = xcalloc(1, num); - } + if (pathspec.nr && error_unmatch) + ps_matched = xcalloc(1, pathspec.nr); if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) die("ls-files --ignored needs some exclude pattern"); @@ -598,7 +583,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (ps_matched) { int bad; - bad = report_path_error(ps_matched, pathspec, prefix); + bad = report_path_error(ps_matched, &pathspec, prefix); if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index fb76e38d84..65ec931846 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -10,6 +10,7 @@ #include "quote.h" #include "builtin.h" #include "parse-options.h" +#include "pathspec.h" static int line_termination = '\n'; #define LS_RECURSIVE 1 @@ -35,7 +36,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname) if (ls_options & LS_RECURSIVE) return 1; - s = pathspec.raw; + s = pathspec._raw; if (!s) return 0; @@ -138,9 +139,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) LS_NAME_ONLY), OPT_SET_INT(0, "full-name", &chomp_prefix, N_("use full path names"), 0), - OPT_BOOLEAN(0, "full-tree", &full_tree, - N_("list entire tree; not just current directory " - "(implies --full-name)")), + OPT_BOOL(0, "full-tree", &full_tree, + N_("list entire tree; not just current directory " + "(implies --full-name)")), OPT__ABBREV(&abbrev), OPT_END() }; @@ -166,7 +167,15 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) if (get_sha1(argv[0], sha1)) die("Not a valid object name %s", argv[0]); - init_pathspec(&pathspec, get_pathspec(prefix, argv + 1)); + /* + * show_recursive() rolls its own matching code and is + * generally ignorant of 'struct pathspec'. The magic mask + * cannot be lifted until it is converted to use + * match_pathspec_depth() or tree_entry_interesting() + */ + parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE, + PATHSPEC_PREFER_CWD, + prefix, argv + 1); for (i = 0; i < pathspec.nr; i++) pathspec.items[i].nowildcard_len = pathspec.items[i].len; pathspec.has_wildcard = 0; diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 0c4cd2f9f7..e88eb93f14 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -95,11 +95,11 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) int is_ancestor = 0; struct option options[] = { - OPT_BOOLEAN('a', "all", &show_all, N_("output all common ancestors")), - OPT_BOOLEAN(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")), - OPT_BOOLEAN(0, "independent", &reduce, N_("list revs not reachable from others")), - OPT_BOOLEAN(0, "is-ancestor", &is_ancestor, - N_("is the first one ancestor of the other?")), + OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")), + OPT_BOOL(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")), + OPT_BOOL(0, "independent", &reduce, N_("list revs not reachable from others")), + OPT_BOOL(0, "is-ancestor", &is_ancestor, + N_("is the first one ancestor of the other?")), OPT_END() }; diff --git a/builtin/merge-file.c b/builtin/merge-file.c index c0570f2407..844f84f40b 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -30,7 +30,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) int quiet = 0; int prefixlen = 0; struct option options[] = { - OPT_BOOLEAN('p', "stdout", &to_stdout, N_("send results to standard output")), + OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")), OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3), OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"), XDL_MERGE_FAVOR_OURS), diff --git a/builtin/merge.c b/builtin/merge.c index 34a6166b52..a8cf4a2484 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -197,15 +197,15 @@ static struct option builtin_merge_options[] = { { OPTION_CALLBACK, 'n', NULL, NULL, NULL, N_("do not show a diffstat at the end of the merge"), PARSE_OPT_NOARG, option_parse_n }, - OPT_BOOLEAN(0, "stat", &show_diffstat, + OPT_BOOL(0, "stat", &show_diffstat, N_("show a diffstat at the end of the merge")), - OPT_BOOLEAN(0, "summary", &show_diffstat, N_("(synonym to --stat)")), + OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), N_("add (at most <n>) entries from shortlog to merge commit message"), PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN }, - OPT_BOOLEAN(0, "squash", &squash, + OPT_BOOL(0, "squash", &squash, N_("create a single commit instead of doing a merge")), - OPT_BOOLEAN(0, "commit", &option_commit, + OPT_BOOL(0, "commit", &option_commit, N_("perform a commit if the merge succeeds (default)")), OPT_BOOL('e', "edit", &option_edit, N_("edit message before committing")), @@ -224,12 +224,12 @@ static struct option builtin_merge_options[] = { N_("merge commit message (for a non-fast-forward merge)"), option_parse_message), OPT__VERBOSITY(&verbosity), - OPT_BOOLEAN(0, "abort", &abort_current_merge, + OPT_BOOL(0, "abort", &abort_current_merge, N_("abort the current in-progress merge")), OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), + OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_END() }; diff --git a/builtin/mv.c b/builtin/mv.c index 034fec92a1..aec79d1838 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -9,14 +9,16 @@ #include "cache-tree.h" #include "string-list.h" #include "parse-options.h" +#include "submodule.h" static const char * const builtin_mv_usage[] = { N_("git mv [options] <source>... <destination>"), NULL }; -static const char **copy_pathspec(const char *prefix, const char **pathspec, - int count, int base_name) +static const char **internal_copy_pathspec(const char *prefix, + const char **pathspec, + int count, int base_name) { int i; const char **result = xmalloc((count + 1) * sizeof(const char *)); @@ -56,20 +58,21 @@ static struct lock_file lock_file; int cmd_mv(int argc, const char **argv, const char *prefix) { - int i, newfd; + int i, newfd, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), - OPT_BOOLEAN('k', NULL, &ignore_errors, N_("skip move/rename errors")), + OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; - const char **source, **destination, **dest_path; + const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; + gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, @@ -81,17 +84,18 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die(_("index file corrupt")); - source = copy_pathspec(prefix, argv, argc, 0); + source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); - dest_path = copy_pathspec(prefix, argv + argc, 1, 0); + dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0); + submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = copy_pathspec(dest_path[0], argv, argc, 1); + destination = internal_copy_pathspec(dest_path[0], argv, argc, 1); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); - destination = copy_pathspec(dest_path[0], argv, argc, 1); + destination = internal_copy_pathspec(dest_path[0], argv, argc, 1); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); @@ -117,55 +121,68 @@ int cmd_mv(int argc, const char **argv, const char *prefix) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { - const char *src_w_slash = add_slash(src); - int len_w_slash = length + 1; - int first, last; - - modes[i] = WORKING_DIRECTORY; - - first = cache_name_pos(src_w_slash, len_w_slash); - if (first >= 0) - die (_("Huh? %.*s is in index?"), - len_w_slash, src_w_slash); - - first = -1 - first; - for (last = first; last < active_nr; last++) { - const char *path = active_cache[last]->name; - if (strncmp(path, src_w_slash, len_w_slash)) - break; - } - free((char *)src_w_slash); - - if (last - first < 1) - bad = _("source directory is empty"); - else { - int j, dst_len; - - if (last - first > 0) { - source = xrealloc(source, - (argc + last - first) - * sizeof(char *)); - destination = xrealloc(destination, - (argc + last - first) - * sizeof(char *)); - modes = xrealloc(modes, - (argc + last - first) - * sizeof(enum update_mode)); + int first = cache_name_pos(src, length); + if (first >= 0) { + struct strbuf submodule_dotgit = STRBUF_INIT; + if (!S_ISGITLINK(active_cache[first]->ce_mode)) + die (_("Huh? Directory %s is in index and no submodule?"), src); + if (!is_staging_gitmodules_ok()) + die (_("Please, stage your changes to .gitmodules or stash them to proceed")); + strbuf_addf(&submodule_dotgit, "%s/.git", src); + submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); + if (submodule_gitfile[i]) + submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); + strbuf_release(&submodule_dotgit); + } else { + const char *src_w_slash = add_slash(src); + int last, len_w_slash = length + 1; + + modes[i] = WORKING_DIRECTORY; + + first = cache_name_pos(src_w_slash, len_w_slash); + if (first >= 0) + die (_("Huh? %.*s is in index?"), + len_w_slash, src_w_slash); + + first = -1 - first; + for (last = first; last < active_nr; last++) { + const char *path = active_cache[last]->name; + if (strncmp(path, src_w_slash, len_w_slash)) + break; } + free((char *)src_w_slash); + + if (last - first < 1) + bad = _("source directory is empty"); + else { + int j, dst_len; - dst = add_slash(dst); - dst_len = strlen(dst); - - for (j = 0; j < last - first; j++) { - const char *path = - active_cache[first + j]->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, - path + length + 1); - modes[argc + j] = INDEX; + if (last - first > 0) { + source = xrealloc(source, + (argc + last - first) + * sizeof(char *)); + destination = xrealloc(destination, + (argc + last - first) + * sizeof(char *)); + modes = xrealloc(modes, + (argc + last - first) + * sizeof(enum update_mode)); + } + + dst = add_slash(dst); + dst_len = strlen(dst); + + for (j = 0; j < last - first; j++) { + const char *path = + active_cache[first + j]->name; + source[argc + j] = path; + destination[argc + j] = + prefix_path(dst, dst_len, + path + length + 1); + modes[argc + j] = INDEX; + } + argc += last - first; } - argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); @@ -210,9 +227,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); - if (!show_only && mode != INDEX && - rename(src, dst) < 0 && !ignore_errors) - die_errno (_("renaming '%s' failed"), src); + if (!show_only && mode != INDEX) { + if (rename(src, dst) < 0 && !ignore_errors) + die_errno (_("renaming '%s' failed"), src); + if (submodule_gitfile[i]) + connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); + if (!update_path_in_gitmodules(src, dst)) + gitmodules_modified = 1; + } if (mode == WORKING_DIRECTORY) continue; @@ -223,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix) rename_cache_entry_at(pos, dst); } + if (gitmodules_modified) + stage_updated_gitmodules(); + if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 0aaa19e4ab..20fcf8c696 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -310,15 +310,15 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, NULL }; struct option opts[] = { - OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")), - OPT_BOOLEAN(0, "tags", &data.tags_only, N_("only use tags to name the commits")), + OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")), + OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")), OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"), N_("only use refs matching <pattern>")), OPT_GROUP(""), - OPT_BOOLEAN(0, "all", &all, N_("list all commits reachable from all refs")), - OPT_BOOLEAN(0, "stdin", &transform_stdin, N_("read from stdin")), - OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")), - OPT_BOOLEAN(0, "always", &always, + OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")), + OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")), + OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), + OPT_BOOL(0, "always", &always, N_("show abbreviated commit object as fallback")), { /* A Hidden OPT_BOOL */ @@ -331,7 +331,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); - if (!!all + !!transform_stdin + !!argc > 1) { + if (all + transform_stdin + !!argc > 1) { error("Specify either a list, or --all, not both!"); usage_with_options(name_rev_usage, opts); } diff --git a/builtin/notes.c b/builtin/notes.c index e4100c4982..d459e23c42 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -483,7 +483,7 @@ static int copy(int argc, const char **argv, const char *prefix) const char *rewrite_cmd = NULL; struct option options[] = { OPT__FORCE(&force, N_("replace existing notes")), - OPT_BOOLEAN(0, "stdin", &from_stdin, N_("read objects from stdin")), + OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")), OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"), N_("load rewriting config for <command> (implies " "--stdin)")), @@ -739,13 +739,13 @@ static int merge(int argc, const char **argv, const char *prefix) N_("resolve notes conflicts using the given strategy " "(manual/ours/theirs/union/cat_sort_uniq)")), OPT_GROUP(N_("Committing unmerged notes")), - { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL, + { OPTION_SET_INT, 0, "commit", &do_commit, NULL, N_("finalize notes merge by committing unmerged notes"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1}, OPT_GROUP(N_("Aborting notes merge resolution")), - { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL, + { OPTION_SET_INT, 0, "abort", &do_abort, NULL, N_("abort notes merge"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1}, OPT_END() }; @@ -853,7 +853,7 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) OPT_BIT(0, "ignore-missing", &flag, N_("attempt to remove non-existent note is not an error"), IGNORE_MISSING), - OPT_BOOLEAN(0, "stdin", &from_stdin, + OPT_BOOL(0, "stdin", &from_stdin, N_("read object names from the standard input")), OPT_END() }; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index f069462cb0..4eb0521c81 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1809,7 +1809,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, static void try_to_free_from_threads(size_t size) { read_lock(); - release_pack_memory(size, -1); + release_pack_memory(size); read_unlock(); } diff --git a/builtin/push.c b/builtin/push.c index 04f0eaf179..7b1b66c36a 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -15,12 +15,14 @@ static const char * const push_usage[] = { NULL, }; -static int thin; +static int thin = 1; static int deleterefs; static const char *receivepack; static int verbosity; static int progress = -1; +static struct push_cas_option cas; + static const char **refspec; static int refspec_nr; static int refspec_alloc; @@ -313,8 +315,14 @@ static int push_with_options(struct transport *transport, int flags) if (receivepack) transport_set_option(transport, TRANS_OPT_RECEIVEPACK, receivepack); - if (thin) - transport_set_option(transport, TRANS_OPT_THIN, "yes"); + transport_set_option(transport, TRANS_OPT_THIN, thin ? "yes" : NULL); + + if (!is_empty_cas(&cas)) { + if (!transport->smart_options) + die("underlying transport does not support --%s option", + CAS_OPT_NAME); + transport->smart_options->cas = &cas; + } if (verbosity > 0) fprintf(stderr, _("Pushing to %s\n"), transport->url); @@ -446,15 +454,19 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL), OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"), (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)), - OPT_BOOLEAN( 0, "delete", &deleterefs, N_("delete refs")), - OPT_BOOLEAN( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")), + OPT_BOOL( 0, "delete", &deleterefs, N_("delete refs")), + OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")), OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN), OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN), OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE), + { OPTION_CALLBACK, + 0, CAS_OPT_NAME, &cas, N_("refname>:<expect"), + N_("require old value of ref to be at this value"), + PARSE_OPT_OPTARG, parseopt_push_cas_option }, { OPTION_CALLBACK, 0, "recurse-submodules", &flags, N_("check"), N_("control recursive pushing of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, - OPT_BOOLEAN( 0 , "thin", &thin, N_("use thin pack")), + OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")), OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")), OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")), OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"), diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index e3eb5fc058..b7e71a04f6 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -8,6 +8,7 @@ #include "commit.h" #include "object.h" #include "remote.h" +#include "connect.h" #include "transport.h" #include "string-list.h" #include "sha1-array.h" @@ -38,6 +39,7 @@ static int quiet; static int prefer_ofs_delta = 1; static int auto_update_server_info; static int auto_gc = 1; +static int fix_thin = 1; static const char *head_name; static void *head_name_to_free; static int sent_capabilities; @@ -869,7 +871,8 @@ static const char *unpack(int err_fd) keeper[i++] = "--stdin"; if (fsck_objects) keeper[i++] = "--strict"; - keeper[i++] = "--fix-thin"; + if (fix_thin) + keeper[i++] = "--fix-thin"; keeper[i++] = hdr_arg; keeper[i++] = keep_arg; keeper[i++] = NULL; @@ -975,6 +978,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) stateless_rpc = 1; continue; } + if (!strcmp(arg, "--reject-thin-pack-for-testing")) { + fix_thin = 0; + continue; + } usage(receive_pack_usage); } diff --git a/builtin/remote.c b/builtin/remote.c index 5e54d367b8..eaac3e2012 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -160,7 +160,7 @@ static int add(int argc, const char **argv) int i; struct option options[] = { - OPT_BOOLEAN('f', "fetch", &fetch, N_("fetch the remote branches")), + OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")), OPT_SET_INT(0, "tags", &fetch_tags, N_("import all tags and associated objects when fetching"), TAGS_SET), @@ -1088,7 +1088,7 @@ static int show(int argc, const char **argv) { int no_query = 0, result = 0, query_flag = 0; struct option options[] = { - OPT_BOOLEAN('n', NULL, &no_query, N_("do not query remotes")), + OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")), OPT_END() }; struct ref_states states; @@ -1195,10 +1195,10 @@ static int set_head(int argc, const char **argv) char *head_name = NULL; struct option options[] = { - OPT_BOOLEAN('a', "auto", &opt_a, - N_("set refs/remotes/<name>/HEAD according to remote")), - OPT_BOOLEAN('d', "delete", &opt_d, - N_("delete refs/remotes/<name>/HEAD")), + OPT_BOOL('a', "auto", &opt_a, + N_("set refs/remotes/<name>/HEAD according to remote")), + OPT_BOOL('d', "delete", &opt_d, + N_("delete refs/remotes/<name>/HEAD")), OPT_END() }; argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage, @@ -1317,8 +1317,8 @@ static int update(int argc, const char **argv) { int i, prune = 0; struct option options[] = { - OPT_BOOLEAN('p', "prune", &prune, - N_("prune remotes after fetching")), + OPT_BOOL('p', "prune", &prune, + N_("prune remotes after fetching")), OPT_END() }; const char **fetch_argv; @@ -1404,7 +1404,7 @@ static int set_branches(int argc, const char **argv) { int add_mode = 0; struct option options[] = { - OPT_BOOLEAN('\0', "add", &add_mode, N_("add branch")), + OPT_BOOL('\0', "add", &add_mode, N_("add branch")), OPT_END() }; @@ -1432,11 +1432,11 @@ static int set_url(int argc, const char **argv) int urlset_nr; struct strbuf name_buf = STRBUF_INIT; struct option options[] = { - OPT_BOOLEAN('\0', "push", &push_mode, - N_("manipulate push URLs")), - OPT_BOOLEAN('\0', "add", &add_mode, - N_("add URL")), - OPT_BOOLEAN('\0', "delete", &delete_mode, + OPT_BOOL('\0', "push", &push_mode, + N_("manipulate push URLs")), + OPT_BOOL('\0', "add", &add_mode, + N_("add URL")), + OPT_BOOL('\0', "delete", &delete_mode, N_("delete URLs")), OPT_END() }; diff --git a/builtin/replace.c b/builtin/replace.c index 59d31152d0..11b0a55ae7 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -118,9 +118,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix) { int list = 0, delete = 0, force = 0; struct option options[] = { - OPT_BOOLEAN('l', NULL, &list, N_("list replace refs")), - OPT_BOOLEAN('d', NULL, &delete, N_("delete replace refs")), - OPT_BOOLEAN('f', NULL, &force, N_("replace the ref if it exists")), + OPT_BOOL('l', NULL, &list, N_("list replace refs")), + OPT_BOOL('d', NULL, &delete, N_("delete replace refs")), + OPT_BOOL('f', NULL, &force, N_("replace the ref if it exists")), OPT_END() }; diff --git a/builtin/rerere.c b/builtin/rerere.c index dc1708e6d6..4e51addb3e 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -6,6 +6,7 @@ #include "rerere.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" +#include "pathspec.h" static const char * const rerere_usage[] = { N_("git rerere [clear | forget path... | status | remaining | diff | gc]"), @@ -68,11 +69,12 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) return rerere(flags); if (!strcmp(argv[0], "forget")) { - const char **pathspec; + struct pathspec pathspec; if (argc < 2) warning("'git rerere forget' without paths is deprecated"); - pathspec = get_pathspec(prefix, argv + 1); - return rerere_forget(pathspec); + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, + prefix, argv + 1); + return rerere_forget(&pathspec); } fd = setup_rerere(&merge_rr, flags); diff --git a/builtin/reset.c b/builtin/reset.c index afa6e020e8..5e4c551531 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -133,12 +133,13 @@ static void update_index_from_diff(struct diff_queue_struct *q, } } -static int read_from_tree(const char **pathspec, unsigned char *tree_sha1) +static int read_from_tree(const struct pathspec *pathspec, + unsigned char *tree_sha1) { struct diff_options opt; memset(&opt, 0, sizeof(opt)); - diff_tree_setup_paths(pathspec, &opt); + copy_pathspec(&opt.pathspec, pathspec); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; @@ -147,7 +148,7 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1) return 1; diffcore_std(&opt); diff_flush(&opt); - diff_tree_release_paths(&opt); + free_pathspec(&opt.pathspec); return 0; } @@ -174,7 +175,10 @@ static void die_if_unmerged_cache(int reset_type) } -static const char **parse_args(const char **argv, const char *prefix, const char **rev_ret) +static void parse_args(struct pathspec *pathspec, + const char **argv, const char *prefix, + int patch_mode, + const char **rev_ret) { const char *rev = "HEAD"; unsigned char unused[20]; @@ -216,7 +220,10 @@ static const char **parse_args(const char **argv, const char *prefix, const char } } *rev_ret = rev; - return argv[0] ? get_pathspec(prefix, argv) : NULL; + parse_pathspec(pathspec, 0, + PATHSPEC_PREFER_FULL | + (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0), + prefix, argv); } static int update_refs(const char *rev, const unsigned char *sha1) @@ -246,7 +253,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) int patch_mode = 0, unborn; const char *rev; unsigned char sha1[20]; - const char **pathspec = NULL; + struct pathspec pathspec; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, @@ -258,7 +265,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), - OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_END() }; @@ -266,13 +273,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); - pathspec = parse_args(argv, prefix, &rev); + parse_args(&pathspec, argv, prefix, patch_mode, &rev); unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ hashcpy(sha1, EMPTY_TREE_SHA1_BIN); - } else if (!pathspec) { + } else if (!pathspec.nr) { struct commit *commit; if (get_sha1_committish(rev, sha1)) die(_("Failed to resolve '%s' as a valid revision."), rev); @@ -293,13 +300,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); - return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec); + return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", &pathspec); } /* git reset tree [--] paths... can be used to * load chosen paths from the tree into the index without * affecting the working tree nor HEAD. */ - if (pathspec) { + if (pathspec.nr) { if (reset_type == MIXED) warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); else if (reset_type != NONE) @@ -326,7 +333,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { - if (read_from_tree(pathspec, sha1)) + if (read_from_tree(&pathspec, sha1)) return 1; } else { int err = reset_index(sha1, reset_type, quiet); @@ -347,7 +354,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) die(_("Could not write new index file.")); } - if (!pathspec && !unborn) { + if (!pathspec.nr && !unborn) { /* Any resets without paths update HEAD to the head being * switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status = update_refs(rev, sha1); @@ -355,7 +362,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type == HARD && !update_ref_status && !quiet) print_new_head_line(lookup_commit_reference(sha1)); } - if (!pathspec) + if (!pathspec.nr) remove_branch_state(); return update_ref_status; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index de894c7577..c76b89dc5b 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -346,9 +346,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) NULL }; static struct option parseopt_opts[] = { - OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash, + OPT_BOOL(0, "keep-dashdash", &keep_dashdash, N_("keep the `--` passed as an arg")), - OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option, + OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option, N_("stop parsing after the " "first non-option argument")), OPT_END(), @@ -486,21 +486,6 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (argc > 1 && !strcmp("--sq-quote", argv[1])) return cmd_sq_quote(argc - 2, argv + 2); - if (argc == 2 && !strcmp("--local-env-vars", argv[1])) { - int i; - for (i = 0; local_repo_env[i]; i++) - printf("%s\n", local_repo_env[i]); - return 0; - } - - if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) { - const char *gitdir = resolve_gitdir(argv[2]); - if (!gitdir) - die("not a gitdir '%s'", argv[2]); - puts(gitdir); - return 0; - } - if (argc > 1 && !strcmp("-h", argv[1])) usage(builtin_rev_parse_usage); @@ -661,6 +646,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_remote_ref(show_reference, NULL); continue; } + if (!strcmp(arg, "--local-env-vars")) { + int i; + for (i = 0; local_repo_env[i]; i++) + printf("%s\n", local_repo_env[i]); + continue; + } if (!strcmp(arg, "--show-toplevel")) { const char *work_tree = get_git_work_tree(); if (work_tree) @@ -711,6 +702,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : ""); continue; } + if (!strcmp(arg, "--resolve-git-dir")) { + const char *gitdir = resolve_gitdir(argv[i+1]); + if (!gitdir) + die("not a gitdir '%s'", argv[i+1]); + puts(gitdir); + continue; + } if (!strcmp(arg, "--is-inside-git-dir")) { printf("%s\n", is_inside_git_dir() ? "true" : "false"); diff --git a/builtin/revert.c b/builtin/revert.c index 1d2648b756..8e87acd12e 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -71,44 +71,19 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...) die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt); } -LAST_ARG_MUST_BE_NULL -static void verify_opt_mutually_compatible(const char *me, ...) -{ - const char *opt1, *opt2 = NULL; - va_list ap; - - va_start(ap, me); - while ((opt1 = va_arg(ap, const char *))) { - if (va_arg(ap, int)) - break; - } - if (opt1) { - while ((opt2 = va_arg(ap, const char *))) { - if (va_arg(ap, int)) - break; - } - } - va_end(ap); - - if (opt1 && opt2) - die(_("%s: %s cannot be used with %s"), me, opt1, opt2); -} - static void parse_args(int argc, const char **argv, struct replay_opts *opts) { const char * const * usage_str = revert_or_cherry_pick_usage(opts); const char *me = action_name(opts); - int remove_state = 0; - int contin = 0; - int rollback = 0; + int cmd = 0; struct option options[] = { - OPT_BOOLEAN(0, "quit", &remove_state, N_("end revert or cherry-pick sequence")), - OPT_BOOLEAN(0, "continue", &contin, N_("resume revert or cherry-pick sequence")), - OPT_BOOLEAN(0, "abort", &rollback, N_("cancel revert or cherry-pick sequence")), - OPT_BOOLEAN('n', "no-commit", &opts->no_commit, N_("don't automatically commit")), - OPT_BOOLEAN('e', "edit", &opts->edit, N_("edit the commit message")), + OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'), + OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'), + OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'), + OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")), + OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")), OPT_NOOP_NOARG('r', NULL), - OPT_BOOLEAN('s', "signoff", &opts->signoff, N_("add Signed-off-by:")), + OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")), OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")), OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto), OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")), @@ -124,11 +99,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) if (opts->action == REPLAY_PICK) { struct option cp_extra[] = { - OPT_BOOLEAN('x', NULL, &opts->record_origin, N_("append commit name")), - OPT_BOOLEAN(0, "ff", &opts->allow_ff, N_("allow fast-forward")), - OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")), - OPT_BOOLEAN(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")), - OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")), + OPT_BOOL('x', NULL, &opts->record_origin, N_("append commit name")), + OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")), + OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")), + OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")), + OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")), OPT_END(), }; if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra)) @@ -139,23 +114,16 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); - /* Check for incompatible subcommands */ - verify_opt_mutually_compatible(me, - "--quit", remove_state, - "--continue", contin, - "--abort", rollback, - NULL); - /* implies allow_empty */ if (opts->keep_redundant_commits) opts->allow_empty = 1; /* Set the subcommand */ - if (remove_state) + if (cmd == 'q') opts->subcommand = REPLAY_REMOVE_STATE; - else if (contin) + else if (cmd == 'c') opts->subcommand = REPLAY_CONTINUE; - else if (rollback) + else if (cmd == 'a') opts->subcommand = REPLAY_ROLLBACK; else opts->subcommand = REPLAY_NONE; diff --git a/builtin/rm.c b/builtin/rm.c index 0df0b4d942..9b59ab3a64 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -11,6 +11,7 @@ #include "parse-options.h" #include "string-list.h" #include "submodule.h" +#include "pathspec.h" static const char * const builtin_rm_usage[] = { N_("git rm [options] [--] <file>..."), @@ -267,10 +268,10 @@ static int ignore_unmatch = 0; static struct option builtin_rm_options[] = { OPT__DRY_RUN(&show_only, N_("dry run")), OPT__QUIET(&quiet, N_("do not list removed files")), - OPT_BOOLEAN( 0 , "cached", &index_only, N_("only remove from the index")), + OPT_BOOL( 0 , "cached", &index_only, N_("only remove from the index")), OPT__FORCE(&force, N_("override the up-to-date check")), - OPT_BOOLEAN('r', NULL, &recursive, N_("allow recursive removal")), - OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch, + OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")), + OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch, N_("exit with a zero status even if nothing matched")), OPT_END(), }; @@ -278,9 +279,10 @@ static struct option builtin_rm_options[] = { int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; - const char **pathspec; + struct pathspec pathspec; char *seen; + gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, @@ -311,31 +313,32 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } } - pathspec = get_pathspec(prefix, argv); - refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, prefix, argv); + refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); - seen = NULL; - for (i = 0; pathspec[i] ; i++) - /* nothing */; - seen = xcalloc(i, 1); + seen = xcalloc(pathspec.nr, 1); for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) + if (!match_pathspec_depth(&pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; - list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); + list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); + if (list.entry[list.nr++].is_submodule && + !is_staging_gitmodules_ok()) + die (_("Please, stage your changes to .gitmodules or stash them to proceed")); } - if (pathspec) { - const char *match; + if (pathspec.nr) { + const char *original; int seen_any = 0; - for (i = 0; (match = pathspec[i]) != NULL ; i++) { + for (i = 0; i < pathspec.nr; i++) { + original = pathspec.items[i].original; if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), - match); + original); } } else { @@ -343,10 +346,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix) } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), - *match ? match : "."); + *original ? original : "."); } - if (! seen_any) + if (!seen_any) exit(0); } @@ -396,13 +399,15 @@ int cmd_rm(int argc, const char **argv, const char *prefix) * in the middle) */ if (!index_only) { - int removed = 0; + int removed = 0, gitmodules_modified = 0; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { if (is_empty_dir(path)) { if (!rmdir(path)) { removed = 1; + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; continue; } } else { @@ -410,9 +415,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix) strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; strbuf_release(&buf); continue; - } + } else if (!file_exists(path)) + /* Submodule was removed by user */ + if (!remove_path_from_gitmodules(path)) + gitmodules_modified = 1; strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } @@ -424,6 +434,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!removed) die_errno("git rm: '%s'", path); } + if (gitmodules_modified) + stage_updated_gitmodules(); } if (active_cache_changed) { diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 152c4ea092..4482f16efb 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -5,6 +5,7 @@ #include "sideband.h" #include "run-command.h" #include "remote.h" +#include "connect.h" #include "send-pack.h" #include "quote.h" #include "transport.h" @@ -54,6 +55,11 @@ static void print_helper_status(struct ref *ref) msg = "needs force"; break; + case REF_STATUS_REJECT_STALE: + res = "error"; + msg = "stale info"; + break; + case REF_STATUS_REJECT_ALREADY_EXISTS: res = "error"; msg = "already exists"; @@ -102,6 +108,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) int flags; unsigned int reject_reasons; int progress = -1; + struct push_cas_option cas = {0}; argv++; for (i = 1; i < argc; i++, argv++) { @@ -164,6 +171,22 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) helper_status = 1; continue; } + if (!strcmp(arg, "--" CAS_OPT_NAME)) { + if (parse_push_cas_option(&cas, NULL, 0) < 0) + exit(1); + continue; + } + if (!strcmp(arg, "--no-" CAS_OPT_NAME)) { + if (parse_push_cas_option(&cas, NULL, 1) < 0) + exit(1); + continue; + } + if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) { + if (parse_push_cas_option(&cas, + strchr(arg, '=') + 1, 0) < 0) + exit(1); + continue; + } usage(send_pack_usage); } if (!dest) { @@ -224,6 +247,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags)) return -1; + if (!is_empty_cas(&cas)) + apply_push_cas(&cas, remote, remote_refs); + set_ref_status_for_push(remote_refs, args.send_mirror, args.force_update); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 1434f8fee4..ae73d17b6c 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -224,12 +224,12 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) int nongit = !startup_info->have_repository; static const struct option options[] = { - OPT_BOOLEAN('n', "numbered", &log.sort_by_number, - N_("sort output according to the number of commits per author")), - OPT_BOOLEAN('s', "summary", &log.summary, - N_("Suppress commit descriptions, only provides commit count")), - OPT_BOOLEAN('e', "email", &log.email, - N_("Show the email address of each author")), + OPT_BOOL('n', "numbered", &log.sort_by_number, + N_("sort output according to the number of commits per author")), + OPT_BOOL('s', "summary", &log.summary, + N_("Suppress commit descriptions, only provides commit count")), + OPT_BOOL('e', "email", &log.email, + N_("Show the email address of each author")), { OPTION_CALLBACK, 'w', NULL, &log, N_("w[,i1[,i2]]"), N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args }, OPT_END(), diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 9788eb115b..001f29ca1b 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -646,30 +646,30 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int dense = 1; const char *reflog_base = NULL; struct option builtin_show_branch_options[] = { - OPT_BOOLEAN('a', "all", &all_heads, - N_("show remote-tracking and local branches")), - OPT_BOOLEAN('r', "remotes", &all_remotes, - N_("show remote-tracking branches")), + OPT_BOOL('a', "all", &all_heads, + N_("show remote-tracking and local branches")), + OPT_BOOL('r', "remotes", &all_remotes, + N_("show remote-tracking branches")), OPT__COLOR(&showbranch_use_color, N_("color '*!+-' corresponding to the branch")), { OPTION_INTEGER, 0, "more", &extra, N_("n"), N_("show <n> more commits after the common ancestor"), PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1), - OPT_BOOLEAN(0, "no-name", &no_name, N_("suppress naming strings")), - OPT_BOOLEAN(0, "current", &with_current_branch, - N_("include the current branch")), - OPT_BOOLEAN(0, "sha1-name", &sha1_name, - N_("name commits with their object names")), - OPT_BOOLEAN(0, "merge-base", &merge_base, - N_("show possible merge bases")), - OPT_BOOLEAN(0, "independent", &independent, + OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")), + OPT_BOOL(0, "current", &with_current_branch, + N_("include the current branch")), + OPT_BOOL(0, "sha1-name", &sha1_name, + N_("name commits with their object names")), + OPT_BOOL(0, "merge-base", &merge_base, + N_("show possible merge bases")), + OPT_BOOL(0, "independent", &independent, N_("show refs unreachable from any other ref")), OPT_SET_INT(0, "topo-order", &sort_order, N_("show commits in topological order"), REV_SORT_IN_GRAPH_ORDER), - OPT_BOOLEAN(0, "topics", &topics, - N_("show only commits not on the first branch")), + OPT_BOOL(0, "topics", &topics, + N_("show only commits not on the first branch")), OPT_SET_INT(0, "sparse", &dense, N_("show merges reachable from only one tip"), 0), OPT_SET_INT(0, "date-order", &sort_order, diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 87806ad5b0..9f3f5e370b 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -165,16 +165,15 @@ static int help_callback(const struct option *opt, const char *arg, int unset) } static const struct option show_ref_options[] = { - OPT_BOOLEAN(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")), - OPT_BOOLEAN(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")), - OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, " + OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")), + OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")), + OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " "requires exact ref path")), - { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL, - N_("show the HEAD reference, even if it would be filtered out"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, - OPT_BOOLEAN(0, "head", &show_head, + OPT_HIDDEN_BOOL('h', NULL, &show_head, + N_("show the HEAD reference, even if it would be filtered out")), + OPT_BOOL(0, "head", &show_head, N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOLEAN('d', "dereference", &deref_tags, + OPT_BOOL('d', "dereference", &deref_tags, N_("dereference tags into object IDs")), { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"), N_("only show SHA1 hash using <n> digits"), diff --git a/builtin/tag.c b/builtin/tag.c index af3af3f649..b577af562a 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -436,26 +436,26 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct ref_lock *lock; struct create_tag_options opt; char *cleanup_arg = NULL; - int annotate = 0, force = 0, lines = -1, list = 0, - delete = 0, verify = 0; + int annotate = 0, force = 0, lines = -1; + int cmdmode = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct commit_list *with_commit = NULL; struct option options[] = { - OPT_BOOLEAN('l', "list", &list, N_("list tag names")), + OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), { OPTION_INTEGER, 'n', NULL, &lines, N_("n"), N_("print <n> lines of each tag message"), PARSE_OPT_OPTARG, NULL, 1 }, - OPT_BOOLEAN('d', "delete", &delete, N_("delete tags")), - OPT_BOOLEAN('v', "verify", &verify, N_("verify tags")), + OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'), + OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'), OPT_GROUP(N_("Tag creation options")), - OPT_BOOLEAN('a', "annotate", &annotate, + OPT_BOOL('a', "annotate", &annotate, N_("annotated tag, needs a message")), OPT_CALLBACK('m', "message", &msg, N_("message"), N_("tag message"), parse_msg_arg), OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), - OPT_BOOLEAN('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), + OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"), N_("how to strip spaces and #comments from message")), OPT_STRING('u', "local-user", &keyid, N_("key id"), @@ -489,22 +489,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix) } if (opt.sign) annotate = 1; - if (argc == 0 && !(delete || verify)) - list = 1; + if (argc == 0 && !cmdmode) + cmdmode = 'l'; - if ((annotate || msg.given || msgfile || force) && - (list || delete || verify)) + if ((annotate || msg.given || msgfile || force) && (cmdmode != 0)) usage_with_options(git_tag_usage, options); - if (list + delete + verify > 1) - usage_with_options(git_tag_usage, options); finalize_colopts(&colopts, -1); - if (list && lines != -1) { + if (cmdmode == 'l' && lines != -1) { if (explicitly_enable_column(colopts)) die(_("--column and -n are incompatible")); colopts = 0; } - if (list) { + if (cmdmode == 'l') { int ret; if (column_active(colopts)) { struct column_options copts; @@ -523,9 +520,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die(_("--contains option is only allowed with -l.")); if (points_at.nr) die(_("--points-at option is only allowed with -l.")); - if (delete) + if (cmdmode == 'd') return for_each_tag_name(argv, delete_tag); - if (verify) + if (cmdmode == 'v') return for_each_tag_name(argv, verify_tag); if (msg.given || msgfile) { diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c index 3f1e7012db..ba3ffe69a9 100644 --- a/builtin/tar-tree.c +++ b/builtin/tar-tree.c @@ -26,8 +26,8 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix) * $0 tree-ish basedir ==> * git archive --format-tar --prefix=basedir tree-ish */ - int i; const char **nargv = xcalloc(sizeof(*nargv), argc + 3); + struct strbuf sb = STRBUF_INIT; char *basedir_arg; int nargc = 0; @@ -65,11 +65,10 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix) fprintf(stderr, "*** \"git tar-tree\" is now deprecated.\n" "*** Running \"git archive\" instead.\n***"); - for (i = 0; i < nargc; i++) { - fputc(' ', stderr); - sq_quote_print(stderr, nargv[i]); - } - fputc('\n', stderr); + sq_quote_argv(&sb, nargv, 0); + strbuf_addch(&sb, '\n'); + fputs(sb.buf, stderr); + strbuf_release(&sb); return cmd_archive(nargc, nargv, prefix); } diff --git a/builtin/update-index.c b/builtin/update-index.c index c317981516..e3a10d706d 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -11,6 +11,7 @@ #include "refs.h" #include "resolve-undo.h" #include "parse-options.h" +#include "pathspec.h" /* * Default to not allowing changes to the list of files. The @@ -546,10 +547,11 @@ static int do_reupdate(int ac, const char **av, */ int pos; int has_head = 1; - const char **paths = get_pathspec(prefix, av + 1); struct pathspec pathspec; - init_pathspec(&pathspec, paths); + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD, + prefix, av + 1); if (read_ref("HEAD", head_sha1)) /* If there is no HEAD, that means it is an initial diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 51d2684859..7484d36a65 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -16,8 +16,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) int delete = 0, no_deref = 0, flags = 0; struct option options[] = { OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), - OPT_BOOLEAN('d', NULL, &delete, N_("delete the reference")), - OPT_BOOLEAN( 0 , "no-deref", &no_deref, + OPT_BOOL('d', NULL, &delete, N_("delete the reference")), + OPT_BOOL( 0 , "no-deref", &no_deref, N_("update <refname> not the one it points to")), OPT_END(), }; diff --git a/bulk-checkin.c b/bulk-checkin.c index 6b0b6d4904..118c62528b 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -114,7 +114,7 @@ static int stream_to_pack(struct bulk_checkin_state *state, if (size && !s.avail_in) { ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf); - if (xread(fd, ibuf, rsize) != rsize) + if (read_in_full(fd, ibuf, rsize) != rsize) die("failed to read %d bytes from '%s'", (int)rsize, path); offset += rsize; @@ -101,9 +101,9 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long); #define CACHE_SIGNATURE 0x44495243 /* "DIRC" */ struct cache_header { - unsigned int hdr_signature; - unsigned int hdr_version; - unsigned int hdr_entries; + uint32_t hdr_signature; + uint32_t hdr_version; + uint32_t hdr_entries; }; #define INDEX_FORMAT_LB 2 @@ -115,8 +115,8 @@ struct cache_header { * check it for equality in the 32 bits we save. */ struct cache_time { - unsigned int sec; - unsigned int nsec; + uint32_t sec; + uint32_t nsec; }; struct stat_data { @@ -189,6 +189,8 @@ struct cache_entry { #error "CE_EXTENDED_FLAGS out of range" #endif +struct pathspec; + /* * Copy the sha1 and stat state of a cache entry from one to * another. But we never change the name, or the hash state! @@ -365,6 +367,9 @@ static inline enum object_type object_type(unsigned int mode) #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF" #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE" #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS" +#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS" +#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS" +#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS" /* * This environment variable is expected to contain a boolean indicating @@ -412,6 +417,7 @@ extern void setup_work_tree(void); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern char *prefix_path(const char *prefix, int len, const char *path); +extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path); extern const char *prefix_filename(const char *prefix, int len, const char *path); extern int check_filename(const char *prefix, const char *name); extern void verify_filename(const char *prefix, @@ -449,7 +455,7 @@ extern void sanitize_stdfds(void); /* Initialize and use the cache information */ extern int read_index(struct index_state *); -extern int read_index_preload(struct index_state *, const char **pathspec); +extern int read_index_preload(struct index_state *, const struct pathspec *pathspec); extern int read_index_from(struct index_state *, const char *path); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); @@ -491,28 +497,8 @@ extern void *read_blob_data_from_index(struct index_state *, const char *, unsig extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int); -#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */ - -struct pathspec { - const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ - int nr; - unsigned int has_wildcard:1; - unsigned int recursive:1; - int max_depth; - struct pathspec_item { - const char *match; - int len; - int nowildcard_len; - int flags; - } *items; -}; - -extern int init_pathspec(struct pathspec *, const char **); -extern void free_pathspec(struct pathspec *); extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); -extern int limit_pathspec_to_literal(void); - #define HASH_WRITE_OBJECT 1 #define HASH_FORMAT_CHECK 2 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); @@ -540,7 +526,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ #define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */ #define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg); +extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg); struct lock_file { struct lock_file *next; @@ -762,6 +748,7 @@ const char *real_path(const char *path); const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); @@ -1038,68 +1025,6 @@ struct pack_entry { struct packed_git *p; }; -struct ref { - struct ref *next; - unsigned char old_sha1[20]; - unsigned char new_sha1[20]; - char *symref; - unsigned int - force:1, - forced_update:1, - deletion:1, - matched:1; - - /* - * Order is important here, as we write to FETCH_HEAD - * in numeric order. And the default NOT_FOR_MERGE - * should be 0, so that xcalloc'd structures get it - * by default. - */ - enum { - FETCH_HEAD_MERGE = -1, - FETCH_HEAD_NOT_FOR_MERGE = 0, - FETCH_HEAD_IGNORE = 1 - } fetch_head_status; - - enum { - REF_STATUS_NONE = 0, - REF_STATUS_OK, - REF_STATUS_REJECT_NONFASTFORWARD, - REF_STATUS_REJECT_ALREADY_EXISTS, - REF_STATUS_REJECT_NODELETE, - REF_STATUS_REJECT_FETCH_FIRST, - REF_STATUS_REJECT_NEEDS_FORCE, - REF_STATUS_UPTODATE, - REF_STATUS_REMOTE_REJECT, - REF_STATUS_EXPECTING_REPORT - } status; - char *remote_status; - struct ref *peer_ref; /* when renaming */ - char name[FLEX_ARRAY]; /* more */ -}; - -#define REF_NORMAL (1u << 0) -#define REF_HEADS (1u << 1) -#define REF_TAGS (1u << 2) - -extern struct ref *find_ref_by_name(const struct ref *list, const char *name); - -#define CONNECT_VERBOSE (1u << 0) -extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); -extern int finish_connect(struct child_process *conn); -extern int git_connection_is_socket(struct child_process *conn); -struct extra_have_objects { - int nr, alloc; - unsigned char (*array)[20]; -}; -extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, - struct ref **list, unsigned int flags, - struct extra_have_objects *); -extern int server_supports(const char *feature); -extern int parse_feature_request(const char *features, const char *feature); -extern const char *server_feature_value(const char *feature, int *len_ret); -extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret); - extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path); /* A hook for count-objects to report invalid files in pack directory */ @@ -1190,6 +1115,7 @@ extern int git_config_with_options(config_fn_t fn, void *, extern int git_config_early(config_fn_t fn, void *, const char *repo_config); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); +extern int64_t git_config_int64(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); extern int git_config_bool_or_int(const char *, const char *, int *); extern int git_config_bool(const char *, const char *); @@ -1305,7 +1231,7 @@ void packet_trace_identity(const char *prog); * return 0 if success, 1 - if addition of a file failed and * ADD_FILES_IGNORE_ERRORS was specified in flags */ -int add_files_to_cache(const char *prefix, const char **pathspec, int flags); +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags); /* diff.c */ extern int diff_auto_refresh_index; @@ -1339,7 +1265,7 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule); #define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK) /* ls-files */ -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix); +int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix); void overlay_tree_on_cache(const char *tree_name, const char *prefix); char *alias_lookup(const char *alias); diff --git a/combine-diff.c b/combine-diff.c index 88525b37cf..3b92c44880 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -10,6 +10,7 @@ #include "refs.h" #include "userdiff.h" #include "sha1-array.h" +#include "revision.h" static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent) { @@ -1305,7 +1306,7 @@ void diff_tree_combined(const unsigned char *sha1, int i, num_paths, needsep, show_log_first, num_parent = parents->nr; diffopts = *opt; - diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts); + copy_pathspec(&diffopts.pathspec, &opt->pathspec); diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; DIFF_OPT_SET(&diffopts, RECURSIVE); DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL); @@ -1377,13 +1378,13 @@ void diff_tree_combined(const unsigned char *sha1, free(tmp); } - diff_tree_release_paths(&diffopts); + free_pathspec(&diffopts.pathspec); } void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev) { - struct commit_list *parent = commit->parents; + struct commit_list *parent = get_saved_parents(rev, commit); struct sha1_array parents = SHA1_ARRAY_INIT; while (parent) { @@ -377,6 +377,22 @@ unsigned commit_list_count(const struct commit_list *l) return c; } +struct commit_list *copy_commit_list(struct commit_list *list) +{ + struct commit_list *head = NULL; + struct commit_list **pp = &head; + while (list) { + struct commit_list *new; + new = xmalloc(sizeof(struct commit_list)); + new->item = list->item; + new->next = NULL; + *pp = new; + pp = &new->next; + list = list->next; + } + return head; +} + void free_commit_list(struct commit_list *list) { while (list) { @@ -62,6 +62,9 @@ struct commit_list *commit_list_insert_by_date(struct commit *item, struct commit_list **list); void commit_list_sort_by_date(struct commit_list **list); +/* Shallow copy of the input list */ +struct commit_list *copy_commit_list(struct commit_list *list); + void free_commit_list(struct commit_list *list); /* Commit formats */ @@ -205,7 +208,7 @@ int in_merge_bases_many(struct commit *, int, struct commit **); extern int interactive_add(int argc, const char **argv, const char *prefix, int patch); extern int run_add_interactive(const char *revision, const char *patch_mode, - const char **pathspec); + const struct pathspec *pathspec); static inline int single_parent(struct commit *commit) { diff --git a/compat/apple-common-crypto.h b/compat/apple-common-crypto.h new file mode 100644 index 0000000000..c8b9b0e1a6 --- /dev/null +++ b/compat/apple-common-crypto.h @@ -0,0 +1,86 @@ +/* suppress inclusion of conflicting openssl functions */ +#define OPENSSL_NO_MD5 +#define HEADER_HMAC_H +#define HEADER_SHA_H +#include <CommonCrypto/CommonHMAC.h> +#define HMAC_CTX CCHmacContext +#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len) +#define HMAC_Update CCHmacUpdate +#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash) +#define HMAC_CTX_cleanup(ignore) +#define EVP_md5(...) kCCHmacAlgMD5 +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define APPLE_LION_OR_NEWER +#include <Security/Security.h> +/* Apple's TYPE_BOOL conflicts with config.c */ +#undef TYPE_BOOL +#endif + +#ifdef APPLE_LION_OR_NEWER +#define git_CC_error_check(pattern, err) \ + do { \ + if (err) { \ + die(pattern, (long)CFErrorGetCode(err)); \ + } \ + } while(0) + +#define EVP_EncodeBlock git_CC_EVP_EncodeBlock +static inline int git_CC_EVP_EncodeBlock(unsigned char *out, + const unsigned char *in, int inlen) +{ + CFErrorRef err; + SecTransformRef encoder; + CFDataRef input, output; + CFIndex length; + + encoder = SecEncodeTransformCreate(kSecBase64Encoding, &err); + git_CC_error_check("SecEncodeTransformCreate failed: %ld", err); + + input = CFDataCreate(kCFAllocatorDefault, in, inlen); + SecTransformSetAttribute(encoder, kSecTransformInputAttributeName, + input, &err); + git_CC_error_check("SecTransformSetAttribute failed: %ld", err); + + output = SecTransformExecute(encoder, &err); + git_CC_error_check("SecTransformExecute failed: %ld", err); + + length = CFDataGetLength(output); + CFDataGetBytes(output, CFRangeMake(0, length), out); + + CFRelease(output); + CFRelease(input); + CFRelease(encoder); + + return (int)strlen((const char *)out); +} + +#define EVP_DecodeBlock git_CC_EVP_DecodeBlock +static int inline git_CC_EVP_DecodeBlock(unsigned char *out, + const unsigned char *in, int inlen) +{ + CFErrorRef err; + SecTransformRef decoder; + CFDataRef input, output; + CFIndex length; + + decoder = SecDecodeTransformCreate(kSecBase64Encoding, &err); + git_CC_error_check("SecEncodeTransformCreate failed: %ld", err); + + input = CFDataCreate(kCFAllocatorDefault, in, inlen); + SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, + input, &err); + git_CC_error_check("SecTransformSetAttribute failed: %ld", err); + + output = SecTransformExecute(decoder, &err); + git_CC_error_check("SecTransformExecute failed: %ld", err); + + length = CFDataGetLength(output); + CFDataGetBytes(output, CFRangeMake(0, length), out); + + CFRelease(output); + CFRelease(input); + CFRelease(decoder); + + return (int)strlen((const char *)out); +} +#endif /* APPLE_LION_OR_NEWER */ diff --git a/compat/clipped-write.c b/compat/clipped-write.c deleted file mode 100644 index b8f98ff77f..0000000000 --- a/compat/clipped-write.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "../git-compat-util.h" -#undef write - -/* - * Version of write that will write at most INT_MAX bytes. - * Workaround a xnu bug on Mac OS X - */ -ssize_t clipped_write(int fildes, const void *buf, size_t nbyte) -{ - if (nbyte > INT_MAX) - nbyte = INT_MAX; - return write(fildes, buf, nbyte); -} diff --git a/compat/mingw.c b/compat/mingw.c index bb92c436f7..22ee9ef1cf 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1086,6 +1086,12 @@ int mingw_kill(pid_t pid, int sig) errno = err_win_to_posix(GetLastError()); CloseHandle(h); return -1; + } else if (pid > 0 && sig == 0) { + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (h) { + CloseHandle(h); + return 0; + } } errno = EINVAL; @@ -27,9 +27,9 @@ struct config_source { struct strbuf value; struct strbuf var; - int (*fgetc)(struct config_source *c); - int (*ungetc)(int c, struct config_source *conf); - long (*ftell)(struct config_source *c); + int (*do_fgetc)(struct config_source *c); + int (*do_ungetc)(int c, struct config_source *conf); + long (*do_ftell)(struct config_source *c); }; static struct config_source *cf; @@ -217,13 +217,13 @@ int git_config_from_parameters(config_fn_t fn, void *data) static int get_next_char(void) { - int c = cf->fgetc(cf); + int c = cf->do_fgetc(cf); if (c == '\r') { /* DOS like systems */ - c = cf->fgetc(cf); + c = cf->do_fgetc(cf); if (c != '\n') { - cf->ungetc(c, cf); + cf->do_ungetc(c, cf); c = '\r'; } } @@ -468,7 +468,7 @@ static int parse_unit_factor(const char *end, uintmax_t *val) return 0; } -static int git_parse_long(const char *value, long *ret) +static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max) { if (value && *value) { char *end; @@ -480,21 +480,25 @@ static int git_parse_long(const char *value, long *ret) val = strtoimax(value, &end, 0); if (errno == ERANGE) return 0; - if (!parse_unit_factor(end, &factor)) + if (!parse_unit_factor(end, &factor)) { + errno = EINVAL; return 0; + } uval = abs(val); uval *= factor; - if ((uval > maximum_signed_value_of_type(long)) || - (abs(val) > uval)) + if (uval > max || abs(val) > uval) { + errno = ERANGE; return 0; + } val *= factor; *ret = val; return 1; } + errno = EINVAL; return 0; } -int git_parse_ulong(const char *value, unsigned long *ret) +int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max) { if (value && *value) { char *end; @@ -506,29 +510,75 @@ int git_parse_ulong(const char *value, unsigned long *ret) if (errno == ERANGE) return 0; oldval = val; - if (!parse_unit_factor(end, &val)) + if (!parse_unit_factor(end, &val)) { + errno = EINVAL; return 0; - if ((val > maximum_unsigned_value_of_type(long)) || - (oldval > val)) + } + if (val > max || oldval > val) { + errno = ERANGE; return 0; + } *ret = val; return 1; } + errno = EINVAL; return 0; } -static void die_bad_config(const char *name) +static int git_parse_int(const char *value, int *ret) { + intmax_t tmp; + if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int))) + return 0; + *ret = tmp; + return 1; +} + +static int git_parse_int64(const char *value, int64_t *ret) +{ + intmax_t tmp; + if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t))) + return 0; + *ret = tmp; + return 1; +} + +int git_parse_ulong(const char *value, unsigned long *ret) +{ + uintmax_t tmp; + if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long))) + return 0; + *ret = tmp; + return 1; +} + +static void die_bad_number(const char *name, const char *value) +{ + const char *reason = errno == ERANGE ? + "out of range" : + "invalid unit"; + if (!value) + value = ""; + if (cf && cf->name) - die("bad config value for '%s' in %s", name, cf->name); - die("bad config value for '%s'", name); + die("bad numeric config value '%s' for '%s' in %s: %s", + value, name, cf->name, reason); + die("bad numeric config value '%s' for '%s': %s", value, name, reason); } int git_config_int(const char *name, const char *value) { - long ret = 0; - if (!git_parse_long(value, &ret)) - die_bad_config(name); + int ret; + if (!git_parse_int(value, &ret)) + die_bad_number(name, value); + return ret; +} + +int64_t git_config_int64(const char *name, const char *value) +{ + int64_t ret; + if (!git_parse_int64(value, &ret)) + die_bad_number(name, value); return ret; } @@ -536,7 +586,7 @@ unsigned long git_config_ulong(const char *name, const char *value) { unsigned long ret; if (!git_parse_ulong(value, &ret)) - die_bad_config(name); + die_bad_number(name, value); return ret; } @@ -559,10 +609,10 @@ static int git_config_maybe_bool_text(const char *name, const char *value) int git_config_maybe_bool(const char *name, const char *value) { - long v = git_config_maybe_bool_text(name, value); + int v = git_config_maybe_bool_text(name, value); if (0 <= v) return v; - if (git_parse_long(value, &v)) + if (git_parse_int(value, &v)) return !!v; return -1; } @@ -992,9 +1042,9 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data) top.u.file = f; top.name = filename; top.die_on_error = 1; - top.fgetc = config_file_fgetc; - top.ungetc = config_file_ungetc; - top.ftell = config_file_ftell; + top.do_fgetc = config_file_fgetc; + top.do_ungetc = config_file_ungetc; + top.do_ftell = config_file_ftell; ret = do_config_from(&top, fn, data); @@ -1013,9 +1063,9 @@ int git_config_from_buf(config_fn_t fn, const char *name, const char *buf, top.u.buf.pos = 0; top.name = name; top.die_on_error = 0; - top.fgetc = config_buf_fgetc; - top.ungetc = config_buf_ungetc; - top.ftell = config_buf_ftell; + top.do_fgetc = config_buf_fgetc; + top.do_ungetc = config_buf_ungetc; + top.do_ftell = config_buf_ftell; return do_config_from(&top, fn, data); } @@ -1196,7 +1246,7 @@ static int store_aux(const char *key, const char *value, void *cb) return 1; } - store.offset[store.seen] = cf->ftell(cf); + store.offset[store.seen] = cf->do_ftell(cf); store.seen++; } break; @@ -1223,19 +1273,19 @@ static int store_aux(const char *key, const char *value, void *cb) * Do not increment matches: this is no match, but we * just made sure we are in the desired section. */ - store.offset[store.seen] = cf->ftell(cf); + store.offset[store.seen] = cf->do_ftell(cf); /* fallthru */ case SECTION_END_SEEN: case START: if (matches(key, value)) { - store.offset[store.seen] = cf->ftell(cf); + store.offset[store.seen] = cf->do_ftell(cf); store.state = KEY_SEEN; store.seen++; } else { if (strrchr(key, '.') - key == store.baselen && !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; - store.offset[store.seen] = cf->ftell(cf); + store.offset[store.seen] = cf->do_ftell(cf); } } } diff --git a/config.mak.uname b/config.mak.uname index b27f51d486..7d615314f4 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -95,7 +95,6 @@ ifeq ($(uname_S),Darwin) NO_MEMMEM = YesPlease USE_ST_TIMESPEC = YesPlease HAVE_DEV_TTY = YesPlease - NEEDS_CLIPPED_WRITE = YesPlease COMPAT_OBJS += compat/precompose_utf8.o BASIC_CFLAGS += -DPRECOMPOSE_UNICODE endif @@ -5,6 +5,7 @@ #include "refs.h" #include "run-command.h" #include "remote.h" +#include "connect.h" #include "url.h" static char *server_capabilities; diff --git a/connect.h b/connect.h new file mode 100644 index 0000000000..9dff25cad4 --- /dev/null +++ b/connect.h @@ -0,0 +1,13 @@ +#ifndef CONNECT_H +#define CONNECT_H + +#define CONNECT_VERBOSE (1u << 0) +extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); +extern int finish_connect(struct child_process *conn); +extern int git_connection_is_socket(struct child_process *conn); +extern int server_supports(const char *feature); +extern int parse_feature_request(const char *features, const char *feature); +extern const char *server_feature_value(const char *feature, int *len_ret); +extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret); + +#endif diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5da920ecd9..e1b7313072 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2580,7 +2580,7 @@ if [[ -n ${ZSH_VERSION-} ]]; then --*=*|*.) ;; *) c="$c " ;; esac - array[$#array+1]="$c" + array[${#array[@]}+1]="$c" done compset -P '*[=:]' compadd -Q -S '' -p "${2-}" -a -- array && _ret=0 diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index a81ef5a482..d6c61b2bde 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -84,6 +84,10 @@ # the colored output of "git status -sb" and are available only when # using __git_ps1 for PROMPT_COMMAND or precmd. +# check whether printf supports -v +__git_printf_supports_v= +printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1 + # stores the divergence from upstream in $p # used by GIT_PS1_SHOWUPSTREAM __git_ps1_show_upstream () @@ -433,7 +437,7 @@ __git_ps1 () local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p" if [ $pcmode = yes ]; then - if [[ -n ${ZSH_VERSION-} ]]; then + if [ "${__git_printf_supports_v-}" != yes ]; then gitstring=$(printf -- "$printf_format" "$gitstring") else printf -v gitstring -- "$printf_format" "$gitstring" diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts index d80f7d1b6e..fb6429b64b 100755 --- a/contrib/contacts/git-contacts +++ b/contrib/contacts/git-contacts @@ -59,11 +59,11 @@ sub import_commits { } sub get_blame { - my ($commits, $source, $start, $len, $from) = @_; - $len = 1 unless defined($len); - return if $len == 0; + my ($commits, $source, $from, $ranges) = @_; + return unless @$ranges; open my $f, '-|', - qw(git blame --porcelain -C), '-L', "$start,+$len", + qw(git blame --porcelain -C), + map({"-L$_->[0],+$_->[1]"} @$ranges), '--since', $since, "$from^", '--', $source or die; while (<$f>) { if (/^([0-9a-f]{40}) \d+ \d+ \d+$/) { @@ -76,8 +76,17 @@ sub get_blame { close $f; } +sub blame_sources { + my ($sources, $commits) = @_; + for my $s (keys %$sources) { + for my $id (keys %{$sources->{$s}}) { + get_blame($commits, $s, $id, $sources->{$s}{$id}); + } + } +} + sub scan_patches { - my ($commits, $id, $f) = @_; + my ($sources, $id, $f) = @_; my $source; while (<$f>) { if (/^From ([0-9a-f]{40}) Mon Sep 17 00:00:00 2001$/) { @@ -90,7 +99,8 @@ sub scan_patches { } elsif (/^--- /) { die "Cannot parse hunk source: $_\n"; } elsif (/^@@ -(\d+)(?:,(\d+))?/ && $source) { - get_blame($commits, $source, $1, $2, $id); + my $len = defined($2) ? $2 : 1; + push @{$sources->{$source}{$id}}, [$1, $len] if $len; } } } @@ -163,13 +173,16 @@ for (@ARGV) { } } -my %commits; +my %sources; for (@files) { - scan_patch_file(\%commits, $_); + scan_patch_file(\%sources, $_); } if (@rev_args) { - scan_rev_args(\%commits, \@rev_args) + scan_rev_args(\%sources, \@rev_args) } + +my %commits; +blame_sources(\%sources, \%commits); import_commits(\%commits); my $contacts = {}; diff --git a/contrib/examples/git-log.sh b/contrib/examples/git-log.sh new file mode 100755 index 0000000000..c2ea71cf14 --- /dev/null +++ b/contrib/examples/git-log.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# + +USAGE='[--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [git-rev-list options]' +SUBDIRECTORY_OK='Yes' +. git-sh-setup + +revs=$(git-rev-parse --revs-only --no-flags --default HEAD "$@") || exit +[ "$revs" ] || { + die "No HEAD ref" +} +git-rev-list --pretty $(git-rev-parse --default HEAD "$@") | +LESS=-S ${PAGER:-less} diff --git a/contrib/examples/git-whatchanged.sh b/contrib/examples/git-whatchanged.sh new file mode 100755 index 0000000000..1fb9feb348 --- /dev/null +++ b/contrib/examples/git-whatchanged.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]' +SUBDIRECTORY_OK='Yes' +. git-sh-setup + +diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit +case "$0" in +*whatchanged) + count= + test -z "$diff_tree_flags" && + diff_tree_flags=$(git-repo-config --get whatchanged.difftree) + diff_tree_default_flags='-c -M --abbrev' ;; +*show) + count=-n1 + test -z "$diff_tree_flags" && + diff_tree_flags=$(git-repo-config --get show.difftree) + diff_tree_default_flags='--cc --always' ;; +esac +test -z "$diff_tree_flags" && + diff_tree_flags="$diff_tree_default_flags" + +rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") && +diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") && + +eval "git-rev-list $count $rev_list_args" | +eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" | +LESS="$LESS -S" ${PAGER:-less} diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 153115029d..8ee410f843 100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -242,6 +242,9 @@ generate_email_header() cat <<-EOF To: $recipients Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: 8bit X-Git-Refname: $refname X-Git-Reftype: $refname_type X-Git-Oldrev: $oldrev @@ -471,7 +474,7 @@ generate_delete_branch_email() echo " was $oldrev" echo "" echo $LOGBEGIN - git show -s --pretty=oneline $oldrev + git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev echo $LOGEND } @@ -547,11 +550,11 @@ generate_atag_email() # performed on them if [ -n "$prevtag" ]; then # Show changes since the previous release - git rev-list --pretty=short "$prevtag..$newrev" | git shortlog + git shortlog "$prevtag..$newrev" else # No previous tag, show all the changes since time # began - git rev-list --pretty=short $newrev | git shortlog + git shortlog $newrev fi ;; *) @@ -571,7 +574,7 @@ generate_delete_atag_email() echo " was $oldrev" echo "" echo $LOGBEGIN - git show -s --pretty=oneline $oldrev + git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev echo $LOGEND } @@ -617,7 +620,7 @@ generate_general_email() echo "" if [ "$newrev_type" = "commit" ]; then echo $LOGBEGIN - git show --no-color --root -s --pretty=medium $newrev + git diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev echo $LOGEND else # What can we do here? The tag marks an object that is not @@ -636,7 +639,7 @@ generate_delete_general_email() echo " was $oldrev" echo "" echo $LOGBEGIN - git show -s --pretty=oneline $oldrev + git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev echo $LOGEND } diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index c3a3cac77b..1e0044b69f 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -674,7 +674,7 @@ def parse_reset(parser): parsed_refs[ref] = mark_to_rev(from_mark) def do_export(parser): - global parsed_refs, dirname + global parsed_refs, dirname, transports parser.next() @@ -699,7 +699,8 @@ def do_export(parser): branch.generate_revision_history(revid, marks.get_tip(name)) if name in peers: - peer = bzrlib.branch.Branch.open(peers[name]) + peer = bzrlib.branch.Branch.open(peers[name], + possible_transports=transports) try: peer.bzrdir.push_branch(branch, revision_id=revid) except bzrlib.errors.DivergedBranches: @@ -769,25 +770,28 @@ def do_list(parser): print def clone(path, remote_branch): + global transports try: - bdir = bzrlib.bzrdir.BzrDir.create(path) + bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports) except bzrlib.errors.AlreadyControlDirError: - bdir = bzrlib.bzrdir.BzrDir.open(path) + bdir = bzrlib.bzrdir.BzrDir.open(path, possible_transports=transports) repo = bdir.find_repository() repo.fetch(remote_branch.repository) return remote_branch.sprout(bdir, repository=repo) def get_remote_branch(name): - global dirname, branches + global dirname, branches, transports - remote_branch = bzrlib.branch.Branch.open(branches[name]) + remote_branch = bzrlib.branch.Branch.open(branches[name], + possible_transports=transports) if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport): return remote_branch branch_path = os.path.join(dirname, 'clone', name) try: - branch = bzrlib.branch.Branch.open(branch_path) + branch = bzrlib.branch.Branch.open(branch_path, + possible_transports=transports) except bzrlib.errors.NotBranchError: # clone branch = clone(branch_path, remote_branch) @@ -821,17 +825,19 @@ def find_branches(repo): yield name, branch.base def get_repo(url, alias): - global dirname, peer, branches + global dirname, peer, branches, transports normal_url = bzrlib.urlutils.normalize_url(url) - origin = bzrlib.bzrdir.BzrDir.open(url) + origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports) is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport) shared_path = os.path.join(gitdir, 'bzr') try: - shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path) + shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path, + possible_transports=transports) except bzrlib.errors.NotBranchError: - shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path) + shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path, + possible_transports=transports) try: shared_repo = shared_dir.open_repository() except bzrlib.errors.NoRepositoryPresent: @@ -844,7 +850,8 @@ def get_repo(url, alias): else: # check and remove old organization try: - bdir = bzrlib.bzrdir.BzrDir.open(clone_path) + bdir = bzrlib.bzrdir.BzrDir.open(clone_path, + possible_transports=transports) bdir.destroy_repository() except bzrlib.errors.NotBranchError: pass @@ -897,6 +904,7 @@ def main(args): global files_cache global is_tmp global branches, peers + global transports alias = args[1] url = args[2] @@ -909,6 +917,7 @@ def main(args): marks = None branches = {} peers = {} + transports = [] if alias[5:] == url: is_tmp = True diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 0194c67fb1..c27603965a 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -391,11 +391,24 @@ def get_repo(url, alias): os.makedirs(dirname) else: shared_path = os.path.join(gitdir, 'hg') - if not os.path.exists(shared_path): - try: - hg.clone(myui, {}, url, shared_path, update=False, pull=True) - except: - die('Repository error') + + # check and upgrade old organization + hg_path = os.path.join(shared_path, '.hg') + if os.path.exists(shared_path) and not os.path.exists(hg_path): + repos = os.listdir(shared_path) + for x in repos: + local_hg = os.path.join(shared_path, x, 'clone', '.hg') + if not os.path.exists(local_hg): + continue + if not os.path.exists(hg_path): + shutil.move(local_hg, hg_path) + shutil.rmtree(os.path.join(shared_path, x, 'clone')) + + # setup shared repo (if not there) + try: + hg.peer(myui, {}, shared_path, create=True) + except error.RepoError: + pass if not os.path.exists(dirname): os.makedirs(dirname) @@ -1124,7 +1137,7 @@ def do_option(parser): def fix_path(alias, repo, orig_url): url = urlparse.urlparse(orig_url, 'file') - if url.scheme != 'file' or os.path.isabs(url.path): + if url.scheme != 'file' or os.path.isabs(os.path.expanduser(url.path)): return abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url) cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url] diff --git a/diff-delta.c b/diff-delta.c index 93385e12ba..3797ce6041 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -155,7 +155,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) entries = 0xfffffffeU / RABIN_WINDOW; } hsize = entries / 4; - for (i = 4; (1u << i) < hsize && i < 31; i++); + for (i = 4; (1u << i) < hsize; i++); hsize = 1 << i; hmask = hsize - 1; diff --git a/diff-lib.c b/diff-lib.c index b6f4b21637..346cac651d 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -87,10 +87,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; - int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) ? CE_MATCH_RACY_IS_DIRTY : 0); + if (option & DIFF_SILENT_ON_REMOVED) + handle_deprecated_show_diff_q(&revs->diffopt); + diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/"); if (diff_unmerged_stage < 0) @@ -137,8 +139,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option) perror(ce->name); continue; } - if (silent_on_removed) - continue; wt_mode = 0; } dpath->mode = wt_mode; @@ -204,8 +204,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option) perror(ce->name); continue; } - if (silent_on_removed) - continue; diff_addremove(&revs->diffopt, '-', ce->ce_mode, ce->sha1, !is_null_sha1(ce->sha1), ce->name, 0); @@ -476,7 +474,6 @@ static int diff_cache(struct rev_info *revs, opts.dst_index = NULL; opts.pathspec = &revs->diffopt.pathspec; opts.pathspec->recursive = 1; - opts.pathspec->max_depth = -1; init_tree_desc(&t, tree->buffer, tree->size); return unpack_trees(1, &t, &opts); @@ -502,7 +499,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) struct rev_info revs; init_revisions(&revs, NULL); - init_pathspec(&revs.prune_data, opt->pathspec.raw); + copy_pathspec(&revs.prune_data, &opt->pathspec); revs.diffopt = *opt; if (diff_cache(&revs, tree_sha1, NULL, 1)) diff --git a/diff-no-index.c b/diff-no-index.c index e66fdf33da..e301aafff9 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -187,7 +187,7 @@ void diff_no_index(struct rev_info *revs, { int i, prefixlen; int no_index = 0; - unsigned options = 0; + unsigned deprecated_show_diff_q_option_used = 0; const char *paths[2]; /* Were we asked to do --no-index explicitly? */ @@ -225,7 +225,7 @@ void diff_no_index(struct rev_info *revs, if (!strcmp(argv[i], "--no-index")) i++; else if (!strcmp(argv[i], "-q")) { - options |= DIFF_SILENT_ON_REMOVED; + deprecated_show_diff_q_option_used = 1; i++; } else if (!strcmp(argv[i], "--")) @@ -260,6 +260,9 @@ void diff_no_index(struct rev_info *revs, revs->max_count = -2; diff_setup_done(&revs->diffopt); + if (deprecated_show_diff_q_option_used) + handle_deprecated_show_diff_q(&revs->diffopt); + setup_diff_pager(&revs->diffopt); DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); @@ -669,7 +669,7 @@ static void emit_rewrite_diff(const char *name_a, memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.color_diff = want_color(o->use_color); ecbdata.found_changesp = &o->found_changes; - ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a); + ecbdata.ws_rule = whitespace_rule(name_b); ecbdata.opt = o; if (ecbdata.ws_rule & WS_BLANK_AT_EOF) { mmfile_t mf1, mf2; @@ -2252,7 +2252,7 @@ static void builtin_diff(const char *name_a, (!two->mode || S_ISGITLINK(two->mode))) { const char *del = diff_get_color_opt(o, DIFF_FILE_OLD); const char *add = diff_get_color_opt(o, DIFF_FILE_NEW); - show_submodule_summary(o->file, one ? one->path : two->path, + show_submodule_summary(o->file, one->path ? one->path : two->path, line_prefix, one->sha1, two->sha1, two->dirty_submodule, meta, del, add, reset); @@ -2372,7 +2372,7 @@ static void builtin_diff(const char *name_a, ecbdata.label_path = lbl; ecbdata.color_diff = want_color(o->use_color); ecbdata.found_changesp = &o->found_changes; - ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a); + ecbdata.ws_rule = whitespace_rule(name_b); if (ecbdata.ws_rule & WS_BLANK_AT_EOF) check_blank_at_eof(&mf1, &mf2, &ecbdata); ecbdata.opt = o; @@ -3503,6 +3503,88 @@ static int parse_submodule_opt(struct diff_options *options, const char *value) return 1; } +static const char diff_status_letters[] = { + DIFF_STATUS_ADDED, + DIFF_STATUS_COPIED, + DIFF_STATUS_DELETED, + DIFF_STATUS_MODIFIED, + DIFF_STATUS_RENAMED, + DIFF_STATUS_TYPE_CHANGED, + DIFF_STATUS_UNKNOWN, + DIFF_STATUS_UNMERGED, + DIFF_STATUS_FILTER_AON, + DIFF_STATUS_FILTER_BROKEN, + '\0', +}; + +static unsigned int filter_bit['Z' + 1]; + +static void prepare_filter_bits(void) +{ + int i; + + if (!filter_bit[DIFF_STATUS_ADDED]) { + for (i = 0; diff_status_letters[i]; i++) + filter_bit[(int) diff_status_letters[i]] = (1 << i); + } +} + +static unsigned filter_bit_tst(char status, const struct diff_options *opt) +{ + return opt->filter & filter_bit[(int) status]; +} + +static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt) +{ + int i, optch; + + prepare_filter_bits(); + + /* + * If there is a negation e.g. 'd' in the input, and we haven't + * initialized the filter field with another --diff-filter, start + * from full set of bits, except for AON. + */ + if (!opt->filter) { + for (i = 0; (optch = optarg[i]) != '\0'; i++) { + if (optch < 'a' || 'z' < optch) + continue; + opt->filter = (1 << (ARRAY_SIZE(diff_status_letters) - 1)) - 1; + opt->filter &= ~filter_bit[DIFF_STATUS_FILTER_AON]; + break; + } + } + + for (i = 0; (optch = optarg[i]) != '\0'; i++) { + unsigned int bit; + int negate; + + if ('a' <= optch && optch <= 'z') { + negate = 1; + optch = toupper(optch); + } else { + negate = 0; + } + + bit = (0 <= optch && optch <= 'Z') ? filter_bit[optch] : 0; + if (!bit) + return optarg[i]; + if (negate) + opt->filter &= ~bit; + else + opt->filter |= bit; + } + return 0; +} + +/* Used only by "diff-files" and "diff --no-index" */ +void handle_deprecated_show_diff_q(struct diff_options *opt) +{ + warning("'diff -q' and 'diff-files -q' are deprecated."); + warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs."); + parse_diff_filter_opt("d", opt); +} + static void enable_patch_output(int *fmt) { *fmt &= ~DIFF_FORMAT_NO_OUTPUT; *fmt |= DIFF_FORMAT_PATCH; @@ -3732,7 +3814,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) return argcount; } else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) { - options->filter = optarg; + int offending = parse_diff_filter_opt(optarg, options); + if (offending) + die("unknown change class '%c' in --diff-filter=%s", + offending, optarg); return argcount; } else if (!strcmp(arg, "--abbrev")) @@ -4524,27 +4609,32 @@ free_queue: } } -static void diffcore_apply_filter(const char *filter) +static int match_filter(const struct diff_options *options, const struct diff_filepair *p) +{ + return (((p->status == DIFF_STATUS_MODIFIED) && + ((p->score && + filter_bit_tst(DIFF_STATUS_FILTER_BROKEN, options)) || + (!p->score && + filter_bit_tst(DIFF_STATUS_MODIFIED, options)))) || + ((p->status != DIFF_STATUS_MODIFIED) && + filter_bit_tst(p->status, options))); +} + +static void diffcore_apply_filter(struct diff_options *options) { int i; struct diff_queue_struct *q = &diff_queued_diff; struct diff_queue_struct outq; + DIFF_QUEUE_CLEAR(&outq); - if (!filter) + if (!options->filter) return; - if (strchr(filter, DIFF_STATUS_FILTER_AON)) { + if (filter_bit_tst(DIFF_STATUS_FILTER_AON, options)) { int found; for (i = found = 0; !found && i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (((p->status == DIFF_STATUS_MODIFIED) && - ((p->score && - strchr(filter, DIFF_STATUS_FILTER_BROKEN)) || - (!p->score && - strchr(filter, DIFF_STATUS_MODIFIED)))) || - ((p->status != DIFF_STATUS_MODIFIED) && - strchr(filter, p->status))) + if (match_filter(options, q->queue[i])) found++; } if (found) @@ -4562,14 +4652,7 @@ static void diffcore_apply_filter(const char *filter) /* Only the matching ones */ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - - if (((p->status == DIFF_STATUS_MODIFIED) && - ((p->score && - strchr(filter, DIFF_STATUS_FILTER_BROKEN)) || - (!p->score && - strchr(filter, DIFF_STATUS_MODIFIED)))) || - ((p->status != DIFF_STATUS_MODIFIED) && - strchr(filter, p->status))) + if (match_filter(options, p)) diff_q(&outq, p); else diff_free_filepair(p); @@ -4676,7 +4759,7 @@ void diffcore_std(struct diff_options *options) if (!options->found_follow) /* See try_to_follow_renames() in tree-diff.c */ diff_resolve_rename_copy(); - diffcore_apply_filter(options->filter); + diffcore_apply_filter(options); if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) DIFF_OPT_SET(options, HAS_CHANGES); @@ -5,6 +5,7 @@ #define DIFF_H #include "tree-walk.h" +#include "pathspec.h" struct rev_info; struct diff_options; @@ -103,12 +104,15 @@ enum diff_words_type { }; struct diff_options { - const char *filter; const char *orderfile; const char *pickaxe; const char *single_follow; const char *a_prefix, *b_prefix; unsigned flags; + + /* diff-filter bits */ + unsigned int filter; + int use_color; int context; int interhunkcontext; @@ -179,8 +183,6 @@ const char *diff_line_prefix(struct diff_options *); extern const char mime_boundary_leader[]; -extern void diff_tree_setup_paths(const char **paths, struct diff_options *); -extern void diff_tree_release_paths(struct diff_options *); extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt); extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, @@ -338,6 +340,8 @@ extern int parse_rename_score(const char **cp_p); extern long parse_algorithm_value(const char *value); +extern void handle_deprecated_show_diff_q(struct diff_options *); + extern int print_stat_summary(FILE *fp, int files, int insertions, int deletions); extern void setup_diff_pager(struct diff_options *); @@ -11,6 +11,7 @@ #include "dir.h" #include "refs.h" #include "wildmatch.h" +#include "pathspec.h" struct path_simplify { int len; @@ -51,26 +52,32 @@ int fnmatch_icase(const char *pattern, const char *string, int flags) return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0)); } -inline int git_fnmatch(const char *pattern, const char *string, - int flags, int prefix) +inline int git_fnmatch(const struct pathspec_item *item, + const char *pattern, const char *string, + int prefix) { - int fnm_flags = 0; - if (flags & GFNM_PATHNAME) - fnm_flags |= FNM_PATHNAME; if (prefix > 0) { - if (strncmp(pattern, string, prefix)) + if (ps_strncmp(item, pattern, string, prefix)) return FNM_NOMATCH; pattern += prefix; string += prefix; } - if (flags & GFNM_ONESTAR) { + if (item->flags & PATHSPEC_ONESTAR) { int pattern_len = strlen(++pattern); int string_len = strlen(string); return string_len < pattern_len || - strcmp(pattern, - string + string_len - pattern_len); + ps_strcmp(item, pattern, + string + string_len - pattern_len); } - return fnmatch(pattern, string, fnm_flags); + if (item->magic & PATHSPEC_GLOB) + return wildmatch(pattern, string, + WM_PATHNAME | + (item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0), + NULL); + else + /* wildmatch has not learned no FNM_PATHNAME mode yet */ + return fnmatch(pattern, string, + item->magic & PATHSPEC_ICASE ? FNM_CASEFOLD : 0); } static int fnmatch_icase_mem(const char *pattern, int patternlen, @@ -102,26 +109,40 @@ static int fnmatch_icase_mem(const char *pattern, int patternlen, return match_status; } -static size_t common_prefix_len(const char **pathspec) +static size_t common_prefix_len(const struct pathspec *pathspec) { - const char *n, *first; + int n; size_t max = 0; - int literal = limit_pathspec_to_literal(); - if (!pathspec) - return max; - - first = *pathspec; - while ((n = *pathspec++)) { - size_t i, len = 0; - for (i = 0; first == n || i < max; i++) { - char c = n[i]; - if (!c || c != first[i] || (!literal && is_glob_special(c))) + /* + * ":(icase)path" is treated as a pathspec full of + * wildcard. In other words, only prefix is considered common + * prefix. If the pathspec is abc/foo abc/bar, running in + * subdir xyz, the common prefix is still xyz, not xuz/abc as + * in non-:(icase). + */ + GUARD_PATHSPEC(pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ICASE); + + for (n = 0; n < pathspec->nr; n++) { + size_t i = 0, len = 0, item_len; + if (pathspec->items[n].magic & PATHSPEC_ICASE) + item_len = pathspec->items[n].prefix; + else + item_len = pathspec->items[n].nowildcard_len; + while (i < item_len && (n == 0 || i < max)) { + char c = pathspec->items[n].match[i]; + if (c != pathspec->items[0].match[i]) break; if (c == '/') len = i + 1; + i++; } - if (first == n || len < max) { + if (n == 0 || len < max) { max = len; if (!max) break; @@ -134,14 +155,14 @@ static size_t common_prefix_len(const char **pathspec) * Returns a copy of the longest leading path common among all * pathspecs. */ -char *common_prefix(const char **pathspec) +char *common_prefix(const struct pathspec *pathspec) { unsigned long len = common_prefix_len(pathspec); - return len ? xmemdupz(*pathspec, len) : NULL; + return len ? xmemdupz(pathspec->items[0].match, len) : NULL; } -int fill_directory(struct dir_struct *dir, const char **pathspec) +int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) { size_t len; @@ -152,7 +173,7 @@ int fill_directory(struct dir_struct *dir, const char **pathspec) len = common_prefix_len(pathspec); /* Read the directory and prune it */ - read_directory(dir, pathspec ? *pathspec : "", len, pathspec); + read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec); return len; } @@ -183,113 +204,6 @@ int within_depth(const char *name, int namelen, * * It returns 0 when there is no match. */ -static int match_one(const char *match, const char *name, int namelen) -{ - int matchlen; - int literal = limit_pathspec_to_literal(); - - /* If the match was just the prefix, we matched */ - if (!*match) - return MATCHED_RECURSIVELY; - - if (ignore_case) { - for (;;) { - unsigned char c1 = tolower(*match); - unsigned char c2 = tolower(*name); - if (c1 == '\0' || (!literal && is_glob_special(c1))) - break; - if (c1 != c2) - return 0; - match++; - name++; - namelen--; - } - } else { - for (;;) { - unsigned char c1 = *match; - unsigned char c2 = *name; - if (c1 == '\0' || (!literal && is_glob_special(c1))) - break; - if (c1 != c2) - return 0; - match++; - name++; - namelen--; - } - } - - /* - * If we don't match the matchstring exactly, - * we need to match by fnmatch - */ - matchlen = strlen(match); - if (strncmp_icase(match, name, matchlen)) { - if (literal) - return 0; - return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0; - } - - if (namelen == matchlen) - return MATCHED_EXACTLY; - if (match[matchlen-1] == '/' || name[matchlen] == '/') - return MATCHED_RECURSIVELY; - return 0; -} - -/* - * Given a name and a list of pathspecs, returns the nature of the - * closest (i.e. most specific) match of the name to any of the - * pathspecs. - * - * The caller typically calls this multiple times with the same - * pathspec and seen[] array but with different name/namelen - * (e.g. entries from the index) and is interested in seeing if and - * how each pathspec matches all the names it calls this function - * with. A mark is left in the seen[] array for each pathspec element - * indicating the closest type of match that element achieved, so if - * seen[n] remains zero after multiple invocations, that means the nth - * pathspec did not match any names, which could indicate that the - * user mistyped the nth pathspec. - */ -int match_pathspec(const char **pathspec, const char *name, int namelen, - int prefix, char *seen) -{ - int i, retval = 0; - - if (!pathspec) - return 1; - - name += prefix; - namelen -= prefix; - - for (i = 0; pathspec[i] != NULL; i++) { - int how; - const char *match = pathspec[i] + prefix; - if (seen && seen[i] == MATCHED_EXACTLY) - continue; - how = match_one(match, name, namelen); - if (how) { - if (retval < how) - retval = how; - if (seen && seen[i] < how) - seen[i] = how; - } - } - return retval; -} - -/* - * Does 'match' match the given name? - * A match is found if - * - * (1) the 'match' string is leading directory of 'name', or - * (2) the 'match' string is a wildcard and matches 'name', or - * (3) the 'match' string is exactly the same as 'name'. - * - * and the return value tells which case it was. - * - * It returns 0 when there is no match. - */ static int match_pathspec_item(const struct pathspec_item *item, int prefix, const char *name, int namelen) { @@ -297,11 +211,44 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, const char *match = item->match + prefix; int matchlen = item->len - prefix; + /* + * The normal call pattern is: + * 1. prefix = common_prefix_len(ps); + * 2. prune something, or fill_directory + * 3. match_pathspec_depth() + * + * 'prefix' at #1 may be shorter than the command's prefix and + * it's ok for #2 to match extra files. Those extras will be + * trimmed at #3. + * + * Suppose the pathspec is 'foo' and '../bar' running from + * subdir 'xyz'. The common prefix at #1 will be empty, thanks + * to "../". We may have xyz/foo _and_ XYZ/foo after #2. The + * user does not want XYZ/foo, only the "foo" part should be + * case-insensitive. We need to filter out XYZ/foo here. In + * other words, we do not trust the caller on comparing the + * prefix part when :(icase) is involved. We do exact + * comparison ourselves. + * + * Normally the caller (common_prefix_len() in fact) does + * _exact_ matching on name[-prefix+1..-1] and we do not need + * to check that part. Be defensive and check it anyway, in + * case common_prefix_len is changed, or a new caller is + * introduced that does not use common_prefix_len. + * + * If the penalty turns out too high when prefix is really + * long, maybe change it to + * strncmp(match, name, item->prefix - prefix) + */ + if (item->prefix && (item->magic & PATHSPEC_ICASE) && + strncmp(item->match, name - prefix, item->prefix)) + return 0; + /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; - if (matchlen <= namelen && !strncmp(match, name, matchlen)) { + if (matchlen <= namelen && !ps_strncmp(item, match, name, matchlen)) { if (matchlen == namelen) return MATCHED_EXACTLY; @@ -310,8 +257,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, } if (item->nowildcard_len < item->len && - !git_fnmatch(match, name, - item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + !git_fnmatch(item, match, name, item->nowildcard_len - prefix)) return MATCHED_FNMATCH; @@ -339,8 +285,17 @@ int match_pathspec_depth(const struct pathspec *ps, { int i, retval = 0; + GUARD_PATHSPEC(ps, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ICASE); + if (!ps->nr) { - if (!ps->recursive || ps->max_depth == -1) + if (!ps->recursive || + !(ps->magic & PATHSPEC_MAXDEPTH) || + ps->max_depth == -1) return MATCHED_RECURSIVELY; if (within_depth(name, namelen, 0, ps->max_depth)) @@ -357,7 +312,9 @@ int match_pathspec_depth(const struct pathspec *ps, if (seen && seen[i] == MATCHED_EXACTLY) continue; how = match_pathspec_item(ps->items+i, prefix, name, namelen); - if (ps->recursive && ps->max_depth != -1 && + if (ps->recursive && + (ps->magic & PATHSPEC_MAXDEPTH) && + ps->max_depth != -1 && how && how != MATCHED_FNMATCH) { int len = ps->items[i].len; if (name[len] == '/') @@ -380,7 +337,7 @@ int match_pathspec_depth(const struct pathspec *ps, /* * Return the length of the "simple" part of a path match limiter. */ -static int simple_length(const char *match) +int simple_length(const char *match) { int len = -1; @@ -392,7 +349,7 @@ static int simple_length(const char *match) } } -static int no_wildcard(const char *string) +int no_wildcard(const char *string) { return string[simple_length(string)] == '\0'; } @@ -472,15 +429,14 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size) unsigned long sz; enum object_type type; void *data; - struct index_state *istate = &the_index; len = strlen(path); - pos = index_name_pos(istate, path, len); + pos = cache_name_pos(path, len); if (pos < 0) return NULL; - if (!ce_skip_worktree(istate->cache[pos])) + if (!ce_skip_worktree(active_cache[pos])) return NULL; - data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz); + data = read_sha1_file(active_cache[pos]->sha1, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -927,13 +883,13 @@ enum exist_status { }; /* - * Do not use the alphabetically stored index to look up + * Do not use the alphabetically sorted index to look up * the directory name; instead, use the case insensitive * name hash. */ static enum exist_status directory_exists_in_index_icase(const char *dirname, int len) { - const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case); + const struct cache_entry *ce = cache_name_exists(dirname, len + 1, ignore_case); unsigned char endchar; if (!ce) @@ -1175,14 +1131,51 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, int dtype, struct dirent *de) { int exclude; + int has_path_in_index = !!cache_name_exists(path->buf, path->len, ignore_case); + if (dtype == DT_UNKNOWN) dtype = get_dtype(de, path->buf, path->len); /* Always exclude indexed files */ - if (dtype != DT_DIR && - cache_name_exists(path->buf, path->len, ignore_case)) + if (dtype != DT_DIR && has_path_in_index) return path_none; + /* + * When we are looking at a directory P in the working tree, + * there are three cases: + * + * (1) P exists in the index. Everything inside the directory P in + * the working tree needs to go when P is checked out from the + * index. + * + * (2) P does not exist in the index, but there is P/Q in the index. + * We know P will stay a directory when we check out the contents + * of the index, but we do not know yet if there is a directory + * P/Q in the working tree to be killed, so we need to recurse. + * + * (3) P does not exist in the index, and there is no P/Q in the index + * to require P to be a directory, either. Only in this case, we + * know that everything inside P will not be killed without + * recursing. + */ + if ((dir->flags & DIR_COLLECT_KILLED_ONLY) && + (dtype == DT_DIR) && + !has_path_in_index) { + /* + * NEEDSWORK: directory_exists_in_index_icase() + * assumes that one byte past the given path is + * readable and has '/', which needs to be fixed, but + * until then, work it around in the caller. + */ + strbuf_addch(path, '/'); + if (directory_exists_in_index(path->buf, path->len - 1) == + index_nonexistent) { + strbuf_setlen(path, path->len - 1); + return path_none; + } + strbuf_setlen(path, path->len - 1); + } + exclude = is_excluded(dir, path->buf, &dtype); /* @@ -1381,14 +1374,25 @@ static int treat_leading_path(struct dir_struct *dir, return rc; } -int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec) +int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec) { struct path_simplify *simplify; + /* + * Check out create_simplify() + */ + if (pathspec) + GUARD_PATHSPEC(pathspec, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ICASE); + if (has_symlink_leading_path(path, len)) return dir->nr; - simplify = create_simplify(pathspec); + simplify = create_simplify(pathspec ? pathspec->_raw : NULL); if (!len || treat_leading_path(dir, path, len, simplify)) read_directory_recursive(dir, path, len, 0, simplify); free_simplify(simplify); @@ -1568,71 +1572,6 @@ int remove_path(const char *name) return 0; } -static int pathspec_item_cmp(const void *a_, const void *b_) -{ - struct pathspec_item *a, *b; - - a = (struct pathspec_item *)a_; - b = (struct pathspec_item *)b_; - return strcmp(a->match, b->match); -} - -int init_pathspec(struct pathspec *pathspec, const char **paths) -{ - const char **p = paths; - int i; - - memset(pathspec, 0, sizeof(*pathspec)); - if (!p) - return 0; - while (*p) - p++; - pathspec->raw = paths; - pathspec->nr = p - paths; - if (!pathspec->nr) - return 0; - - pathspec->items = xmalloc(sizeof(struct pathspec_item)*pathspec->nr); - for (i = 0; i < pathspec->nr; i++) { - struct pathspec_item *item = pathspec->items+i; - const char *path = paths[i]; - - item->match = path; - item->len = strlen(path); - item->flags = 0; - if (limit_pathspec_to_literal()) { - item->nowildcard_len = item->len; - } else { - item->nowildcard_len = simple_length(path); - if (item->nowildcard_len < item->len) { - pathspec->has_wildcard = 1; - if (path[item->nowildcard_len] == '*' && - no_wildcard(path + item->nowildcard_len + 1)) - item->flags |= PATHSPEC_ONESTAR; - } - } - } - - qsort(pathspec->items, pathspec->nr, - sizeof(struct pathspec_item), pathspec_item_cmp); - - return 0; -} - -void free_pathspec(struct pathspec *pathspec) -{ - free(pathspec->items); - pathspec->items = NULL; -} - -int limit_pathspec_to_literal(void) -{ - static int flag = -1; - if (flag < 0) - flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0); - return flag; -} - /* * Frees memory within dir which was allocated for exclude lists and * the exclude_stack. Does not free dir itself. @@ -80,7 +80,8 @@ struct dir_struct { DIR_HIDE_EMPTY_DIRECTORIES = 1<<2, DIR_NO_GITLINKS = 1<<3, DIR_COLLECT_IGNORED = 1<<4, - DIR_SHOW_IGNORED_TOO = 1<<5 + DIR_SHOW_IGNORED_TOO = 1<<5, + DIR_COLLECT_KILLED_ONLY = 1<<6 } flags; struct dir_entry **entries; struct dir_entry **ignored; @@ -128,15 +129,16 @@ struct dir_struct { #define MATCHED_RECURSIVELY 1 #define MATCHED_FNMATCH 2 #define MATCHED_EXACTLY 3 -extern char *common_prefix(const char **pathspec); -extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); +extern int simple_length(const char *match); +extern int no_wildcard(const char *string); +extern char *common_prefix(const struct pathspec *pathspec); extern int match_pathspec_depth(const struct pathspec *pathspec, const char *name, int namelen, int prefix, char *seen); extern int within_depth(const char *name, int namelen, int depth, int max_depth); -extern int fill_directory(struct dir_struct *dir, const char **pathspec); -extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec); +extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec); +extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec); extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, struct exclude_list *el); @@ -198,10 +200,9 @@ extern int fnmatch_icase(const char *pattern, const char *string, int flags); /* * The prefix part of pattern must not contains wildcards. */ -#define GFNM_PATHNAME 1 /* similar to FNM_PATHNAME */ -#define GFNM_ONESTAR 2 /* there is only _one_ wildcard, a star */ - -extern int git_fnmatch(const char *pattern, const char *string, - int flags, int prefix); +struct pathspec_item; +extern int git_fnmatch(const struct pathspec_item *item, + const char *pattern, const char *string, + int prefix); #endif @@ -37,7 +37,7 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en return error("Terminal is dumb, but EDITOR unset"); if (strcmp(editor, ":")) { - const char *args[] = { editor, path, NULL }; + const char *args[] = { editor, real_path(path), NULL }; struct child_process p; int ret, sig; diff --git a/fast-import.c b/fast-import.c index 23f625f561..21db3fc46d 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1568,7 +1568,8 @@ static int tree_content_set( static int tree_content_remove( struct tree_entry *root, const char *p, - struct tree_entry *backup_leaf) + struct tree_entry *backup_leaf, + int allow_root) { struct tree_content *t; const char *slash1; @@ -1583,6 +1584,12 @@ static int tree_content_remove( if (!root->tree) load_tree(root); + + if (!*p && allow_root) { + e = root; + goto del_entry; + } + t = root->tree; for (i = 0; i < t->entry_count; i++) { e = t->entries[i]; @@ -1599,7 +1606,7 @@ static int tree_content_remove( goto del_entry; if (!e->tree) load_tree(e); - if (tree_content_remove(e, slash1 + 1, backup_leaf)) { + if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) { for (n = 0; n < e->tree->entry_count; n++) { if (e->tree->entries[n]->versions[1].mode) { hashclr(root->versions[1].sha1); @@ -1629,7 +1636,8 @@ del_entry: static int tree_content_get( struct tree_entry *root, const char *p, - struct tree_entry *leaf) + struct tree_entry *leaf, + int allow_root) { struct tree_content *t; const char *slash1; @@ -1641,31 +1649,39 @@ static int tree_content_get( n = slash1 - p; else n = strlen(p); - if (!n) + if (!n && !allow_root) die("Empty path component found in input"); if (!root->tree) load_tree(root); + + if (!n) { + e = root; + goto found_entry; + } + t = root->tree; for (i = 0; i < t->entry_count; i++) { e = t->entries[i]; if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) { - if (!slash1) { - memcpy(leaf, e, sizeof(*leaf)); - if (e->tree && is_null_sha1(e->versions[1].sha1)) - leaf->tree = dup_tree_content(e->tree); - else - leaf->tree = NULL; - return 1; - } + if (!slash1) + goto found_entry; if (!S_ISDIR(e->versions[1].mode)) return 0; if (!e->tree) load_tree(e); - return tree_content_get(e, slash1 + 1, leaf); + return tree_content_get(e, slash1 + 1, leaf, 0); } } return 0; + +found_entry: + memcpy(leaf, e, sizeof(*leaf)); + if (e->tree && is_null_sha1(e->versions[1].sha1)) + leaf->tree = dup_tree_content(e->tree); + else + leaf->tree = NULL; + return 1; } static int update_branch(struct branch *b) @@ -2179,7 +2195,7 @@ static uintmax_t do_change_note_fanout( } /* Rename fullpath to realpath */ - if (!tree_content_remove(orig_root, fullpath, &leaf)) + if (!tree_content_remove(orig_root, fullpath, &leaf, 0)) die("Failed to remove path %s", fullpath); tree_content_set(orig_root, realpath, leaf.versions[1].sha1, @@ -2314,7 +2330,7 @@ static void file_change_m(struct branch *b) /* Git does not track empty, non-toplevel directories. */ if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) { - tree_content_remove(&b->branch_tree, p, NULL); + tree_content_remove(&b->branch_tree, p, NULL, 0); return; } @@ -2375,7 +2391,7 @@ static void file_change_d(struct branch *b) die("Garbage after path in: %s", command_buf.buf); p = uq.buf; } - tree_content_remove(&b->branch_tree, p, NULL); + tree_content_remove(&b->branch_tree, p, NULL, 1); } static void file_change_cr(struct branch *b, int rename) @@ -2413,9 +2429,9 @@ static void file_change_cr(struct branch *b, int rename) memset(&leaf, 0, sizeof(leaf)); if (rename) - tree_content_remove(&b->branch_tree, s, &leaf); + tree_content_remove(&b->branch_tree, s, &leaf, 1); else - tree_content_get(&b->branch_tree, s, &leaf); + tree_content_get(&b->branch_tree, s, &leaf, 1); if (!leaf.versions[1].mode) die("Path %s not in branch", s); if (!*d) { /* C "path/to/subdir" "" */ @@ -2521,7 +2537,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout) } construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path); - if (tree_content_remove(&b->branch_tree, path, NULL)) + if (tree_content_remove(&b->branch_tree, path, NULL, 0)) b->num_notes--; if (is_null_sha1(sha1)) @@ -3051,6 +3067,8 @@ static void parse_ls(struct branch *b) struct object_entry *e = parse_treeish_dataref(&p); root = new_tree_entry(); hashcpy(root->versions[1].sha1, e->idx.sha1); + if (!is_null_sha1(root->versions[1].sha1)) + root->versions[1].mode = S_IFDIR; load_tree(root); if (*p++ != ' ') die("Missing space after tree-ish: %s", command_buf.buf); @@ -3065,7 +3083,7 @@ static void parse_ls(struct branch *b) die("Garbage after path in: %s", command_buf.buf); p = uq.buf; } - tree_content_get(root, p, &leaf); + tree_content_get(root, p, &leaf, 1); /* * A directory in preparation would have a sha1 of zero * until it is saved. Save, for simplicity. diff --git a/fetch-pack.c b/fetch-pack.c index 6684348c0e..094267fd80 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -9,6 +9,7 @@ #include "fetch-pack.h" #include "remote.h" #include "run-command.h" +#include "connect.h" #include "transport.h" #include "version.h" #include "prio-queue.h" @@ -897,6 +898,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, packet_flush(fd[1]); if (args->depth > 0) setup_alternate_shallow(); + else + alternate_shallow_file = NULL; if (get_pack(args, fd, pack_lockfile)) die("git fetch-pack: fetch failed."); @@ -987,7 +990,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args, } ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile); - if (alternate_shallow_file) { + if (args->depth > 0 && alternate_shallow_file) { if (*alternate_shallow_file == '\0') { /* --unshallow */ unlink_or_warn(git_path("shallow")); rollback_lock_file(&shallow_lock); diff --git a/fetch-pack.h b/fetch-pack.h index 40f08bab24..461cbf39b2 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -2,6 +2,7 @@ #define FETCH_PACK_H #include "string-list.h" +#include "run-command.h" struct fetch_pack_args { const char *uploadpack; diff --git a/git-compat-util.h b/git-compat-util.h index 115cb1da42..9549de6318 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -185,11 +185,6 @@ typedef unsigned long uintptr_t; #define probe_utf8_pathname_composition(a,b) #endif -#ifdef NEEDS_CLIPPED_WRITE -ssize_t clipped_write(int fildes, const void *buf, size_t nbyte); -#define write(x,y,z) clipped_write((x),(y),(z)) -#endif - #ifdef MKDIR_WO_TRAILING_SLASH #define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b)) extern int compat_mkdir_wo_trailing_slash(const char*, mode_t); @@ -330,6 +325,16 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); +#ifndef NO_OPENSSL +#ifdef APPLE_COMMON_CRYPTO +#include "compat/apple-common-crypto.h" +#else +#include <openssl/evp.h> +#include <openssl/hmac.h> +#endif /* APPLE_COMMON_CRYPTO */ +#include <openssl/x509v3.h> +#endif /* NO_OPENSSL */ + /* * Let callers be aware of the constant return value; this can help * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though, @@ -514,7 +519,7 @@ int inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, size_t size); #endif -extern void release_pack_memory(size_t, int); +extern void release_pack_memory(size_t); typedef void (*try_to_free_t)(size_t); extern try_to_free_t set_try_to_free_routine(try_to_free_t); @@ -2180,9 +2180,13 @@ class P4Sync(Command, P4UserMap): git_mode = "100755" if type_base == "symlink": git_mode = "120000" - # p4 print on a symlink contains "target\n"; remove the newline + # p4 print on a symlink sometimes contains "target\n"; + # if it does, remove the newline data = ''.join(contents) - contents = [data[:-1]] + if data[-1] == '\n': + contents = [data[:-1]] + else: + contents = [data] if type_base == "utf16": # p4 delivers different text in the python output to -G diff --git a/git-pull.sh b/git-pull.sh index f0df41c841..e11d9a0580 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -4,7 +4,7 @@ # # Fetch one or more remote refs and merge it/them into the current HEAD. -USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...' +USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...' LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.' SUBDIRECTORY_OK=Yes OPTIONS_SPEC= @@ -38,15 +38,19 @@ Please, commit your changes before you can merge.")" test -z "$(git ls-files -u)" || die_conflict test -f "$GIT_DIR/MERGE_HEAD" && die_merge +bool_or_string_config () { + git config --bool "$1" 2>/dev/null || git config "$1" +} + strategy_args= diffstat= no_commit= squash= no_ff= ff_only= log_arg= verbosity= progress= recurse_submodules= verify_signatures= -merge_args= edit= +merge_args= edit= rebase_args= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short="${curr_branch#refs/heads/}" -rebase=$(git config --bool branch.$curr_branch_short.rebase) +rebase=$(bool_or_string_config branch.$curr_branch_short.rebase) if test -z "$rebase" then - rebase=$(git config --bool pull.rebase) + rebase=$(bool_or_string_config pull.rebase) fi dry_run= while : @@ -110,6 +114,9 @@ do esac merge_args="$merge_args$xx " ;; + -r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*) + rebase="${1#*=}" + ;; -r|--r|--re|--reb|--reba|--rebas|--rebase) rebase=true ;; @@ -145,6 +152,20 @@ do shift done +case "$rebase" in +preserve) + rebase=true + rebase_args=--preserve-merges + ;; +true|false|'') + ;; +*) + echo "Invalid value for --rebase, should be true, false, or preserve" + usage + exit 1 + ;; +esac + error_on_no_merge_candidates () { exec >&2 for opt @@ -292,7 +313,7 @@ fi merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit case "$rebase" in true) - eval="git-rebase $diffstat $strategy_args $merge_args $verbosity" + eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity" eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}" ;; *) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 83d6d4676b..10bf318d0d 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -352,8 +352,9 @@ pick_one_preserving_merges () { msg_content="$(commit_message $sha1)" # No point in merging the first parent, that's HEAD new_parents=${new_parents# $first_parent} + merge_args="--no-log --no-ff" if ! do_with_author output eval \ - 'git merge --no-ff $strategy_args -m "$msg_content" $new_parents' + 'git merge $merge_args $strategy_args -m "$msg_content" $new_parents' then printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG die_with_patch $sha1 "Error redoing merge $sha1" @@ -671,7 +672,7 @@ skip_unnecessary_picks () { ;; esac ;; - 3,#*|3,) + 3,"$comment_char"*|3,) # copy comments ;; *) @@ -689,6 +690,32 @@ skip_unnecessary_picks () { die "Could not skip unnecessary pick commands" } +transform_todo_ids () { + while read -r command rest + do + case "$command" in + "$comment_char"* | exec) + # Be careful for oddball commands like 'exec' + # that do not have a SHA-1 at the beginning of $rest. + ;; + *) + sha1=$(git rev-parse --verify --quiet "$@" ${rest%% *}) && + rest="$sha1 ${rest#* }" + ;; + esac + printf '%s\n' "$command${rest:+ }$rest" + done <"$todo" >"$todo.new" && + mv -f "$todo.new" "$todo" +} + +expand_todo_ids() { + transform_todo_ids +} + +collapse_todo_ids() { + transform_todo_ids --short=7 +} + # Rearrange the todo list that has both "pick sha1 msg" and # "pick sha1 fixup!/squash! msg" appears in it so that the latter # comes immediately after the former, and change "pick" to @@ -841,6 +868,7 @@ skip) edit-todo) git stripspace --strip-comments <"$todo" >"$todo".new mv -f "$todo".new "$todo" + collapse_todo_ids append_todo_help git stripspace --comment-lines >>"$todo" <<\EOF @@ -852,6 +880,7 @@ EOF git_sequence_editor "$todo" || die "Could not execute editor" + expand_todo_ids exit ;; @@ -1008,6 +1037,8 @@ git_sequence_editor "$todo" || has_action "$todo" || die_abort "Nothing to do" +expand_todo_ids + test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 7a964ad2ff..e15be51636 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -53,7 +53,7 @@ die () { die_with_status () { status=$1 shift - echo >&2 "$*" + printf >&2 '%s\n' "$*" exit "$status" } @@ -147,6 +147,18 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1); if (envchanged) *envchanged = 1; + } else if (!strcmp(cmd, "--glob-pathspecs")) { + setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; + } else if (!strcmp(cmd, "--noglob-pathspecs")) { + setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; + } else if (!strcmp(cmd, "--icase-pathspecs")) { + setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; } else if (!strcmp(cmd, "--shallow-file")) { (*argv)++; (*argc)--; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index f429f75897..b5d156f7d0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -4035,8 +4035,8 @@ sub print_search_form { $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" . $cgi->popup_menu(-name => 'st', -default => 'commit', -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) . - $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) . - " search:\n", + " " . $cgi->a({-href => href(action=>"search_help"), + -title => "search help" }, "?") . " search:\n", $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" . "<span title=\"Extended regular expression\">" . $cgi->checkbox(-name => 'sr', -value => 1, -label => 're', @@ -6468,7 +6468,7 @@ sub git_summary { print "<div class=\"title\"> </div>\n"; print "<table class=\"projects_list\">\n" . "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n"; - unless ($omit_owner) { + if ($owner and not $omit_owner) { print "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n"; } if (defined $cd{'rfc2822'}) { diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css index cb86d2d029..3b4d833823 100644 --- a/gitweb/static/gitweb.css +++ b/gitweb/static/gitweb.css @@ -68,12 +68,13 @@ div.page_path { } div.page_footer { - height: 17px; + height: 22px; padding: 4px 8px; background-color: #d9d8d1; } div.page_footer_text { + line-height: 22px; float: left; color: #555555; font-style: italic; @@ -548,8 +549,7 @@ a.linenr { a.rss_logo { float: right; - padding: 3px 0px; - width: 35px; + padding: 3px 5px; line-height: 10px; border: 1px solid; border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; @@ -3,6 +3,7 @@ #include "sideband.h" #include "run-command.h" #include "url.h" +#include "urlmatch.h" #include "credential.h" #include "version.h" #include "pkt-line.h" @@ -45,6 +46,7 @@ static long curl_low_speed_time = -1; static int curl_ftp_no_epsv; static const char *curl_http_proxy; static const char *curl_cookie_file; +static int curl_save_cookies; static struct credential http_auth = CREDENTIAL_INIT; static int http_proactive_auth; static const char *user_agent; @@ -160,8 +162,7 @@ static int http_options(const char *var, const char *value, void *cb) if (!strcmp("http.sslcainfo", var)) return git_config_string(&ssl_cainfo, var, value); if (!strcmp("http.sslcertpasswordprotected", var)) { - if (git_config_bool(var, value)) - ssl_cert_password_required = 1; + ssl_cert_password_required = git_config_bool(var, value); return 0; } if (!strcmp("http.ssltry", var)) { @@ -200,6 +201,10 @@ static int http_options(const char *var, const char *value, void *cb) if (!strcmp("http.cookiefile", var)) return git_config_string(&curl_cookie_file, var, value); + if (!strcmp("http.savecookies", var)) { + curl_save_cookies = git_config_bool(var, value); + return 0; + } if (!strcmp("http.postbuffer", var)) { http_post_buffer = git_config_int(var, value); @@ -341,10 +346,20 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) { char *low_speed_limit; char *low_speed_time; + char *normalized_url; + struct urlmatch_config config = { STRING_LIST_INIT_DUP }; + + config.section = "http"; + config.key = NULL; + config.collect_fn = http_options; + config.cascade_fn = git_default_config; + config.cb = NULL; http_is_verbose = 0; + normalized_url = url_normalize(url, &config.url); - git_config(http_options, NULL); + git_config(urlmatch_config_entry, &config); + free(normalized_url); curl_global_init(CURL_GLOBAL_ALL); @@ -513,6 +528,8 @@ struct active_request_slot *get_active_slot(void) slot->callback_data = NULL; slot->callback_func = NULL; curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file); + if (curl_save_cookies) + curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header); curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL); diff --git a/imap-send.c b/imap-send.c index d6b65e204c..6f5cc4f782 100644 --- a/imap-send.c +++ b/imap-send.c @@ -28,20 +28,6 @@ #include "prompt.h" #ifdef NO_OPENSSL typedef void *SSL; -#else -#ifdef APPLE_COMMON_CRYPTO -#include <CommonCrypto/CommonHMAC.h> -#define HMAC_CTX CCHmacContext -#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len) -#define HMAC_Update CCHmacUpdate -#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash) -#define HMAC_CTX_cleanup(ignore) -#define EVP_md5() kCCHmacAlgMD5 -#else -#include <openssl/evp.h> -#include <openssl/hmac.h> -#endif -#include <openssl/x509v3.h> #endif static const char imap_send_usage[] = "git imap-send < <mbox>"; diff --git a/line-log.c b/line-log.c index c2d01dccc2..8b6e497b3f 100644 --- a/line-log.c +++ b/line-log.c @@ -23,7 +23,7 @@ static void range_set_grow(struct range_set *rs, size_t extra) /* Either initialization would be fine */ #define RANGE_SET_INIT {0} -static void range_set_init(struct range_set *rs, size_t prealloc) +void range_set_init(struct range_set *rs, size_t prealloc) { rs->alloc = rs->nr = 0; rs->ranges = NULL; @@ -31,7 +31,7 @@ static void range_set_init(struct range_set *rs, size_t prealloc) range_set_grow(rs, prealloc); } -static void range_set_release(struct range_set *rs) +void range_set_release(struct range_set *rs) { free(rs->ranges); rs->alloc = rs->nr = 0; @@ -56,7 +56,7 @@ static void range_set_move(struct range_set *dst, struct range_set *src) } /* tack on a _new_ range _at the end_ */ -static void range_set_append_unsafe(struct range_set *rs, long a, long b) +void range_set_append_unsafe(struct range_set *rs, long a, long b) { assert(a <= b); range_set_grow(rs, 1); @@ -65,7 +65,7 @@ static void range_set_append_unsafe(struct range_set *rs, long a, long b) rs->nr++; } -static void range_set_append(struct range_set *rs, long a, long b) +void range_set_append(struct range_set *rs, long a, long b) { assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a); range_set_append_unsafe(rs, a, b); @@ -107,7 +107,7 @@ static void range_set_check_invariants(struct range_set *rs) * In-place pass of sorting and merging the ranges in the range set, * to establish the invariants when we get the ranges from the user */ -static void sort_and_merge_range_set(struct range_set *rs) +void sort_and_merge_range_set(struct range_set *rs) { int i; int o = 0; /* output cursor */ @@ -291,7 +291,6 @@ static void line_log_data_insert(struct line_log_data **list, if (p) { range_set_append_unsafe(&p->ranges, begin, end); - sort_and_merge_range_set(&p->ranges); free(path); return; } @@ -299,7 +298,6 @@ static void line_log_data_insert(struct line_log_data **list, p = xcalloc(1, sizeof(struct line_log_data)); p->path = path; range_set_append(&p->ranges, begin, end); - sort_and_merge_range_set(&p->ranges); if (ip) { p->next = ip->next; ip->next = p; @@ -566,12 +564,14 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args) struct nth_line_cb cb_data; struct string_list_item *item; struct line_log_data *ranges = NULL; + struct line_log_data *p; for_each_string_list_item(item, args) { const char *name_part, *range_part; char *full_name; struct diff_filespec *spec; long begin = 0, end = 0; + long anchor; name_part = skip_range_arg(item->string); if (!name_part || *name_part != ':' || !name_part[1]) @@ -590,17 +590,23 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args) cb_data.lines = lines; cb_data.line_ends = ends; + p = search_line_log_data(ranges, full_name, NULL); + if (p && p->ranges.nr) + anchor = p->ranges.ranges[p->ranges.nr - 1].end + 1; + else + anchor = 1; + if (parse_range_arg(range_part, nth_line, &cb_data, - lines, &begin, &end, + lines, anchor, &begin, &end, full_name)) die("malformed -L argument '%s'", range_part); + if (lines < end || ((lines || begin) && lines < begin)) + die("file %s has only %lu lines", name_part, lines); if (begin < 1) begin = 1; if (end < 1) end = lines; begin--; - if (lines < end || lines < begin) - die("file %s has only %ld lines", name_part, lines); line_log_data_insert(&ranges, full_name, begin, end); free_filespec(spec); @@ -608,6 +614,9 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args) ends = NULL; } + for (p = ranges; p; p = p->next) + sort_and_merge_range_set(&p->ranges); + return ranges; } @@ -751,7 +760,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list r = r->next; } paths[count] = NULL; - init_pathspec(&rev->diffopt.pathspec, paths); + parse_pathspec(&rev->diffopt.pathspec, 0, 0, "", paths); free(paths); } } diff --git a/line-log.h b/line-log.h index 8bea45fd78..a9212d84e4 100644 --- a/line-log.h +++ b/line-log.h @@ -25,6 +25,18 @@ struct diff_ranges { struct range_set target; }; +extern void range_set_init(struct range_set *, size_t prealloc); +extern void range_set_release(struct range_set *); +/* Range includes start; excludes end */ +extern void range_set_append_unsafe(struct range_set *, long start, long end); +/* New range must begin at or after end of last added range */ +extern void range_set_append(struct range_set *, long start, long end); +/* + * In-place pass of sorting and merging the ranges in the range set, + * to sort and make the ranges disjoint. + */ +extern void sort_and_merge_range_set(struct range_set *); + /* Linked list of interesting files and their associated ranges. The * list must be kept sorted by path. * diff --git a/line-range.c b/line-range.c index 3942475c2f..de4e32f942 100644 --- a/line-range.c +++ b/line-range.c @@ -6,6 +6,18 @@ /* * Parse one item in the -L option + * + * 'begin' is applicable only to relative range anchors. Absolute anchors + * ignore this value. + * + * When parsing "-L A,B", parse_loc() is called once for A and once for B. + * + * When parsing A, 'begin' must be a negative number, the absolute value of + * which is the line at which relative start-of-range anchors should be + * based. Beginning of file is represented by -1. + * + * When parsing B, 'begin' must be the positive line number immediately + * following the line computed for 'A'. */ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line, void *data, long lines, long begin, long *ret) @@ -21,11 +33,13 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line, * for 20 lines, or "-L <something>,-5" for 5 lines ending at * <something>. */ - if (1 < begin && (spec[0] == '+' || spec[0] == '-')) { + if (1 <= begin && (spec[0] == '+' || spec[0] == '-')) { num = strtol(spec + 1, &term, 10); if (term != spec + 1) { if (!ret) return term; + if (num == 0) + die("-L invalid empty range"); if (spec[0] == '-') num = 0 - num; if (0 < num) @@ -40,10 +54,23 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line, } num = strtol(spec, &term, 10); if (term != spec) { - if (ret) + if (ret) { + if (num <= 0) + die("-L invalid line number: %ld", num); *ret = num; + } return term; } + + if (begin < 0) { + if (spec[0] != '^') + begin = -begin; + else { + begin = 1; + spec++; + } + } + if (spec[0] != '/') return spec; @@ -83,7 +110,8 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line, else { char errbuf[1024]; regerror(reg_error, ®exp, errbuf, 1024); - die("-L parameter '%s': %s", spec + 1, errbuf); + die("-L parameter '%s' starting at line %ld: %s", + spec + 1, begin + 1, errbuf); } } @@ -136,7 +164,7 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char } static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb, - void *cb_data, long lines, long *begin, long *end, + void *cb_data, long lines, long anchor, long *begin, long *end, const char *path) { char *pattern; @@ -148,6 +176,11 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_ int reg_error; regex_t regexp; + if (*arg == '^') { + anchor = 1; + arg++; + } + assert(*arg == ':'); term = arg+1; while (*term && *term != ':') { @@ -162,7 +195,8 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_ pattern = xstrndup(arg+1, term-(arg+1)); - start = nth_line_cb(cb_data, 0); + anchor--; /* input is in human terms */ + start = nth_line_cb(cb_data, anchor); drv = userdiff_find_by_path(path); if (drv && drv->funcname.pattern) { @@ -180,7 +214,8 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_ p = find_funcname_matching_regexp(xecfg, (char*) start, ®exp); if (!p) - die("-L parameter '%s': no match", pattern); + die("-L parameter '%s' starting at line %ld: no match", + pattern, anchor + 1); *begin = 0; while (p > nth_line_cb(cb_data, *begin)) (*begin)++; @@ -208,19 +243,24 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_ } int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, - void *cb_data, long lines, long *begin, long *end, - const char *path) + void *cb_data, long lines, long anchor, + long *begin, long *end, const char *path) { *begin = *end = 0; - if (*arg == ':') { - arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path); + if (anchor < 1) + anchor = 1; + if (anchor > lines) + anchor = lines + 1; + + if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':')) { + arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, anchor, begin, end, path); if (!arg || *arg) return -1; return 0; } - arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin); + arg = parse_loc(arg, nth_line_cb, cb_data, lines, -anchor, begin); if (*arg == ',') arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end); @@ -238,8 +278,8 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, const char *skip_range_arg(const char *arg) { - if (*arg == ':') - return parse_range_funcname(arg, NULL, NULL, 0, NULL, NULL, NULL); + if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':')) + return parse_range_funcname(arg, NULL, NULL, 0, 0, NULL, NULL, NULL); arg = parse_loc(arg, NULL, NULL, 0, -1, NULL); diff --git a/line-range.h b/line-range.h index ae3d0123b4..83ba3c25e8 100644 --- a/line-range.h +++ b/line-range.h @@ -9,6 +9,9 @@ * line 'lno' inside the 'cb_data'. The caller is expected to already * have a suitable map at hand to make this a constant-time lookup. * + * 'anchor' is the 1-based line at which relative range specifications + * should be anchored. Absolute ranges are unaffected by this value. + * * Returns 0 in case of success and -1 if there was an error. The * actual range is stored in *begin and *end. The counting starts * at 1! In case of error, the caller should show usage message. @@ -18,7 +21,7 @@ typedef const char *(*nth_line_fn_t)(void *data, long lno); extern int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, - void *cb_data, long lines, + void *cb_data, long lines, long anchor, long *begin, long *end, const char *path); diff --git a/log-tree.c b/log-tree.c index a49d8e895d..8534d91826 100644 --- a/log-tree.c +++ b/log-tree.c @@ -738,7 +738,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log sha1 = commit->tree->object.sha1; /* Root commit? */ - parents = commit->parents; + parents = get_saved_parents(opt, commit); if (!parents) { if (opt->show_root_diff) { diff_root_tree_sha1(sha1, "", &opt->diffopt); @@ -153,8 +153,7 @@ static void read_mailmap_line(struct string_list *map, char *buffer, if (!strncmp(buffer, abbrev, abblen)) { char *cp; - if (repo_abbrev) - free(*repo_abbrev); + free(*repo_abbrev); *repo_abbrev = xmalloc(len); for (cp = buffer + abblen; isspace(*cp); cp++) @@ -193,20 +192,17 @@ static int read_mailmap_file(struct string_list *map, const char *filename, return 0; } -static void read_mailmap_buf(struct string_list *map, - const char *buf, unsigned long len, - char **repo_abbrev) +static void read_mailmap_string(struct string_list *map, char *buf, + char **repo_abbrev) { - while (len) { - const char *end = strchrnul(buf, '\n'); - unsigned long linelen = end - buf + 1; - char *line = xmemdupz(buf, linelen); + while (*buf) { + char *end = strchrnul(buf, '\n'); - read_mailmap_line(map, line, repo_abbrev); + if (*end) + *end++ = '\0'; - free(line); - buf += linelen; - len -= linelen; + read_mailmap_line(map, buf, repo_abbrev); + buf = end; } } @@ -230,7 +226,7 @@ static int read_mailmap_blob(struct string_list *map, if (type != OBJ_BLOB) return error("mailmap is not a blob: %s", name); - read_mailmap_buf(map, buf, size, repo_abbrev); + read_mailmap_string(map, buf, repo_abbrev); free(buf); return 0; diff --git a/merge-recursive.c b/merge-recursive.c index f95933b0aa..40eb840a52 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -298,7 +298,7 @@ static int get_files_dirs(struct merge_options *o, struct tree *tree) { int n; struct pathspec match_all; - init_pathspec(&match_all, NULL); + memset(&match_all, 0, sizeof(match_all)); if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o)) return 0; n = o->current_file_set.nr + o->current_directory_set.nr; diff --git a/notes-merge.c b/notes-merge.c index ab18857074..94a1a8ae46 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -170,7 +170,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, sha1_to_hex(mp->remote)); } diff_flush(&opt); - diff_tree_release_paths(&opt); + free_pathspec(&opt.pathspec); *num_changes = len; return changes; @@ -256,7 +256,7 @@ static void diff_tree_local(struct notes_merge_options *o, sha1_to_hex(mp->local)); } diff_flush(&opt); - diff_tree_release_paths(&opt); + free_pathspec(&opt.pathspec); } static void check_notes_merge_worktree(struct notes_merge_options *o) diff --git a/parse-options.c b/parse-options.c index c2cbca25cc..62e9b1cc68 100644 --- a/parse-options.c +++ b/parse-options.c @@ -43,8 +43,42 @@ static void fix_filename(const char *prefix, const char **file) *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file)); } +static int opt_command_mode_error(const struct option *opt, + const struct option *all_opts, + int flags) +{ + const struct option *that; + struct strbuf message = STRBUF_INIT; + struct strbuf that_name = STRBUF_INIT; + + /* + * Find the other option that was used to set the variable + * already, and report that this is not compatible with it. + */ + for (that = all_opts; that->type != OPTION_END; that++) { + if (that == opt || + that->type != OPTION_CMDMODE || + that->value != opt->value || + that->defval != *(int *)opt->value) + continue; + + if (that->long_name) + strbuf_addf(&that_name, "--%s", that->long_name); + else + strbuf_addf(&that_name, "-%c", that->short_name); + strbuf_addf(&message, ": incompatible with %s", that_name.buf); + strbuf_release(&that_name); + opterror(opt, message.buf, flags); + strbuf_release(&message); + return -1; + } + return opterror(opt, ": incompatible with something else", flags); +} + static int get_value(struct parse_opt_ctx_t *p, - const struct option *opt, int flags) + const struct option *opt, + const struct option *all_opts, + int flags) { const char *s, *arg; const int unset = flags & OPT_UNSET; @@ -83,6 +117,16 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value = unset ? 0 : opt->defval; return 0; + case OPTION_CMDMODE: + /* + * Giving the same mode option twice, although is unnecessary, + * is not a grave error, so let it pass. + */ + if (*(int *)opt->value && *(int *)opt->value != opt->defval) + return opt_command_mode_error(opt, all_opts, flags); + *(int *)opt->value = opt->defval; + return 0; + case OPTION_SET_PTR: *(void **)opt->value = unset ? NULL : (void *)opt->defval; return 0; @@ -143,12 +187,13 @@ static int get_value(struct parse_opt_ctx_t *p, static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) { + const struct option *all_opts = options; const struct option *numopt = NULL; for (; options->type != OPTION_END; options++) { if (options->short_name == *p->opt) { p->opt = p->opt[1] ? p->opt + 1 : NULL; - return get_value(p, options, OPT_SHORT); + return get_value(p, options, all_opts, OPT_SHORT); } /* @@ -177,6 +222,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, const struct option *options) { + const struct option *all_opts = options; const char *arg_end = strchr(arg, '='); const struct option *abbrev_option = NULL, *ambiguous_option = NULL; int abbrev_flags = 0, ambiguous_flags = 0; @@ -253,7 +299,7 @@ is_abbreviated: continue; p->opt = rest + 1; } - return get_value(p, options, flags ^ opt_flags); + return get_value(p, options, all_opts, flags ^ opt_flags); } if (ambiguous_option) @@ -265,18 +311,20 @@ is_abbreviated: (abbrev_flags & OPT_UNSET) ? "no-" : "", abbrev_option->long_name); if (abbrev_option) - return get_value(p, abbrev_option, abbrev_flags); + return get_value(p, abbrev_option, all_opts, abbrev_flags); return -2; } static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg, const struct option *options) { + const struct option *all_opts = options; + for (; options->type != OPTION_END; options++) { if (!(options->flags & PARSE_OPT_NODASH)) continue; if (options->short_name == arg[0] && arg[1] == '\0') - return get_value(p, options, OPT_SHORT); + return get_value(p, options, all_opts, OPT_SHORT); } return -2; } diff --git a/parse-options.h b/parse-options.h index 9b94596e4a..8736006ed7 100644 --- a/parse-options.h +++ b/parse-options.h @@ -13,6 +13,7 @@ enum parse_opt_type { OPTION_COUNTUP, OPTION_SET_INT, OPTION_SET_PTR, + OPTION_CMDMODE, /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, @@ -21,9 +22,6 @@ enum parse_opt_type { OPTION_FILENAME }; -/* Deprecated synonym */ -#define OPTION_BOOLEAN OPTION_COUNTUP - enum parse_opt_flags { PARSE_OPT_KEEP_DASHDASH = 1, PARSE_OPT_STOP_AT_NON_OPTION = 2, @@ -128,8 +126,12 @@ struct option { #define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, \ (h), PARSE_OPT_NOARG, NULL, (i) } #define OPT_BOOL(s, l, v, h) OPT_SET_INT(s, l, v, h, 1) +#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \ + (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1} #define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, \ (h), PARSE_OPT_NOARG, NULL, (p) } +#define OPT_CMDMODE(s, l, v, h, i) { OPTION_CMDMODE, (s), (l), (v), NULL, \ + (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) } #define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) } #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } #define OPT_STRING_LIST(s, l, v, a, h) \ @@ -543,8 +543,14 @@ const char *relative_path(const char *in, const char *prefix, * * Note that this function is purely textual. It does not follow symlinks, * verify the existence of the path, or make any system calls. + * + * prefix_len != NULL is for a specific case of prefix_pathspec(): + * assume that src == dst and src[0..prefix_len-1] is already + * normalized, any time "../" eats up to the prefix_len part, + * prefix_len is reduced. In the end prefix_len is the remaining + * prefix that has not been overridden by user pathspec. */ -int normalize_path_copy(char *dst, const char *src) +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len) { char *dst0; @@ -619,11 +625,18 @@ int normalize_path_copy(char *dst, const char *src) /* Windows: dst[-1] cannot be backslash anymore */ while (dst0 < dst && dst[-1] != '/') dst--; + if (prefix_len && *prefix_len > dst - dst0) + *prefix_len = dst - dst0; } *dst = '\0'; return 0; } +int normalize_path_copy(char *dst, const char *src) +{ + return normalize_path_copy_len(dst, src, NULL); +} + /* * path = Canonical absolute path * prefixes = string_list containing normalized, absolute paths without diff --git a/pathspec.c b/pathspec.c index 6ea0867493..ad1a9f5b28 100644 --- a/pathspec.c +++ b/pathspec.c @@ -15,8 +15,8 @@ * If seen[] has not already been written to, it may make sense * to use find_pathspecs_matching_against_index() instead. */ -void add_pathspec_matches_against_index(const char **pathspec, - char *seen, int specs) +void add_pathspec_matches_against_index(const struct pathspec *pathspec, + char *seen) { int num_unmatched = 0, i; @@ -26,14 +26,14 @@ void add_pathspec_matches_against_index(const char **pathspec, * mistakenly think that the user gave a pathspec that did not match * anything. */ - for (i = 0; i < specs; i++) + for (i = 0; i < pathspec->nr; i++) if (!seen[i]) num_unmatched++; if (!num_unmatched) return; for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; - match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen); + match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen); } } @@ -45,57 +45,430 @@ void add_pathspec_matches_against_index(const char **pathspec, * nature of the "closest" (i.e. most specific) matches which each of the * given pathspecs achieves against all items in the index. */ -char *find_pathspecs_matching_against_index(const char **pathspec) +char *find_pathspecs_matching_against_index(const struct pathspec *pathspec) { - char *seen; - int i; - - for (i = 0; pathspec[i]; i++) - ; /* just counting */ - seen = xcalloc(i, 1); - add_pathspec_matches_against_index(pathspec, seen, i); + char *seen = xcalloc(pathspec->nr, 1); + add_pathspec_matches_against_index(pathspec, seen); return seen; } /* - * Check the index to see whether path refers to a submodule, or - * something inside a submodule. If the former, returns the path with - * any trailing slash stripped. If the latter, dies with an error - * message. + * Magic pathspec + * + * Possible future magic semantics include stuff like: + * + * { PATHSPEC_RECURSIVE, '*', "recursive" }, + * { PATHSPEC_REGEXP, '\0', "regexp" }, + * + */ + +static struct pathspec_magic { + unsigned bit; + char mnemonic; /* this cannot be ':'! */ + const char *name; +} pathspec_magic[] = { + { PATHSPEC_FROMTOP, '/', "top" }, + { PATHSPEC_LITERAL, 0, "literal" }, + { PATHSPEC_GLOB, '\0', "glob" }, + { PATHSPEC_ICASE, '\0', "icase" }, +}; + +/* + * Take an element of a pathspec and check for magic signatures. + * Append the result to the prefix. Return the magic bitmap. + * + * For now, we only parse the syntax and throw out anything other than + * "top" magic. + * + * NEEDSWORK: This needs to be rewritten when we start migrating + * get_pathspec() users to use the "struct pathspec" interface. For + * example, a pathspec element may be marked as case-insensitive, but + * the prefix part must always match literally, and a single stupid + * string cannot express such a case. */ -const char *check_path_for_gitlink(const char *path) +static unsigned prefix_pathspec(struct pathspec_item *item, + unsigned *p_short_magic, + const char **raw, unsigned flags, + const char *prefix, int prefixlen, + const char *elt) { - int i, path_len = strlen(path); - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; - if (S_ISGITLINK(ce->ce_mode)) { - int ce_len = ce_namelen(ce); - if (path_len <= ce_len || path[ce_len] != '/' || - memcmp(ce->name, path, ce_len)) - /* path does not refer to this - * submodule or anything inside it */ + static int literal_global = -1; + static int glob_global = -1; + static int noglob_global = -1; + static int icase_global = -1; + unsigned magic = 0, short_magic = 0, global_magic = 0; + const char *copyfrom = elt, *long_magic_end = NULL; + char *match; + int i, pathspec_prefix = -1; + + if (literal_global < 0) + literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0); + if (literal_global) + global_magic |= PATHSPEC_LITERAL; + + if (glob_global < 0) + glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0); + if (glob_global) + global_magic |= PATHSPEC_GLOB; + + if (noglob_global < 0) + noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0); + + if (glob_global && noglob_global) + die(_("global 'glob' and 'noglob' pathspec settings are incompatible")); + + + if (icase_global < 0) + icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0); + if (icase_global) + global_magic |= PATHSPEC_ICASE; + + if ((global_magic & PATHSPEC_LITERAL) && + (global_magic & ~PATHSPEC_LITERAL)) + die(_("global 'literal' pathspec setting is incompatible " + "with all other global pathspec settings")); + + if (elt[0] != ':' || literal_global) { + ; /* nothing to do */ + } else if (elt[1] == '(') { + /* longhand */ + const char *nextat; + for (copyfrom = elt + 2; + *copyfrom && *copyfrom != ')'; + copyfrom = nextat) { + size_t len = strcspn(copyfrom, ",)"); + if (copyfrom[len] == ',') + nextat = copyfrom + len + 1; + else + /* handle ')' and '\0' */ + nextat = copyfrom + len; + if (!len) continue; - if (path_len == ce_len + 1) { - /* path refers to submodule; - * strip trailing slash */ - return xstrndup(ce->name, ce_len); + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) { + if (strlen(pathspec_magic[i].name) == len && + !strncmp(pathspec_magic[i].name, copyfrom, len)) { + magic |= pathspec_magic[i].bit; + break; + } + if (!prefixcmp(copyfrom, "prefix:")) { + char *endptr; + pathspec_prefix = strtol(copyfrom + 7, + &endptr, 10); + if (endptr - copyfrom != len) + die(_("invalid parameter for pathspec magic 'prefix'")); + /* "i" would be wrong, but it does not matter */ + break; + } + } + if (ARRAY_SIZE(pathspec_magic) <= i) + die(_("Invalid pathspec magic '%.*s' in '%s'"), + (int) len, copyfrom, elt); + } + if (*copyfrom != ')') + die(_("Missing ')' at the end of pathspec magic in '%s'"), elt); + long_magic_end = copyfrom; + copyfrom++; + } else { + /* shorthand */ + for (copyfrom = elt + 1; + *copyfrom && *copyfrom != ':'; + copyfrom++) { + char ch = *copyfrom; + + if (!is_pathspec_magic(ch)) + break; + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) + if (pathspec_magic[i].mnemonic == ch) { + short_magic |= pathspec_magic[i].bit; + break; + } + if (ARRAY_SIZE(pathspec_magic) <= i) + die(_("Unimplemented pathspec magic '%c' in '%s'"), + ch, elt); + } + if (*copyfrom == ':') + copyfrom++; + } + + magic |= short_magic; + *p_short_magic = short_magic; + + /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specifed */ + if (noglob_global && !(magic & PATHSPEC_GLOB)) + global_magic |= PATHSPEC_LITERAL; + + /* --glob-pathspec is overriden by :(literal) */ + if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL)) + global_magic &= ~PATHSPEC_GLOB; + + magic |= global_magic; + + if (pathspec_prefix >= 0 && + (prefixlen || (prefix && *prefix))) + die("BUG: 'prefix' magic is supposed to be used at worktree's root"); + + if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB)) + die(_("%s: 'literal' and 'glob' are incompatible"), elt); + + if (pathspec_prefix >= 0) { + match = xstrdup(copyfrom); + prefixlen = pathspec_prefix; + } else if (magic & PATHSPEC_FROMTOP) { + match = xstrdup(copyfrom); + prefixlen = 0; + } else { + match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom); + if (!match) + die(_("%s: '%s' is outside repository"), elt, copyfrom); + } + *raw = item->match = match; + /* + * Prefix the pathspec (keep all magic) and assign to + * original. Useful for passing to another command. + */ + if (flags & PATHSPEC_PREFIX_ORIGIN) { + struct strbuf sb = STRBUF_INIT; + const char *start = elt; + if (prefixlen && !literal_global) { + /* Preserve the actual prefix length of each pattern */ + if (short_magic) + die("BUG: prefixing on short magic is not supported"); + else if (long_magic_end) { + strbuf_add(&sb, start, long_magic_end - start); + strbuf_addf(&sb, ",prefix:%d", prefixlen); + start = long_magic_end; } else { - die (_("Path '%s' is in submodule '%.*s'"), - path, ce_len, ce->name); + if (*start == ':') + start++; + strbuf_addf(&sb, ":(prefix:%d)", prefixlen); } } + strbuf_add(&sb, start, copyfrom - start); + strbuf_addstr(&sb, match); + item->original = strbuf_detach(&sb, NULL); + } else + item->original = elt; + item->len = strlen(item->match); + item->prefix = prefixlen; + + if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) && + (item->len >= 1 && item->match[item->len - 1] == '/') && + (i = cache_name_pos(item->match, item->len - 1)) >= 0 && + S_ISGITLINK(active_cache[i]->ce_mode)) { + item->len--; + match[item->len] = '\0'; + } + + if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE) + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + int ce_len = ce_namelen(ce); + + if (!S_ISGITLINK(ce->ce_mode)) + continue; + + if (item->len <= ce_len || match[ce_len] != '/' || + memcmp(ce->name, match, ce_len)) + continue; + if (item->len == ce_len + 1) { + /* strip trailing slash */ + item->len--; + match[item->len] = '\0'; + } else + die (_("Pathspec '%s' is in submodule '%.*s'"), + elt, ce_len, ce->name); + } + + if (magic & PATHSPEC_LITERAL) + item->nowildcard_len = item->len; + else { + item->nowildcard_len = simple_length(item->match); + if (item->nowildcard_len < prefixlen) + item->nowildcard_len = prefixlen; + } + item->flags = 0; + if (magic & PATHSPEC_GLOB) { + /* + * FIXME: should we enable ONESTAR in _GLOB for + * pattern "* * / * . c"? + */ + } else { + if (item->nowildcard_len < item->len && + item->match[item->nowildcard_len] == '*' && + no_wildcard(item->match + item->nowildcard_len + 1)) + item->flags |= PATHSPEC_ONESTAR; } - return path; + + /* sanity checks, pathspec matchers assume these are sane */ + assert(item->nowildcard_len <= item->len && + item->prefix <= item->len); + return magic; +} + +static int pathspec_item_cmp(const void *a_, const void *b_) +{ + struct pathspec_item *a, *b; + + a = (struct pathspec_item *)a_; + b = (struct pathspec_item *)b_; + return strcmp(a->match, b->match); +} + +static void NORETURN unsupported_magic(const char *pattern, + unsigned magic, + unsigned short_magic) +{ + struct strbuf sb = STRBUF_INIT; + int i, n; + for (n = i = 0; i < ARRAY_SIZE(pathspec_magic); i++) { + const struct pathspec_magic *m = pathspec_magic + i; + if (!(magic & m->bit)) + continue; + if (sb.len) + strbuf_addstr(&sb, " "); + if (short_magic & m->bit) + strbuf_addf(&sb, "'%c'", m->mnemonic); + else + strbuf_addf(&sb, "'%s'", m->name); + n++; + } + /* + * We may want to substitute "this command" with a command + * name. E.g. when add--interactive dies when running + * "checkout -p" + */ + die(_("%s: pathspec magic not supported by this command: %s"), + pattern, sb.buf); } /* - * Dies if the given path refers to a file inside a symlinked - * directory in the index. + * Given command line arguments and a prefix, convert the input to + * pathspec. die() if any magic in magic_mask is used. */ -void die_if_path_beyond_symlink(const char *path, const char *prefix) +void parse_pathspec(struct pathspec *pathspec, + unsigned magic_mask, unsigned flags, + const char *prefix, const char **argv) { - if (has_symlink_leading_path(path, strlen(path))) { - int len = prefix ? strlen(prefix) : 0; - die(_("'%s' is beyond a symbolic link"), path + len); + struct pathspec_item *item; + const char *entry = argv ? *argv : NULL; + int i, n, prefixlen; + + memset(pathspec, 0, sizeof(*pathspec)); + + if (flags & PATHSPEC_MAXDEPTH_VALID) + pathspec->magic |= PATHSPEC_MAXDEPTH; + + /* No arguments, no prefix -> no pathspec */ + if (!entry && !prefix) + return; + + if ((flags & PATHSPEC_PREFER_CWD) && + (flags & PATHSPEC_PREFER_FULL)) + die("BUG: PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible"); + + /* No arguments with prefix -> prefix pathspec */ + if (!entry) { + static const char *raw[2]; + + if (flags & PATHSPEC_PREFER_FULL) + return; + + if (!(flags & PATHSPEC_PREFER_CWD)) + die("BUG: PATHSPEC_PREFER_CWD requires arguments"); + + pathspec->items = item = xmalloc(sizeof(*item)); + memset(item, 0, sizeof(*item)); + item->match = prefix; + item->original = prefix; + item->nowildcard_len = item->len = strlen(prefix); + item->prefix = item->len; + raw[0] = prefix; + raw[1] = NULL; + pathspec->nr = 1; + pathspec->_raw = raw; + return; } + + n = 0; + while (argv[n]) + n++; + + pathspec->nr = n; + pathspec->items = item = xmalloc(sizeof(*item) * n); + pathspec->_raw = argv; + prefixlen = prefix ? strlen(prefix) : 0; + + for (i = 0; i < n; i++) { + unsigned short_magic; + entry = argv[i]; + + item[i].magic = prefix_pathspec(item + i, &short_magic, + argv + i, flags, + prefix, prefixlen, entry); + if (item[i].magic & magic_mask) + unsupported_magic(entry, + item[i].magic & magic_mask, + short_magic); + + if ((flags & PATHSPEC_SYMLINK_LEADING_PATH) && + has_symlink_leading_path(item[i].match, item[i].len)) { + die(_("pathspec '%s' is beyond a symbolic link"), entry); + } + + if (item[i].nowildcard_len < item[i].len) + pathspec->has_wildcard = 1; + pathspec->magic |= item[i].magic; + } + + + if (pathspec->magic & PATHSPEC_MAXDEPTH) { + if (flags & PATHSPEC_KEEP_ORDER) + die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible"); + qsort(pathspec->items, pathspec->nr, + sizeof(struct pathspec_item), pathspec_item_cmp); + } +} + +/* + * N.B. get_pathspec() is deprecated in favor of the "struct pathspec" + * based interface - see pathspec.c:parse_pathspec(). + * + * Arguments: + * - prefix - a path relative to the root of the working tree + * - pathspec - a list of paths underneath the prefix path + * + * Iterates over pathspec, prepending each path with prefix, + * and return the resulting list. + * + * If pathspec is empty, return a singleton list containing prefix. + * + * If pathspec and prefix are both empty, return an empty list. + * + * This is typically used by built-in commands such as add.c, in order + * to normalize argv arguments provided to the built-in into a list of + * paths to process, all relative to the root of the working tree. + */ +const char **get_pathspec(const char *prefix, const char **pathspec) +{ + struct pathspec ps; + parse_pathspec(&ps, + PATHSPEC_ALL_MAGIC & + ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL), + PATHSPEC_PREFER_CWD, + prefix, pathspec); + return ps._raw; +} + +void copy_pathspec(struct pathspec *dst, const struct pathspec *src) +{ + *dst = *src; + dst->items = xmalloc(sizeof(struct pathspec_item) * dst->nr); + memcpy(dst->items, src->items, + sizeof(struct pathspec_item) * dst->nr); +} + +void free_pathspec(struct pathspec *pathspec) +{ + free(pathspec->items); + pathspec->items = NULL; } diff --git a/pathspec.h b/pathspec.h index db0184a1ac..944baeb622 100644 --- a/pathspec.h +++ b/pathspec.h @@ -1,8 +1,92 @@ #ifndef PATHSPEC_H #define PATHSPEC_H -extern char *find_pathspecs_matching_against_index(const char **pathspec); -extern void add_pathspec_matches_against_index(const char **pathspec, char *seen, int specs); +/* Pathspec magic */ +#define PATHSPEC_FROMTOP (1<<0) +#define PATHSPEC_MAXDEPTH (1<<1) +#define PATHSPEC_LITERAL (1<<2) +#define PATHSPEC_GLOB (1<<3) +#define PATHSPEC_ICASE (1<<4) +#define PATHSPEC_ALL_MAGIC \ + (PATHSPEC_FROMTOP | \ + PATHSPEC_MAXDEPTH | \ + PATHSPEC_LITERAL | \ + PATHSPEC_GLOB | \ + PATHSPEC_ICASE) + +#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */ + +struct pathspec { + const char **_raw; /* get_pathspec() result, not freed by free_pathspec() */ + int nr; + unsigned int has_wildcard:1; + unsigned int recursive:1; + unsigned magic; + int max_depth; + struct pathspec_item { + const char *match; + const char *original; + unsigned magic; + int len, prefix; + int nowildcard_len; + int flags; + } *items; +}; + +#define GUARD_PATHSPEC(ps, mask) \ + do { \ + if ((ps)->magic & ~(mask)) \ + die("BUG:%s:%d: unsupported magic %x", \ + __FILE__, __LINE__, (ps)->magic & ~(mask)); \ + } while (0) + +/* parse_pathspec flags */ +#define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */ +#define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */ +#define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */ +/* strip the trailing slash if the given path is a gitlink */ +#define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3) +/* die if a symlink is part of the given path's directory */ +#define PATHSPEC_SYMLINK_LEADING_PATH (1<<4) +/* + * This is like a combination of ..LEADING_PATH and .._SLASH_CHEAP + * (but not the same): it strips the trailing slash if the given path + * is a gitlink but also checks and dies if gitlink is part of the + * leading path (i.e. the given path goes beyond a submodule). It's + * safer than _SLASH_CHEAP and also more expensive. + */ +#define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5) +#define PATHSPEC_PREFIX_ORIGIN (1<<6) +#define PATHSPEC_KEEP_ORDER (1<<7) + +extern void parse_pathspec(struct pathspec *pathspec, + unsigned magic_mask, + unsigned flags, + const char *prefix, + const char **args); +extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src); +extern void free_pathspec(struct pathspec *); + +static inline int ps_strncmp(const struct pathspec_item *item, + const char *s1, const char *s2, size_t n) +{ + if (item->magic & PATHSPEC_ICASE) + return strncasecmp(s1, s2, n); + else + return strncmp(s1, s2, n); +} + +static inline int ps_strcmp(const struct pathspec_item *item, + const char *s1, const char *s2) +{ + if (item->magic & PATHSPEC_ICASE) + return strcasecmp(s1, s2); + else + return strcmp(s1, s2); +} + +extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec); +extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen); extern const char *check_path_for_gitlink(const char *path); extern void die_if_path_beyond_symlink(const char *path, const char *prefix); diff --git a/perl/Git.pm b/perl/Git.pm index 7a252ef872..204fdc6737 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -61,7 +61,7 @@ require Exporter; remote_refs prompt get_tz_offset credential credential_read credential_write - temp_acquire temp_release temp_reset temp_path); + temp_acquire temp_is_locked temp_release temp_reset temp_path); =head1 DESCRIPTION @@ -1206,6 +1206,35 @@ sub temp_acquire { $temp_fd; } +=item temp_is_locked ( NAME ) + +Returns true if the internal lock created by a previous C<temp_acquire()> +call with C<NAME> is still in effect. + +When temp_acquire is called on a C<NAME>, it internally locks the temporary +file mapped to C<NAME>. That lock will not be released until C<temp_release()> +is called with either the original C<NAME> or the L<File::Handle> that was +returned from the original call to temp_acquire. + +Subsequent attempts to call C<temp_acquire()> with the same C<NAME> will fail +unless there has been an intervening C<temp_release()> call for that C<NAME> +(or its corresponding L<File::Handle> that was returned by the original +C<temp_acquire()> call). + +If true is returned by C<temp_is_locked()> for a C<NAME>, an attempt to +C<temp_acquire()> the same C<NAME> will cause an error unless +C<temp_release> is first called on that C<NAME> (or its corresponding +L<File::Handle> that was returned by the original C<temp_acquire()> call). + +=cut + +sub temp_is_locked { + my ($self, $name) = _maybe_self(@_); + my $temp_fd = \$TEMP_FILEMAP{$name}; + + defined $$temp_fd && $$temp_fd->opened && $TEMP_FILES{$$temp_fd}{locked}; +} + =item temp_release ( NAME ) =item temp_release ( FILEHANDLE ) diff --git a/perl/Git/SVN/Fetcher.pm b/perl/Git/SVN/Fetcher.pm index bd174189b9..10edb27732 100644 --- a/perl/Git/SVN/Fetcher.pm +++ b/perl/Git/SVN/Fetcher.pm @@ -315,11 +315,13 @@ sub change_file_prop { sub apply_textdelta { my ($self, $fb, $exp) = @_; return undef if $self->is_path_ignored($fb->{path}); - my $fh = $::_repository->temp_acquire('svn_delta'); + my $suffix = 0; + ++$suffix while $::_repository->temp_is_locked("svn_delta_${$}_$suffix"); + my $fh = $::_repository->temp_acquire("svn_delta_${$}_$suffix"); # $fh gets auto-closed() by SVN::TxDelta::apply(), # (but $base does not,) so dup() it for reading in close_file open my $dup, '<&', $fh or croak $!; - my $base = $::_repository->temp_acquire('git_blob'); + my $base = $::_repository->temp_acquire("git_blob_${$}_$suffix"); if ($fb->{blob}) { my ($base_is_link, $size); @@ -15,6 +15,7 @@ Members: Thomas Rast <trast@student.ethz.ch> Language: fr (French) Repository: https://github.com/jnavila/git Leader: Jean-Noël Avila <jn.avila@free.fr> +Members: Sébastien Helleu <flashcode@flashtux.org> Language: is (Icelandic) Leader: Ævar Arnfjörð Bjarmason <avarab@gmail.com> @@ -1,15 +1,63 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# French translations for Git. +# Copyright (C) 2013 Jean-Noël Avila <jn.avila@free.fr> +# This file is distributed under the same license as the Git package. +# Jean-Noël Avila <jn.avila@free.fr>, 2013. +# Sébastien Helleu <flashcode@flashtux.org>, 2013. +# +# French translations of common Git words used in this file: +# +# English | French +# -----------------+--------------------------------- +# #NN | n°NN +# a commit | un commit +# backward | +# compatibility | rétrocompatibilité +# bare repository | dépôt nu +# bisect | bissection +# blob | blob +# bug | bogue +# bundle | colis +# cherry-pick | picorer +# dangling | en suspens +# debugging | débogage +# fast-forward | avance rapide +# fast-forwarded | mis à jour en avance rapide +# glob | glob +# hash | hachage +# HEAD | HEAD (genre féminin) +# hook | hook +# hunk | section +# merge | fusion +# pattern | motif +# repository | dépôt +# remote | distante (ou serveur distant) +# revision | révision +# stash | remisage +# tag | étiquette +# template | modèle +# to checkout | extraire +# to commit | valider +# to fetch | rapatrier +# to prune | élaguer +# to push | pousser +# to rebase | rebaser +# to stash | remiser +# to track | suivre +# to unstage | désindexer +# tree-ish | arbre +# upstream | amont +# worktree / | +# work(ing) tree | copie de travail # msgid "" -msgstr "Project-Id-Version: git\n" +msgstr "" +"Project-Id-Version: git\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" "POT-Creation-Date: 2013-04-30 08:25+0800\n" -"PO-Revision-Date: 2013-07-02 22:28+0100\n" -"Last-Translator: Jean-Noël Avila <jn.avila@free.fr>\n" +"PO-Revision-Date: 2013-08-27 19:43+0200\n" +"Last-Translator: Sébastien Helleu <flashcode@flashtux.org>\n" "Language-Team: Jean-Noël Avila <jn.avila@free.fr>\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -19,7 +67,7 @@ msgstr "Project-Id-Version: git\n" #: advice.c:53 #, c-format msgid "hint: %.*s\n" -msgstr "astuce: %*s\n" +msgstr "astuce: %.*s\n" #. #. * Message used both when 'git commit' fails and when @@ -31,8 +79,9 @@ msgid "" "and then use 'git add/rm <file>' as\n" "appropriate to mark resolution and make a commit,\n" "or use 'git commit -a'." -msgstr "Corrigez-les dans l'espace de travail,\n" -"et utilisez 'git add/rm <fichier>' comme\n" +msgstr "" +"Corrigez-les dans la copie de travail,\n" +"et utilisez 'git add/rm <fichier>' si\n" "nécessaire pour marquer la résolution et valider,\n" "ou utilisez 'git commit -a'." @@ -47,7 +96,7 @@ msgstr "git archive --list" #: archive.c:12 msgid "" "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]" -msgstr "git archive --remote <dépot> [--exec <commande>] [options] <arbre> [<chemin>...]" +msgstr "git archive --remote <dépôt> [--exec <commande>] [options] <arbre> [<chemin>...]" #: archive.c:13 msgid "git archive --remote <repo> [--exec <cmd>] --list" @@ -67,7 +116,7 @@ msgstr "préfixe" #: archive.c:325 msgid "prepend prefix to each pathname in the archive" -msgstr "Préfixer chaque chemin de fichier dans l'archive" +msgstr "préfixer chaque chemin de fichier dans l'archive" #: archive.c:326 builtin/archive.c:88 builtin/blame.c:2371 #: builtin/blame.c:2372 builtin/config.c:55 builtin/fast-export.c:665 @@ -79,7 +128,7 @@ msgstr "fichier" #: archive.c:327 builtin/archive.c:89 msgid "write the archive to this file" -msgstr "Écrire l'archive dans ce fichier" +msgstr "écrire l'archive dans ce fichier" #: archive.c:329 msgid "read .gitattributes in working directory" @@ -125,8 +174,9 @@ msgstr "chemin vers la commande distante git-upload-archive" msgid "" "Negative patterns are ignored in git attributes\n" "Use '\\!' for literal leading exclamation." -msgstr "Les patrons négatifs sont ignorés dans les attributs git\n" -"Utilisez '\\!' pour un point d'exclamation littéral" +msgstr "" +"Les motifs de négation sont ignorés dans les attributs git\n" +"Utilisez '\\!' pour un point d'exclamation littéral." #: branch.c:60 #, c-format @@ -217,30 +267,30 @@ msgid "" "If you are planning to push out a new local branch that\n" "will track its remote counterpart, you may want to use\n" "\"git push -u\" to set the upstream config as you push." -msgstr "\n" +msgstr "" +"\n" "Si vous comptez baser votre travail sur une branche\n" "amont qui existe déjà sur le serveur distant, vous pourriez\n" "devoir lancer \"git fetch\" pour la récupérer.\n" "\n" -"Si vous comptez pousser une nouvelle branche locale\n" -"qui suivra sa jumelle distante, vous souhaiterez utiliser\n" -"\"git push -u\" pour paramétrer le suivi distant en même\n" -"temps que vous poussez." +"Si vous comptez pousser une nouvelle branche locale qui suivra\n" +"sa jumelle distante, vous souhaiterez utiliser \"git push -u\"\n" +"pour paramétrer le suivi distant en même temps que vous poussez." #: branch.c:250 #, c-format msgid "Not a valid object name: '%s'." -msgstr "Nom d'objet invalide : '%s'" +msgstr "Nom d'objet invalide : '%s'." #: branch.c:270 #, c-format msgid "Ambiguous object name: '%s'." -msgstr "Nom d'objet ambigu : '%s'" +msgstr "Nom d'objet ambigu : '%s'." #: branch.c:275 #, c-format msgid "Not a valid branch point: '%s'." -msgstr "Point d'embranchement invalide : '%s'" +msgstr "Point d'embranchement invalide : '%s'." #: branch.c:281 msgid "Failed to lock ref for update" @@ -258,7 +308,7 @@ msgstr "'%s' ne semble pas être un fichier bundle v2" #: bundle.c:63 #, c-format msgid "unrecognized header: %s%s (%d)" -msgstr "entête non reconnu : %s %s (%d)" +msgstr "en-tête non reconnu : %s%s (%d)" #: bundle.c:89 builtin/commit.c:676 #, c-format @@ -284,13 +334,13 @@ msgstr[1] "Le colis contient ces %d références :" #: bundle.c:193 msgid "The bundle records a complete history." -msgstr "Le bundle enregistre l'historique complet." +msgstr "Le colis enregistre l'historique complet." #: bundle.c:195 #, c-format msgid "The bundle requires this ref:" msgid_plural "The bundle requires these %d refs:" -msgstr[0] "Le colis exige cette référence" +msgstr[0] "Le colis exige cette référence :" msgstr[1] "Le colis exige ces %d références :" #: bundle.c:294 @@ -305,11 +355,11 @@ msgstr "argument non reconnu : %s" #: bundle.c:335 #, c-format msgid "ref '%s' is excluded by the rev-list options" -msgstr "ref '%s' est exclus par les options de rev-list" +msgstr "la référence '%s' est exclue par les options de rev-list" #: bundle.c:380 msgid "Refusing to create empty bundle." -msgstr "Refus de créer un bundle vide" +msgstr "Refus de créer un colis vide." #: bundle.c:398 msgid "Could not spawn pack-objects" @@ -340,16 +390,16 @@ msgstr "%s %s n'est pas un commit !" #: compat/obstack.c:406 compat/obstack.c:408 msgid "memory exhausted" -msgstr "Plus de mémoire" +msgstr "plus de mémoire" #: connected.c:39 msgid "Could not run 'git rev-list'" -msgstr "impossible de lancer 'git rev-list'" +msgstr "Impossible de lancer 'git rev-list'" #: connected.c:48 #, c-format msgid "failed write to rev-list: %s" -msgstr "impossible d'écrire la rev-list : %s" +msgstr "impossible d'écrire dans la rev-list : %s" #: connected.c:56 #, c-format @@ -507,14 +557,16 @@ msgstr "commandes git disponibles depuis un autre endroit de votre $PATH" #: help.c:235 msgid "The most commonly used git commands are:" -msgstr "Les commandes git les plus usitées sont :" +msgstr "Les commandes git les plus utilisées sont :" #: help.c:292 #, c-format msgid "" "'%s' appears to be a git command, but we were not\n" "able to execute it. Maybe git-%s is broken?" -msgstr "'%s' semble être une commande git, mais elle n'a pas pu être éxecutée. Peut-être git-%s est-il cassé ?" +msgstr "" +"'%s' semble être une commande git, mais elle n'a pas pu\n" +"être exécutée. Peut-être git-%s est-elle cassée ?" #: help.c:349 msgid "Uh oh. Your system reports no Git commands at all." @@ -526,17 +578,17 @@ msgid "" "WARNING: You called a Git command named '%s', which does not exist.\n" "Continuing under the assumption that you meant '%s'" msgstr "ATTENTION : vous avez invoqué une commande Git nommée '%s' qui n'existe pas.\n" -"Poursuite avec l'hypothèse que vous avez voulu dire '%s'" +"Poursuite en supposant que vous avez voulu dire '%s'" #: help.c:376 #, c-format msgid "in %0.1f seconds automatically..." -msgstr "dans %01f secondes automatiquement..." +msgstr "dans %0.1f secondes automatiquement..." #: help.c:383 #, c-format msgid "git: '%s' is not a git command. See 'git --help'." -msgstr "git : '%s' n'est pas une commande git. Référez-vous à 'git --help'" +msgstr "git : '%s' n'est pas une commande git. Voir 'git --help'." #: help.c:387 msgid "" @@ -547,8 +599,7 @@ msgid_plural "" "Did you mean one of these?" msgstr[0] "\n" "Vouliez-vous dire cela ?" -msgstr[1] "\n" -"Vouliez-vous dire un de cela ?" +msgstr[1] "\nVouliez-vous dire un de ceux-là ?" #: merge.c:56 msgid "failed to read the cache" @@ -567,11 +618,11 @@ msgstr "(mauvais commit)\n" #: merge-recursive.c:206 #, c-format msgid "addinfo_cache failed for path '%s'" -msgstr "Échec de addinfo_cache pour le chemin '%s'" +msgstr "échec de addinfo_cache pour le chemin '%s'" #: merge-recursive.c:268 msgid "error building trees" -msgstr "Erreur à la construction des arbres" +msgstr "erreur de construction des arbres" #: merge-recursive.c:672 #, c-format @@ -607,12 +658,12 @@ msgstr "blob attendu pour %s '%s'" #: merge-recursive.c:773 builtin/clone.c:313 #, c-format msgid "failed to open '%s'" -msgstr "Échec à l'ouverture de '%s'" +msgstr "échec à l'ouverture de '%s'" #: merge-recursive.c:781 #, c-format msgid "failed to symlink '%s'" -msgstr "Échec à la création du lien symbolique '%s'" +msgstr "échec à la création du lien symbolique '%s'" #: merge-recursive.c:784 #, c-format @@ -644,7 +695,7 @@ msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version % msgid "" "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " "in tree at %s." -msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans%s. Version %s de %s laissée dans l'arbre dans le fichier %s." +msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %s de %s laissée dans l'arbre dans le fichier %s." #: merge-recursive.c:1081 msgid "rename" @@ -664,7 +715,7 @@ msgstr "%s est un répertoire dans %s ajouté plutôt comme %s" msgid "" "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s" "\"->\"%s\" in \"%s\"%s" -msgstr "CONFLIT (renommage/renommage) : Renommage de '%s'->'%s' dans la branche '%s' et renommage '%s'->'%s' dans '%s'%s" +msgstr "CONFLIT (renommage/renommage) : Renommage de \"%s\"->\"%s\" dans la branche \"%s\" et renommage \"%s\"->\"%s\" dans \"%s\"%s" #: merge-recursive.c:1164 msgid " (left unresolved)" @@ -678,12 +729,12 @@ msgstr "CONFLIT (renommage/renommage) : renommage '%s'->'%s' dans %s. Renommage #: merge-recursive.c:1248 #, c-format msgid "Renaming %s to %s and %s to %s instead" -msgstr "Renommage préféré de %s en %s et de %s en %s" +msgstr "Renommage de %s en %s et de %s en %s à la place" #: merge-recursive.c:1447 #, c-format msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s" -msgstr "CONFLIT (renommage/ajout) : renommage de '%s'->'%s' dans ' %s. %s ajouté dans %s" +msgstr "CONFLIT (renommage/ajout) : Renommage de %s->%s dans %s. %s ajouté dans %s" #: merge-recursive.c:1457 #, c-format @@ -729,7 +780,7 @@ msgstr "%s ignoré (fusion identique à l'existant)" #: merge-recursive.c:1629 #, c-format msgid "Auto-merging %s" -msgstr "fusion automatique de %s" +msgstr "Fusion automatique de %s" #: merge-recursive.c:1633 git-submodule.sh:1029 msgid "submodule" @@ -738,7 +789,7 @@ msgstr "sous-module" #: merge-recursive.c:1634 #, c-format msgid "CONFLICT (%s): Merge conflict in %s" -msgstr "CONFLIT (%s) : conflit de fusion dans %s" +msgstr "CONFLIT (%s) : Conflit de fusion dans %s" #: merge-recursive.c:1724 #, c-format @@ -774,7 +825,7 @@ msgstr "Déjà à jour !" #: merge-recursive.c:1815 #, c-format msgid "merging of trees %s and %s failed" -msgstr "Échec de fusion des arbres %s et %s" +msgstr "échec de fusion des arbres %s et %s" #: merge-recursive.c:1845 #, c-format @@ -808,7 +859,7 @@ msgstr "Impossible d'écrire l'index." #: object.c:195 #, c-format msgid "unable to parse object: %s" -msgstr "Impossible d'analyser l'objet '%s'" +msgstr "impossible d'analyser l'objet : %s" #: parse-options.c:489 msgid "..." @@ -849,24 +900,24 @@ msgstr "'%s' est au delà d'un lien symbolique" #, c-format msgid "Your branch is ahead of '%s' by %d commit.\n" msgid_plural "Your branch is ahead of '%s' by %d commits.\n" -msgstr[0] "Votre branche est en avance sur '%s' par %d commit.\n" -msgstr[1] "Votre branche est en avance sur '%s' par %d commits.\n" +msgstr[0] "Votre branche est en avance sur '%s' de %d commit.\n" +msgstr[1] "Votre branche est en avance sur '%s' de %d commits.\n" #: remote.c:1787 msgid " (use \"git push\" to publish your local commits)\n" -msgstr " (utilisez 'git push' pour publier vos commits locaux)\n" +msgstr " (utilisez \"git push\" pour publier vos commits locaux)\n" #: remote.c:1790 #, c-format msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n" msgid_plural "" "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n" -msgstr[0] "Votre branche est en retard sur '%s' par %d commit, et peut être mise à jour en avance rapide.\n" -msgstr[1] "Votre branche est en retard sur '%s' par %d commits, et peut être mise à jour en avance rapide.\n" +msgstr[0] "Votre branche est en retard sur '%s' de %d commit, et peut être mise à jour en avance rapide.\n" +msgstr[1] "Votre branche est en retard sur '%s' de %d commits, et peut être mise à jour en avance rapide.\n" #: remote.c:1798 msgid " (use \"git pull\" to update your local branch)\n" -msgstr " ( utilisez 'git pull' pour mettre à jour votre branche locale)\n" +msgstr " (utilisez \"git pull\" pour mettre à jour votre branche locale)\n" #: remote.c:1801 #, c-format @@ -909,7 +960,8 @@ msgid "" "after resolving the conflicts, mark the corrected paths\n" "with 'git add <paths>' or 'git rm <paths>'\n" "and commit the result with 'git commit'" -msgstr "après résolution des conflits, marquez les chemins corrigés\n" +msgstr "" +"après résolution des conflits, marquez les chemins corrigés\n" "avec 'git add <chemins>' ou 'git rm <chemins>'\n" "puis validez le résultat avec 'git commit'" @@ -925,7 +977,7 @@ msgstr "Erreur à l'emballage de %s" #: sequencer.c:263 msgid "Your local changes would be overwritten by cherry-pick." -msgstr "Vos modifications locales serait écrasées par cherry-pick." +msgstr "Vos modifications locales seraient écrasées par cherry-pick." #: sequencer.c:265 msgid "Your local changes would be overwritten by revert." @@ -939,7 +991,7 @@ msgstr "Validez vos modifications ou remisez-les pour continuer." #: sequencer.c:319 #, c-format msgid "%s: Unable to write new index file" -msgstr "%s: impossiblde d'écrire le nouveau fichier index" +msgstr "%s: Impossible d'écrire le nouveau fichier index" #: sequencer.c:350 msgid "Could not resolve HEAD commit\n" @@ -971,7 +1023,7 @@ msgstr "Le commit %s est une fusion mais l'option -m n'a pas été spécifiée." #: sequencer.c:514 #, c-format msgid "Commit %s does not have parent %d" -msgstr "Le commit %s n'a pas %d pour parent" +msgstr "Le commit %s n'a pas le parent %d" #: sequencer.c:518 #, c-format @@ -1060,7 +1112,7 @@ msgstr "Feuille d'options malformée : %s" #: sequencer.c:840 msgid "a cherry-pick or revert is already in progress" -msgstr "Un picorage ou un retour est déjà en cours" +msgstr "un picorage ou un retour est déjà en cours" #: sequencer.c:841 msgid "try \"git cherry-pick (--continue | --quit | --abort)\"" @@ -1105,7 +1157,7 @@ msgstr "fin de fichier inattendue" #: sequencer.c:916 #, c-format msgid "stored pre-cherry-pick HEAD file '%s' is corrupt" -msgstr "Le fichier HEAD de préparation de picorage '%s' est corrompu" +msgstr "le fichier HEAD de préparation de picorage '%s' est corrompu" #: sequencer.c:939 #, c-format @@ -1120,7 +1172,7 @@ msgstr "%s : impossible de picorer un %s" #: sequencer.c:1085 #, c-format msgid "%s: bad revision" -msgstr "%s: mauvaise révision" +msgstr "%s : mauvaise révision" #: sequencer.c:1119 msgid "Can't revert as initial commit" @@ -1128,7 +1180,7 @@ msgstr "Impossible d'annuler en tant que commit initial" #: sequencer.c:1120 msgid "Can't cherry-pick into empty head" -msgstr "Impossible de picorer vers un HEAD vide" +msgstr "Impossible de picorer vers une HEAD vide" #: sha1_name.c:1036 msgid "HEAD does not point to a branch" @@ -1183,15 +1235,15 @@ msgstr " (utilisez \"git rm --cached <fichier>...\" pour désindexer)" #: wt-status.c:173 msgid " (use \"git add <file>...\" to mark resolution)" -msgstr " (utilisez \"git add <fichier>...\" pour marquer résolu)" +msgstr " (utilisez \"git add <fichier>...\" pour marquer comme résolu)" #: wt-status.c:175 wt-status.c:179 msgid " (use \"git add/rm <file>...\" as appropriate to mark resolution)" -msgstr " (utilisez \"git add/rm <fichier>...\" selon le cas pour marquer résolu)" +msgstr " (utilisez \"git add/rm <fichier>...\" si nécessaire pour marquer comme résolu)" #: wt-status.c:177 msgid " (use \"git rm <file>...\" to mark resolution)" -msgstr " (utilisez \"git rm <fichier>...\" pour marquer résolu)" +msgstr " (utilisez \"git rm <fichier>...\" pour marquer comme résolu)" #: wt-status.c:188 msgid "Changes to be committed:" @@ -1216,7 +1268,7 @@ msgstr " (utilisez \"git checkout -- <fichier>...\" pour annuler les modificati #: wt-status.c:215 msgid " (commit or discard the untracked or modified content in submodules)" -msgstr " (valider out annuler le contenu non suivi our modifié dans les sous-modules)" +msgstr " (valider ou annuler le contenu non suivi ou modifié dans les sous-modules)" #: wt-status.c:227 #, c-format @@ -1229,7 +1281,7 @@ msgstr "bogue" #: wt-status.c:249 msgid "both deleted:" -msgstr "effacé des deux côtés :" +msgstr "supprimé des deux côtés :" #: wt-status.c:250 msgid "added by us:" @@ -1237,7 +1289,7 @@ msgstr "ajouté par nous :" #: wt-status.c:251 msgid "deleted by them:" -msgstr "effacé par eux :" +msgstr "supprimé par eux :" #: wt-status.c:252 msgid "added by them:" @@ -1245,11 +1297,11 @@ msgstr "ajouté par eux :" #: wt-status.c:253 msgid "deleted by us:" -msgstr "effacé par nous :" +msgstr "supprimé par nous :" #: wt-status.c:254 msgid "both added:" -msgstr "Ajouté de deux côtés :" +msgstr "ajouté de deux côtés :" #: wt-status.c:255 msgid "both modified:" @@ -1270,42 +1322,42 @@ msgstr "contenu non suivi, " #: wt-status.c:306 #, c-format msgid "new file: %s" -msgstr "nouveau : %s" +msgstr "nouveau : %s" #: wt-status.c:309 #, c-format msgid "copied: %s -> %s" -msgstr "copié : %s->%s" +msgstr "copié : %s -> %s" #: wt-status.c:312 #, c-format msgid "deleted: %s" -msgstr "effacé : %s" +msgstr "supprimé : %s" #: wt-status.c:315 #, c-format msgid "modified: %s" -msgstr "modifié : %s" +msgstr "modifié : %s" #: wt-status.c:318 #, c-format msgid "renamed: %s -> %s" -msgstr "renommé : %s -> %s" +msgstr "renommé : %s -> %s" #: wt-status.c:321 #, c-format msgid "typechange: %s" -msgstr "nv type : %s" +msgstr "nv type : %s" #: wt-status.c:324 #, c-format msgid "unknown: %s" -msgstr "inconnu : %s" +msgstr "inconnu : %s" #: wt-status.c:327 #, c-format msgid "unmerged: %s" -msgstr "non fus : %s" +msgstr "non fus. : %s" #: wt-status.c:330 #, c-format @@ -1346,7 +1398,7 @@ msgstr " (utilisez \"git am --skip\" pour sauter ce patch)" #: wt-status.c:833 msgid " (use \"git am --abort\" to restore the original branch)" -msgstr " (utilisez \"git am --abort\" pour restaurer la branche originelle)" +msgstr " (utilisez \"git am --abort\" pour restaurer la branche d'origine)" #: wt-status.c:893 wt-status.c:910 #, c-format @@ -1416,7 +1468,7 @@ msgstr " (tous les conflits sont résolus : lancez \"git commit\")" #: wt-status.c:970 #, c-format msgid "You are currently reverting commit %s." -msgstr "Vous êtes actuellement en train de rétablir un commit %s." +msgstr "Vous êtes actuellement en train de rétablir le commit %s." #: wt-status.c:975 msgid " (fix conflicts and run \"git revert --continue\")" @@ -1441,7 +1493,7 @@ msgstr "Vous êtes en cours de bissection." #: wt-status.c:998 msgid " (use \"git bisect reset\" to get back to the original branch)" -msgstr " (utilisez \"git bisect reset\" pour revenir à la branche de départ)" +msgstr " (utilisez \"git bisect reset\" pour revenir à la branche d'origine)" #: wt-status.c:1173 msgid "On branch " @@ -1449,11 +1501,11 @@ msgstr "Sur la branche " #: wt-status.c:1184 msgid "HEAD detached at " -msgstr "HEAD détaché sur " +msgstr "HEAD détachée sur " #: wt-status.c:1186 msgid "HEAD detached from " -msgstr "HEAD détaché depuis " +msgstr "HEAD détachée depuis " #: wt-status.c:1189 msgid "Not currently on any branch." @@ -1461,7 +1513,7 @@ msgstr "Actuellement sur aucun branche." #: wt-status.c:1206 msgid "Initial commit" -msgstr "Validation initiale." +msgstr "Validation initiale" #: wt-status.c:1220 msgid "Untracked files" @@ -1484,7 +1536,7 @@ msgstr "L'énumération des fichiers non suivis a duré %.2f secondes. 'status - #: wt-status.c:1232 #, c-format msgid "Untracked files not listed%s" -msgstr "Les fichiers non suivis ne sont pas listés %s" +msgstr "Fichiers non suivis non listés%s" #: wt-status.c:1234 msgid " (use -u option to show untracked files)" @@ -1497,29 +1549,29 @@ msgstr "Aucune modification" #: wt-status.c:1245 #, c-format msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n" -msgstr "aucune modification n'a été ajoutée au commit (utilisez \"git add\" ou \"git commit -a\")\n" +msgstr "aucune modification n'a été ajoutée à la validation (utilisez \"git add\" ou \"git commit -a\")\n" #: wt-status.c:1248 #, c-format msgid "no changes added to commit\n" -msgstr "aucune modification indexée\n" +msgstr "aucune modification ajoutée à la validation\n" #: wt-status.c:1251 #, c-format msgid "" "nothing added to commit but untracked files present (use \"git add\" to " "track)\n" -msgstr "Aucune modification indexée mais des fichiers non suivis sont présents (utilisez \"git add\" pour les suivre)\n" +msgstr "aucune modification ajoutée à la validation mais des fichiers non suivis sont présents (utilisez \"git add\" pour les suivre)\n" #: wt-status.c:1254 #, c-format msgid "nothing added to commit but untracked files present\n" -msgstr "Aucune modification indexée mais des fichiers non-suivis sont présents\n" +msgstr "aucune modification ajoutée à la validation mais des fichiers non suivis sont présents\n" #: wt-status.c:1257 #, c-format msgid "nothing to commit (create/copy files and use \"git add\" to track)\n" -msgstr "rien à valider (créez/copiez des fichiers et utilisez \"git add\" pour le suivre)\n" +msgstr "rien à valider (créez/copiez des fichiers et utilisez \"git add\" pour les suivre)\n" #: wt-status.c:1260 wt-status.c:1265 #, c-format @@ -1593,10 +1645,10 @@ msgid "" "\n" "With the current Git version, the command is restricted to the current " "directory.\n" -msgstr "Le comportement de 'git add %s (ou %s)' sans argument de chemin\n" -"depuis un sous-répertoire du projet va changer dans Git 2.0. Cet usage\n" -"est déconseillé.\n" -"Pour ajouter du contenu pour tout le projet, lancez :\n" +msgstr "" +"Le comportement de 'git add %s (ou %s)' sans argument de chemin depuis un\n" +"sous-répertoire du projet va changer dans Git 2.0 et ne doit plus être utilisé.\n" +"Pour ajouter le contenu de toute l'arborescence, lancez :\n" "\n" " git add %s :/\n" " (ou git add %s :/)\n" @@ -1622,15 +1674,18 @@ msgid "" "* 'git add --all <pathspec>' will let you also record the removals.\n" "\n" "Run 'git status' to check the paths you removed from your working tree.\n" -msgstr "Vous avez lancé 'git add' sans '-A (--all)' ni '--ignore-removal',\n" -"Le comportement vis à vis des fichiers supprimés va changer dans Git 2.0.\n" -"Les chemins tels que '%s' qui ont été retirés de votre copie de travail sont\n" -"actuellement ignorés.\n" +msgstr "" +"Vous avez lancé 'git add' sans '-A (--all)' ni '--ignore-removal',\n" +"dont le comportement va changer dans Git 2.0 avec le respect des chemins que vous supprimez.\n" +"Les chemins tels que '%s' qui ont été\n" +"retirés de votre copie de travail sont ignorés avec cette version de Git.\n" "\n" -" 'git add --ignore-removal <chemin>', est l'option par défaut actuelle qui\n" -" ignore les chemins que vous avez supprimé de votre copie de travail.\n" +"* 'git add --ignore-removal <chemin>', qui est l'option par défaut actuelle,\n" +" ignore les chemins que vous avez supprimés de votre copie de travail.\n" "\n" "* 'git add --all <chemin>' permet d'enregistrer aussi les suppressions.\n" +"\n" +"Lancez 'git status' pour vérifier les chemins que vous avez supprimés de votre copie de travail.\n" #: builtin/add.c:144 #, c-format @@ -1639,16 +1694,16 @@ msgstr "status de diff inattendu %c" #: builtin/add.c:149 builtin/commit.c:233 msgid "updating files failed" -msgstr "echec de la mise à jour des fichiers" +msgstr "échec de la mise à jour des fichiers" #: builtin/add.c:163 #, c-format msgid "remove '%s'\n" -msgstr "retrait de '%s'\n" +msgstr "suppression de '%s'\n" #: builtin/add.c:253 msgid "Unstaged changes after refreshing the index:" -msgstr "Modifications non indexées après rafraîchissement de l'index" +msgstr "Modifications non indexées après rafraîchissement de l'index :" #: builtin/add.c:256 builtin/add.c:572 builtin/rm.c:275 #, c-format @@ -1716,7 +1771,7 @@ msgstr "permettre l'ajout de fichiers ignorés" #: builtin/add.c:400 msgid "update tracked files" -msgstr "mise à jour des fichiers suivis" +msgstr "mettre à jour les fichiers suivis" #: builtin/add.c:401 msgid "record only the fact that the path will be added later" @@ -1724,7 +1779,7 @@ msgstr "enregistrer seulement le fait que le chemin sera ajouté plus tard" #: builtin/add.c:402 msgid "add changes from all tracked and untracked files" -msgstr "ajouter les modifications de tous les fichiers suivis et non-suivis" +msgstr "ajouter les modifications de tous les fichiers suivis et non suivis" #. takes no arguments #: builtin/add.c:405 @@ -1741,7 +1796,7 @@ msgstr "sauter seulement les fichiers qui ne peuvent pas être ajoutés du fait #: builtin/add.c:409 msgid "check if - even missing - files are ignored in dry run" -msgstr "vérifier à vide si des fichiers, même manquants, sont ignorés" +msgstr "vérifier si des fichiers - même manquants - sont ignorés, à vide" #: builtin/add.c:431 #, c-format @@ -1754,7 +1809,7 @@ msgstr "aucun fichier ajouté" #: builtin/add.c:438 msgid "adding files failed" -msgstr "echec de l'ajout de fichiers" +msgstr "échec de l'ajout de fichiers" #: builtin/add.c:477 msgid "-A and -u are mutually incompatible" @@ -1762,17 +1817,17 @@ msgstr "-A et -u sont mutuellement incompatibles" #: builtin/add.c:495 msgid "Option --ignore-missing can only be used together with --dry-run" -msgstr "L'option --ignore-missing ne peut être utilisée qu'en complément de --dry-run" +msgstr "L'option --ignore-missing ne peut être utilisée qu'en complément de --dry-run" #: builtin/add.c:525 #, c-format msgid "Nothing specified, nothing added.\n" -msgstr "Spécification vide, riien à ajouter.\n" +msgstr "Rien de spécifié, rien n'a été ajouté.\n" #: builtin/add.c:526 #, c-format msgid "Maybe you wanted to say 'git add .'?\n" -msgstr "Vous voulez sûrement dire 'git add .' ?\n" +msgstr "Vous vouliez sûrement dire 'git add .' ?\n" #: builtin/add.c:532 builtin/check-ignore.c:66 builtin/clean.c:204 #: builtin/commit.c:293 builtin/mv.c:82 builtin/rm.c:235 @@ -1785,17 +1840,17 @@ msgstr "Impossible d'écrire le nouveau fichier d'index" #: builtin/apply.c:57 msgid "git apply [options] [<patch>...]" -msgstr "git apply [option] [<patch>...]" +msgstr "git apply [options] [<patch>...]" #: builtin/apply.c:110 #, c-format msgid "unrecognized whitespace option '%s'" -msgstr "option d'espaces non reconnue '%s'" +msgstr "option d'espace non reconnue '%s'" #: builtin/apply.c:125 #, c-format msgid "unrecognized whitespace ignore option '%s'" -msgstr "option d'espaces ignorés non reconnue '%s'" +msgstr "option d'ignorance d'espce non reconnue '%s'" #: builtin/apply.c:823 #, c-format @@ -1805,32 +1860,32 @@ msgstr "Impossible de préparer la regexp d'horodatage %s" #: builtin/apply.c:832 #, c-format msgid "regexec returned %d for input: %s" -msgstr "regexec a retourné %d pour l'entrée %s" +msgstr "regexec a retourné %d pour l'entrée : %s" #: builtin/apply.c:913 #, c-format msgid "unable to find filename in patch at line %d" -msgstr "nom de fichier du patch introuvable ligne %d" +msgstr "nom de fichier du patch introuvable à la ligne %d" #: builtin/apply.c:945 #, c-format msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d" -msgstr "git apply : mauvais format de git-diff - /dev/null attendu, %s trouvé ligne %d" +msgstr "git apply : mauvais format de git-diff - /dev/null attendu, %s trouvé à la ligne %d" #: builtin/apply.c:949 #, c-format msgid "git apply: bad git-diff - inconsistent new filename on line %d" -msgstr "git apply : mauvais format de git-diff - nouveau nom de fichier inconsistant ligne %d" +msgstr "git apply : mauvais format de git-diff - nouveau nom de fichier inconsistant à la ligne %d" #: builtin/apply.c:950 #, c-format msgid "git apply: bad git-diff - inconsistent old filename on line %d" -msgstr "git apply : mauvais format de git-diff - ancien nom de fichier inconsistant ligne %d" +msgstr "git apply : mauvais format de git-diff - ancien nom de fichier inconsistant à la ligne %d" #: builtin/apply.c:957 #, c-format msgid "git apply: bad git-diff - expected /dev/null on line %d" -msgstr "git apply : mauvais format de git-diff - /dev/null attendu ligne %d" +msgstr "git apply : mauvais format de git-diff - /dev/null attendu à la ligne %d" #: builtin/apply.c:1422 #, c-format @@ -1840,7 +1895,7 @@ msgstr "recount : ligne inattendue : %.*s" #: builtin/apply.c:1479 #, c-format msgid "patch fragment without header at line %d: %.*s" -msgstr "fragment de patch sans entête ligne %d : %.*s" +msgstr "fragment de patch sans en-tête à la ligne %d : %.*s" #: builtin/apply.c:1496 #, c-format @@ -1850,8 +1905,8 @@ msgid "" msgid_plural "" "git diff header lacks filename information when removing %d leading pathname " "components (line %d)" -msgstr[0] "information de nom de fichier manquante dans l'entête de git diff lors de la suppression de %d composant de préfix de chemin (ligne %d)" -msgstr[1] "information de nom de fichier manquante dans l'entête de git diff lors de la suppression de %d composants de préfix de chemin (ligne %d)" +msgstr[0] "information de nom de fichier manquante dans l'en-tête de git diff lors de la suppression de %d composant de préfixe de chemin (ligne %d)" +msgstr[1] "information de nom de fichier manquante dans l'en-tête de git diff lors de la suppression de %d composants de préfixe de chemin (ligne %d)" #: builtin/apply.c:1656 msgid "new file depends on old contents" @@ -1879,28 +1934,28 @@ msgstr "le fichier supprimé %s a encore du contenu" #: builtin/apply.c:1725 #, c-format msgid "** warning: file %s becomes empty but is not deleted" -msgstr "** attention : le fichier %s devient vide mais n'est pas effacé" +msgstr "** attention : le fichier %s devient vide mais n'est pas supprimé" #: builtin/apply.c:1871 #, c-format msgid "corrupt binary patch at line %d: %.*s" -msgstr "patch binaire corrompu ligne %d : %.*s" +msgstr "patch binaire corrompu à la ligne %d : %.*s" #. there has to be one hunk (forward hunk) #: builtin/apply.c:1900 #, c-format msgid "unrecognized binary patch at line %d" -msgstr "patch binaire non reconnu ligne %d" +msgstr "patch binaire non reconnu à la ligne %d" #: builtin/apply.c:1986 #, c-format msgid "patch with only garbage at line %d" -msgstr "patch totalement incompréhensible ligne %d" +msgstr "patch totalement incompréhensible à la ligne %d" #: builtin/apply.c:2076 #, c-format msgid "unable to read symlink %s" -msgstr "lecture du symlink %s impossible" +msgstr "lecture du lien symbolique %s impossible" #: builtin/apply.c:2080 #, c-format @@ -1916,8 +1971,8 @@ msgstr "début de ligne invalide : '%c'" #, c-format msgid "Hunk #%d succeeded at %d (offset %d line)." msgid_plural "Hunk #%d succeeded at %d (offset %d lines)." -msgstr[0] "La section #%d a réussi à la ligne %d (offset %d ligne)" -msgstr[1] "La section #%d a réussi à la ligne %d (offset %d lignes)" +msgstr[0] "La section n°%d a réussi à la ligne %d (offset %d ligne)." +msgstr[1] "La section n°%d a réussi à la ligne %d (offset %d lignes)." #: builtin/apply.c:2818 #, c-format @@ -1950,7 +2005,7 @@ msgstr "le patch binaire sur '%s' crée un résultat incorrect (%s attendu, mais #: builtin/apply.c:2973 #, c-format msgid "patch failed: %s:%ld" -msgstr "le patch a échoué : %s : %ld" +msgstr "le patch a échoué : %s:%ld" #: builtin/apply.c:3095 #, c-format @@ -1965,7 +2020,7 @@ msgstr "echec de la lecture de %s" #: builtin/apply.c:3173 builtin/apply.c:3395 #, c-format msgid "path %s has been renamed/deleted" -msgstr "le fichier %s a été renommé/supprimé" +msgstr "le chemin %s a été renommé/supprimé" #: builtin/apply.c:3254 builtin/apply.c:3409 #, c-format @@ -2049,7 +2104,7 @@ msgstr "stat du fichier nouvellement créé '%s' impossible" #: builtin/apply.c:3859 #, c-format msgid "unable to create backing store for newly created file %s" -msgstr "création du magasin de sauvegard pour le fichier nouvellement créé %s impossible" +msgstr "création du magasin de stockage pour le fichier nouvellement créé %s impossible" #: builtin/apply.c:3862 builtin/apply.c:3970 #, c-format @@ -2059,7 +2114,7 @@ msgstr "ajout de l'entrée de cache %s impossible" #: builtin/apply.c:3895 #, c-format msgid "closing file '%s'" -msgstr "fermeture du ficheir '%s'" +msgstr "fermeture du fichier '%s'" #: builtin/apply.c:3944 #, c-format @@ -2091,12 +2146,12 @@ msgstr "troncature du nom de fichier .rej en %.*s.rej" #: builtin/apply.c:4073 #, c-format msgid "Hunk #%d applied cleanly." -msgstr "Section no %d appliqué proprement." +msgstr "Section n°%d appliquée proprement." #: builtin/apply.c:4076 #, c-format msgid "Rejected hunk #%d." -msgstr "Section no %d rejetée." +msgstr "Section n°%d rejetée." #: builtin/apply.c:4226 msgid "unrecognized input" @@ -2165,7 +2220,7 @@ msgstr "tenter une fusion à 3 points si le patch ne s'applique pas proprement" #: builtin/apply.c:4386 msgid "build a temporary index based on embedded index information" -msgstr "construire une index temporaire fondé sur l'information de l'index embarqué" +msgstr "construire un index temporaire fondé sur l'information de l'index embarqué" #: builtin/apply.c:4388 builtin/checkout-index.c:197 builtin/ls-files.c:456 msgid "paths are separated with NUL character" @@ -2185,15 +2240,15 @@ msgstr "détecter des lignes nouvelles ou modifiées qui contiennent des erreurs #: builtin/apply.c:4396 builtin/apply.c:4399 msgid "ignore changes in whitespace when finding context" -msgstr "ignorer des modifications d'espaces lors de la recherche de contexte" +msgstr "ignorer des modifications d'espace lors de la recherche de contexte" #: builtin/apply.c:4402 msgid "apply the patch in reverse" -msgstr "appliquer le patch en inverse" +msgstr "appliquer le patch en sens inverse" #: builtin/apply.c:4404 msgid "don't expect at least one line of context" -msgstr "accepter moins d'une ligne de contexte" +msgstr "ne pas s'attendre à au moins une ligne de contexte" #: builtin/apply.c:4406 msgid "leave the rejected hunks in corresponding *.rej files" @@ -2205,11 +2260,11 @@ msgstr "accepter les recouvrements de sections" #: builtin/apply.c:4411 msgid "tolerate incorrectly detected missing new-line at the end of file" -msgstr "tolérer faux positifs de retours chariot manquant en fin de fichier" +msgstr "tolérer des erreurs de détection de retours chariot manquants en fin de fichier" #: builtin/apply.c:4414 msgid "do not trust the line counts in the hunk headers" -msgstr "ne pas se fier au comptes de lignes dans les entêtes de section" +msgstr "ne pas se fier au compte de lignes dans les en-têtes de section" #: builtin/apply.c:4416 msgid "root" @@ -2225,7 +2280,7 @@ msgstr "--3way hors d'un dépôt" #: builtin/apply.c:4447 msgid "--index outside a repository" -msgstr "--index hors d'une dépôt" +msgstr "--index hors d'un dépôt" #: builtin/apply.c:4450 msgid "--cached outside a repository" @@ -2247,7 +2302,7 @@ msgstr[1] "%d erreurs d'espace ignorées" #, c-format msgid "%d line adds whitespace errors." msgid_plural "%d lines add whitespace errors." -msgstr[0] "%d ligne ont ajouté des erreurs d'espace." +msgstr[0] "%d ligne a ajouté des erreurs d'espace." msgstr[1] "%d lignes ont ajouté des erreurs d'espace." #: builtin/archive.c:17 @@ -2299,23 +2354,23 @@ msgstr "mettre à jour BISECT_HEAD au lieu d'extraire le commit actuel" #: builtin/blame.c:25 msgid "git blame [options] [rev-opts] [rev] [--] file" -msgstr "git blame [options] [option de révision] [rev] [--] file" +msgstr "git blame [options] [options-de-révision] [rev] [--] fichier" #: builtin/blame.c:30 msgid "[rev-opts] are documented in git-rev-list(1)" -msgstr "[rev-opts] sont documentés dans git-rev-list(1)" +msgstr "[options-de-révision] sont documentés dans git-rev-list(1)" #: builtin/blame.c:2355 msgid "Show blame entries as we find them, incrementally" -msgstr "Montrer les entrée de blame au fur et à mesure de leur découverte, incrémentalement" +msgstr "Montrer les entrée de blâme au fur et à mesure de leur découverte, de manière incrémentale" #: builtin/blame.c:2356 msgid "Show blank SHA-1 for boundary commits (Default: off)" -msgstr "Montrer un SHA-1 blanc pour les commits de limite (défaut : désactivé)" +msgstr "Montrer un SHA-1 blanc pour les commits de limite (Défaut : désactivé)" #: builtin/blame.c:2357 msgid "Do not treat root commits as boundaries (Default: off)" -msgstr "Ne pas traiter les commits racine comme des limites (défaut : désactivé)" +msgstr "Ne pas traiter les commits racine comme des limites (Défaut : désactivé)" #: builtin/blame.c:2358 msgid "Show work cost statistics" @@ -2327,11 +2382,11 @@ msgstr "Montrer le score de sortie pour les entrées de blâme" #: builtin/blame.c:2360 msgid "Show original filename (Default: auto)" -msgstr "Montrer les noms de fichier originaux (défaut : auto)" +msgstr "Montrer les noms de fichier originaux (Défaut : auto)" #: builtin/blame.c:2361 msgid "Show original linenumber (Default: off)" -msgstr "Montrer les numéros de lignes originaux (défaut : désactivé)" +msgstr "Montrer les numéros de lignes originaux (Défaut : désactivé)" #: builtin/blame.c:2362 msgid "Show in a format designed for machine consumption" @@ -2343,23 +2398,23 @@ msgstr "Afficher en format porcelaine avec l'information de commit par ligne" #: builtin/blame.c:2364 msgid "Use the same output mode as git-annotate (Default: off)" -msgstr "Utiliser le même mode de sortie que git-annotate (défaut : désactivé)" +msgstr "Utiliser le même mode de sortie que git-annotate (Défaut : désactivé)" #: builtin/blame.c:2365 msgid "Show raw timestamp (Default: off)" -msgstr "Afficher les horodatages bruts (défaut: désactivé)" +msgstr "Afficher les horodatages bruts (Défaut: désactivé)" #: builtin/blame.c:2366 msgid "Show long commit SHA1 (Default: off)" -msgstr "Afficher les SHA1 longs (defaut : désactivé)" +msgstr "Afficher les longs SHA1 de commits (Défaut : désactivé)" #: builtin/blame.c:2367 msgid "Suppress author name and timestamp (Default: off)" -msgstr "Supprimer le nom de l'auteur et l'horodatage (défaut : désactivé)" +msgstr "Supprimer le nom de l'auteur et l'horodatage (Défaut : désactivé)" #: builtin/blame.c:2368 msgid "Show author email instead of name (Default: off)" -msgstr "Afficher l'e-mail de l'auteur au lieu du nom (défaut : désactivé)" +msgstr "Afficher l'e-mail de l'auteur au lieu du nom (Défaut : désactivé)" #: builtin/blame.c:2369 msgid "Ignore whitespace differences" @@ -2387,7 +2442,7 @@ msgstr "Trouver les copies de ligne dans et entre les fichiers" #: builtin/blame.c:2374 msgid "Find line movements within and across files" -msgstr "Trouver les mouvements dans et entre les fichiers" +msgstr "Trouver les mouvements de ligne dans et entre les fichiers" #: builtin/blame.c:2375 msgid "n,m" @@ -2411,23 +2466,25 @@ msgstr "git branch [options] [-r] (-d | -D) <nomdebranche>..." #: builtin/branch.c:27 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>" -msgstr "git branch [options] (-m | -M) [<anciennebranch>] <nouvellebranch>" +msgstr "git branch [options] (-m | -M) [<anciennebranche>] <nouvellebranche>" #: builtin/branch.c:150 #, c-format msgid "" "deleting branch '%s' that has been merged to\n" " '%s', but not yet merged to HEAD." -msgstr "suppression de la branche '%s' qui a été fusionnée dans\n" -" '%s', mais pas dans HEAD." +msgstr "" +"suppression de la branche '%s' qui a été fusionnée dans\n" +" '%s', mais pas dans HEAD." #: builtin/branch.c:154 #, c-format msgid "" "not deleting branch '%s' that is not yet merged to\n" " '%s', even though it is merged to HEAD." -msgstr "branche '%s' non supprimée car elle n'a pas été fusionnée dans\n" -" '%s', mais dans HEAD." +msgstr "" +"branche '%s' non supprimée car elle n'a pas été fusionnée dans\n" +" '%s', même si elle est fusionnée dans HEAD." #: builtin/branch.c:168 #, c-format @@ -2439,12 +2496,13 @@ msgstr "Impossible de rechercher l'objet commit pour '%s'" msgid "" "The branch '%s' is not fully merged.\n" "If you are sure you want to delete it, run 'git branch -D %s'." -msgstr "La branche '%s' n'est pas totalement fusionnée.\n" -"Si vous êtes sur que vous voulez la supprimer, lancez 'git branch -D %s'" +msgstr "" +"La branche '%s' n'est pas totalement fusionnée.\n" +"Si vous êtes sur que vous voulez la supprimer, lancez 'git branch -D %s'." #: builtin/branch.c:185 msgid "Update of config-file failed" -msgstr "Echec de la mise à jour du fichier config" +msgstr "Échec de la mise à jour du fichier de config" #: builtin/branch.c:213 msgid "cannot use -a with -d" @@ -2457,7 +2515,7 @@ msgstr "Impossible de rechercher l'objet commit pour HEAD" #: builtin/branch.c:227 #, c-format msgid "Cannot delete the branch '%s' which you are currently on." -msgstr "Impossible d'effacer la branche '%s' qui est actuellement extraite" +msgstr "Impossible de supprimer la branche '%s' sur laquelle vous êtes." #: builtin/branch.c:240 #, c-format @@ -2482,12 +2540,12 @@ msgstr "Erreur lors de la suppression de la branche '%s'" #: builtin/branch.c:263 #, c-format msgid "Deleted remote branch %s (was %s).\n" -msgstr "Branche distante %s supprimée (précédemment %s)\n" +msgstr "Branche distante %s supprimée (précédemment %s).\n" #: builtin/branch.c:264 #, c-format msgid "Deleted branch %s (was %s).\n" -msgstr "Branche %s supprimée (précédemment %s)\n" +msgstr "Branche %s supprimée (précédemment %s).\n" #: builtin/branch.c:366 #, c-format @@ -2577,11 +2635,11 @@ msgstr "Renommage d'un branche mal nommée '%s'" #: builtin/branch.c:715 #, c-format msgid "Branch renamed to %s, but HEAD is not updated!" -msgstr "La branche a été renommée '%s', mais HEAD n'est pas mis à jour !" +msgstr "La branche a été renommée en %s, mais HEAD n'est pas mise à jour !" #: builtin/branch.c:722 msgid "Branch is renamed, but update of config-file failed" -msgstr "La branche est renommée, mais echec de la mise à jour du fichier de config" +msgstr "La branche est renommée, mais la mise à jour du fichier de config a échoué" #: builtin/branch.c:737 #, c-format @@ -2599,7 +2657,7 @@ msgstr "Options génériques" #: builtin/branch.c:793 msgid "show hash and subject, give twice for upstream branch" -msgstr "afficher le condensé et le sujet, doublé pour la branche amont" +msgstr "afficher le hachage et le sujet, doublé pour la branche amont" #: builtin/branch.c:794 msgid "suppress informational messages" @@ -2619,7 +2677,7 @@ msgstr "utiliser la coloration dans la sortie" #: builtin/branch.c:802 msgid "act on remote-tracking branches" -msgstr "agir sur les branches de suivi distant" +msgstr "agir sur les branches de suivi distantes" #: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832 #: builtin/branch.c:838 builtin/commit.c:1368 builtin/commit.c:1369 @@ -2685,11 +2743,11 @@ msgstr "afficher les branches en colonnes" #: builtin/branch.c:855 msgid "Failed to resolve HEAD as a valid ref." -msgstr "Echec de résolution de HEAD comme référence valide." +msgstr "Échec de résolution de HEAD comme référence valide." #: builtin/branch.c:860 builtin/clone.c:619 msgid "HEAD not found below refs/heads!" -msgstr "HEAD non trouvé sous refs/heads !" +msgstr "HEAD non trouvée sous refs/heads !" #: builtin/branch.c:883 msgid "--column and --verbose are incompatible" @@ -2697,11 +2755,11 @@ msgstr "--column et --verbose sont incompatibles" #: builtin/branch.c:889 builtin/branch.c:928 msgid "branch name required" -msgstr "nom de branche exigé" +msgstr "le nom de branche est requis" #: builtin/branch.c:904 msgid "Cannot give description to detached HEAD" -msgstr "Impossible de décrire HEAD détaché" +msgstr "Impossible de décrire une HEAD détachée" #: builtin/branch.c:909 msgid "cannot edit description of more than one branch" @@ -2710,7 +2768,7 @@ msgstr "impossible d'éditer la description de plus d'une branche" #: builtin/branch.c:916 #, c-format msgid "No commit on branch '%s' yet." -msgstr "Aucun commit sur la branche '%s'" +msgstr "Aucun commit sur la branche '%s'." #: builtin/branch.c:919 #, c-format @@ -2734,12 +2792,12 @@ msgstr "impossible de spécifier une branche amont de HEAD par %s qui ne pointe #: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990 #, c-format msgid "no such branch '%s'" -msgstr "branche inexistante '%s'" +msgstr "pas de branche '%s'" #: builtin/branch.c:950 #, c-format msgid "branch '%s' does not exist" -msgstr "branche inexistante '%s'" +msgstr "la branche '%s' n'existe pas" #: builtin/branch.c:962 msgid "too many branches to unset upstream" @@ -2747,7 +2805,7 @@ msgstr "trop de branches pour désactiver un amont" #: builtin/branch.c:966 msgid "could not unset upstream of HEAD when it does not point to any branch." -msgstr "impossible de désactiver une branche amont de HEAD qu'elle ne pointe sur aucune branche." +msgstr "impossible de désactiver une branche amont de HEAD quand elle ne pointe sur aucune branche." #: builtin/branch.c:972 #, c-format @@ -2792,15 +2850,15 @@ msgstr " git branch -set-upstream-to %s\n" #: builtin/bundle.c:47 #, c-format msgid "%s is okay\n" -msgstr "%s convient\n" +msgstr "%s est correct\n" #: builtin/bundle.c:56 msgid "Need a repository to create a bundle." -msgstr "La création d'un bundle requiert un dépôt." +msgstr "La création d'un colis requiert un dépôt." #: builtin/bundle.c:60 msgid "Need a repository to unbundle." -msgstr "Le dépaquetage d'un bundle requiert un dépôt." +msgstr "Le dépaquetage d'un colis requiert un dépôt." #: builtin/cat-file.c:176 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>" @@ -2820,7 +2878,7 @@ msgstr "afficher le type de l'objet" #: builtin/cat-file.c:197 msgid "show object size" -msgstr "affichier la taille de l'objet" +msgstr "afficher la taille de l'objet" #: builtin/cat-file.c:199 msgid "exit with zero when there's no error" @@ -2836,7 +2894,7 @@ msgstr "pour les objets blob, lancer textconv sur le contenu de l'objet" #: builtin/cat-file.c:204 msgid "show info and content of objects fed from the standard input" -msgstr "afficher l'information et le contenu des objets passé en entrée standard" +msgstr "afficher l'information et le contenu des objets passés en entrée standard" #: builtin/cat-file.c:207 msgid "show info about objects fed from the standard input" @@ -2868,11 +2926,11 @@ msgstr "les chemins en entrée sont terminés par le caractère nul" #: builtin/check-ignore.c:18 builtin/checkout.c:1044 builtin/gc.c:177 msgid "suppress progress reporting" -msgstr "supprimer le rapport de progrès" +msgstr "supprimer l'état d'avancement" #: builtin/check-ignore.c:146 msgid "cannot specify pathnames with --stdin" -msgstr "l'option --stdin et la spécification de chemin sont incompatibles" +msgstr "impossible de spécifier les chemins avec --stdin" #: builtin/check-ignore.c:149 msgid "-z only makes sense with --stdin" @@ -2888,7 +2946,7 @@ msgstr "--quiet n'est valide qu'avec un seul chemin" #: builtin/check-ignore.c:157 msgid "cannot have both --quiet and --verbose" -msgstr "les options --quiet et --verbose sont incompatibles" +msgstr "impossible d'avoir --quiet et --verbose" #: builtin/checkout-index.c:126 msgid "git checkout-index [options] [--] [<file>...]" @@ -2900,11 +2958,11 @@ msgstr "extraire tous les fichiers présents dans l'index" #: builtin/checkout-index.c:188 msgid "force overwrite of existing files" -msgstr "forcer l'écrasement des fichiers existant" +msgstr "forcer l'écrasement des fichiers existants" #: builtin/checkout-index.c:190 msgid "no warning for existing files and files not in index" -msgstr "éliminer les alertes pour les fichiers existant et les fichiers absents de l'index" +msgstr "pas d'avertissement pour les fichiers existants et les fichiers absents de l'index" #: builtin/checkout-index.c:192 msgid "don't checkout new files" @@ -2928,7 +2986,7 @@ msgstr "chaîne" #: builtin/checkout-index.c:204 msgid "when creating files, prepend <string>" -msgstr "lors de la création de fichiers, préfixer <chaîne>" +msgstr "lors de la création de fichiers, préfixer par <chaîne>" #: builtin/checkout-index.c:207 msgid "copy out the files from named stage" @@ -2965,7 +3023,7 @@ msgstr "le chemin '%s' n'a pas les versions nécessaires" #: builtin/checkout.c:196 #, c-format msgid "path '%s': cannot merge" -msgstr "impossible de fusionner le chemin '%s'" +msgstr "chemin '%s' : impossible de fusionner" #: builtin/checkout.c:213 #, c-format @@ -3008,7 +3066,7 @@ msgstr "Impossible de faire un reflog pour '%s'\n" #: builtin/checkout.c:634 msgid "HEAD is now at" -msgstr "HEAD est maintenant sur " +msgstr "HEAD est maintenant sur" #: builtin/checkout.c:641 #, c-format @@ -3053,12 +3111,14 @@ msgid_plural "" "any of your branches:\n" "\n" "%s\n" -msgstr[0] "Attention : vous laissez %d commit en retard, connectés à aucune de vos\n" -"branches :\n" +msgstr[0] "" +"Attention : vous laissez %d commit en retard, non connectés à \n" +"une branche :\n" "\n" "%s\n" -msgstr[1] "Attention : vous laissez %d commits en retard, connectés à aucune de vos\n" -"branches :\n" +msgstr[1] "" +"Attention : vous laissez %d commits en retard, non connectés à \n" +"une branche :\n" "\n" "%s\n" @@ -3070,7 +3130,9 @@ msgid "" "\n" " git branch new_branch_name %s\n" "\n" -msgstr "Si vous souhaitez les garder en créant une nouvelle branches, c'est le bon moment avec :\n" +msgstr "" +"Si vous souhaitez les garder en créant une nouvelle branche, c'est le bon moment\n" +"de le faire avec :\n" "\n" "git branche nouvelle_branche %s\n" "\n" @@ -3081,7 +3143,7 @@ msgstr "erreur interne lors du parcours des révisions" #: builtin/checkout.c:766 msgid "Previous HEAD position was" -msgstr "Lo position précédente de HEAD était " +msgstr "La position précédente de HEAD était" #: builtin/checkout.c:793 builtin/checkout.c:982 msgid "You are on a branch yet to be born" @@ -3106,13 +3168,13 @@ msgstr "impossible d'utiliser des chemins avec un basculement de branches" #: builtin/checkout.c:999 builtin/checkout.c:1003 #, c-format msgid "'%s' cannot be used with switching branches" -msgstr "impossible d'utiliser '%s' avec un basculement de branches " +msgstr "'%s' ne peut pas être utilisé avec un basculement de branches" #: builtin/checkout.c:1007 builtin/checkout.c:1010 builtin/checkout.c:1015 #: builtin/checkout.c:1018 #, c-format msgid "'%s' cannot be used with '%s'" -msgstr "impossible d'utiliser '%s' avec '%s'" +msgstr "'%s' ne peut pas être utilisé avec '%s'" #: builtin/checkout.c:1023 #, c-format @@ -3126,11 +3188,11 @@ msgstr "branche" #: builtin/checkout.c:1046 msgid "create and checkout a new branch" -msgstr "créer et extrait une nouvelle branche" +msgstr "créer et extraire une nouvelle branche" #: builtin/checkout.c:1048 msgid "create/reset and checkout a branch" -msgstr "créer/reinitialise et extrait une branche" +msgstr "créer/réinitialiser et extraire une branche" #: builtin/checkout.c:1049 msgid "create reflog for new branch" @@ -3138,11 +3200,11 @@ msgstr "créer un refog pour une nouvelle branche" #: builtin/checkout.c:1050 msgid "detach the HEAD at named commit" -msgstr "détacher le HEAD au commit nommé" +msgstr "détacher la HEAD à la validation nommée" #: builtin/checkout.c:1051 msgid "set upstream info for new branch" -msgstr "paramètrer l'information de branche amont pour une nouvelle branche" +msgstr "paramétrer l'information de branche amont pour une nouvelle branche" #: builtin/checkout.c:1053 msgid "new branch" @@ -3162,7 +3224,7 @@ msgstr "extraire leur version pour les fichiers non fusionnés" #: builtin/checkout.c:1058 msgid "force checkout (throw away local modifications)" -msgstr "forcer l'extraction (écraser les modifications locales)" +msgstr "forcer l'extraction (laisser tomber les modifications locales)" #: builtin/checkout.c:1059 msgid "perform a 3-way merge with the new branch" @@ -3194,11 +3256,11 @@ msgstr "-b, -B et --orphan sont mutuellement exclusifs" #: builtin/checkout.c:1108 msgid "--track needs a branch name" -msgstr "un nom de branche est nécessaire pour --track" +msgstr "--track requiert un nom de branche" #: builtin/checkout.c:1115 msgid "Missing branch name; try -b" -msgstr "Nom de branche manquant ; essayez avec -b" +msgstr "Nom de branche manquant ; essayez -b" #: builtin/checkout.c:1150 msgid "invalid path specification" @@ -3209,24 +3271,26 @@ msgstr "spécification de chemin invalide" msgid "" "Cannot update paths and switch to branch '%s' at the same time.\n" "Did you intend to checkout '%s' which can not be resolved as commit?" -msgstr "Impossible e mettre à jour les chemins et de basculer sur la branche '%s' en même temps.\n" +msgstr "" +"Impossible de mettre à jour les chemins et de basculer sur la branche '%s' en même temps.\n" "Souhaitiez-vous extraire '%s' qui ne peut être résolu comme commit ?" #: builtin/checkout.c:1162 #, c-format msgid "git checkout: --detach does not take a path argument '%s'" -msgstr "git checkout: --detach d'accepte pas un argument de chemin '%s'" +msgstr "git checkout: --detach n'accepte pas un argument de chemin '%s'" #: builtin/checkout.c:1166 msgid "" "git checkout: --ours/--theirs, --force and --merge are incompatible when\n" "checking out of the index." -msgstr "git checkout: --ours/--theirs, --force et --merge sont incompatible lors\n" +msgstr "" +"git checkout: --ours/--theirs, --force et --merge sont incompatibles lors\n" "de l'extraction de l'index." #: builtin/clean.c:20 msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..." -msgstr "git clean [-d] [-f] [-n] [-q] [-e <expression>] [-x | -X] [--] <chemins>..." +msgstr "git clean [-d] [-f] [-n] [-q] [-e <motif>] [-x | -X] [--] <chemins>..." #: builtin/clean.c:24 #, c-format @@ -3241,7 +3305,7 @@ msgstr "Supprimerait %s\n" #: builtin/clean.c:26 #, c-format msgid "Skipping repository %s\n" -msgstr "Dépôt %s ignoré\n" +msgstr "Ignore le dépôt %s\n" #: builtin/clean.c:27 #, c-format @@ -3251,7 +3315,7 @@ msgstr "Ignorerait le dépôt %s\n" #: builtin/clean.c:28 #, c-format msgid "failed to remove %s" -msgstr "echec de la suppression de %s" +msgstr "échec de la suppression de %s" #: builtin/clean.c:160 msgid "do not print names of files removed" @@ -3263,16 +3327,16 @@ msgstr "forcer" #: builtin/clean.c:164 msgid "remove whole directories" -msgstr "supprimer des répertoires entiers" +msgstr "supprimer les répertoires entiers" #: builtin/clean.c:165 builtin/describe.c:412 builtin/grep.c:717 #: builtin/ls-files.c:487 builtin/name-rev.c:231 builtin/show-ref.c:182 msgid "pattern" -msgstr "expression" +msgstr "motif" #: builtin/clean.c:166 msgid "add <pattern> to ignore rules" -msgstr "ajouter <expression> aux règles ignore" +msgstr "ajouter <motif> aux règles ignore" #: builtin/clean.c:167 msgid "remove ignored files, too" @@ -3284,7 +3348,7 @@ msgstr "supprimer seulement les fichiers ignorés" #: builtin/clean.c:187 msgid "-x and -X cannot be used together" -msgstr "-x et -X sont mutuellement exclusifs" +msgstr "-x et -X ne peuvent pas être utilisés ensemble" #: builtin/clean.c:191 msgid "" @@ -3304,7 +3368,7 @@ msgstr "git clone [options] [--] <dépôt> [<répertoire>]" #: builtin/clone.c:65 builtin/fetch.c:82 builtin/merge.c:214 #: builtin/push.c:436 msgid "force progress reporting" -msgstr "forcer l'affichage du progrès" +msgstr "forcer l'état d'avancement" #: builtin/clone.c:67 msgid "don't create a checkout" @@ -3324,11 +3388,11 @@ msgstr "pour cloner depuis un dépôt local" #: builtin/clone.c:77 msgid "don't use local hardlinks, always copy" -msgstr "ne pas utiliser de liens durs locaux, mais toujours copier" +msgstr "ne pas utiliser de liens durs locaux, toujours copier" #: builtin/clone.c:79 msgid "setup as shared repository" -msgstr "régler comme dépôt partager" +msgstr "régler comme dépôt partagé" #: builtin/clone.c:81 builtin/clone.c:83 msgid "initialize submodules in the clone" @@ -3352,11 +3416,11 @@ msgstr "nom" #: builtin/clone.c:89 msgid "use <name> instead of 'origin' to track upstream" -msgstr "utiliser <nom> au lieu de 'origin' pour traquer la branche amont" +msgstr "utiliser <nom> au lieu de 'origin' pour suivre la branche amont" #: builtin/clone.c:91 msgid "checkout <branch> instead of the remote's HEAD" -msgstr "extraire <branche> au lieu du HEAD du répertoire distant" +msgstr "extraire <branche> au lieu de la HEAD du répertoire distant" #: builtin/clone.c:93 msgid "path to git-upload-pack on the remote" @@ -3372,7 +3436,7 @@ msgstr "créer un clone superficiel de cette profondeur" #: builtin/clone.c:97 msgid "clone only one branch, HEAD or --branch" -msgstr "clonez seulement une branche, HEAD ou --branch" +msgstr "cloner seulement une branche, HEAD ou --branch" #: builtin/clone.c:98 builtin/init-db.c:494 msgid "gitdir" @@ -3393,37 +3457,37 @@ msgstr "régler la configuration dans le nouveau dépôt" #: builtin/clone.c:254 #, c-format msgid "reference repository '%s' is not a local repository." -msgstr "le dépôt de référence '%s' n'est pas un dépôt local" +msgstr "le dépôt de référence '%s' n'est pas un dépôt local." #: builtin/clone.c:317 #, c-format msgid "failed to create directory '%s'" -msgstr "echec de la création du répertoire '%s'" +msgstr "échec de la création du répertoire '%s'" #: builtin/clone.c:319 builtin/diff.c:77 #, c-format msgid "failed to stat '%s'" -msgstr "echec du stat de '%s'" +msgstr "échec du stat de '%s'" #: builtin/clone.c:321 #, c-format msgid "%s exists and is not a directory" -msgstr "%s existe et n'est pas un répertoire" +msgstr "%s existe et n'est pas un répertoire" #: builtin/clone.c:335 #, c-format msgid "failed to stat %s\n" -msgstr "echec du stat de %s\n" +msgstr "échec du stat de %s\n" #: builtin/clone.c:357 #, c-format msgid "failed to create link '%s'" -msgstr "echec de la création du lien '%s'" +msgstr "échec de la création du lien '%s'" #: builtin/clone.c:361 #, c-format msgid "failed to copy file to '%s'" -msgstr "echec de la copie vers '%s'" +msgstr "échec de la copie vers '%s'" #: builtin/clone.c:384 #, c-format @@ -3450,15 +3514,15 @@ msgstr "le serveur distant n'a pas envoyé tous les objets nécessaires" #: builtin/clone.c:610 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n" -msgstr "le HEAD distant réfère à une référence non existante, impossible de l'extraire.\n" +msgstr "la HEAD distante réfère à une référence non existante, impossible de l'extraire.\n" #: builtin/clone.c:641 msgid "unable to checkout working tree" -msgstr "incapable d'extraire la copie de travail" +msgstr "inpossible d'extraire la copie de travail" #: builtin/clone.c:749 msgid "Too many arguments." -msgstr "Trop d'arguments" +msgstr "Trop d'arguments." #: builtin/clone.c:753 msgid "You must specify a repository to clone." @@ -3467,11 +3531,11 @@ msgstr "Vous devez spécifier un dépôt à cloner." #: builtin/clone.c:764 #, c-format msgid "--bare and --origin %s options are incompatible." -msgstr "les options --bare et --origin %s sont incompatibles" +msgstr "les options --bare et --origin %s sont incompatibles." #: builtin/clone.c:767 msgid "--bare and --separate-git-dir are incompatible." -msgstr "les option --bare --separate-git-dir sont incompatibles." +msgstr "--bare et --separate-git-dir sont incompatibles." #: builtin/clone.c:780 #, c-format @@ -3480,12 +3544,12 @@ msgstr "le dépôt '%s' n'existe pas" #: builtin/clone.c:785 msgid "--depth is ignored in local clones; use file:// instead." -msgstr "--depth est ignoré dans les clones locaux : utilisez plutôt file://" +msgstr "--depth est ignoré dans les clones locaux : utilisez plutôt \"file://\"." #: builtin/clone.c:795 #, c-format msgid "destination path '%s' already exists and is not an empty directory." -msgstr "le chemin de destination '%s' existe déjà et n'est pas un répertoire vide." +msgstr "le chemin de destination '%s' existe déjà et n'est pas un répertoire vide." #: builtin/clone.c:805 #, c-format @@ -3500,7 +3564,7 @@ msgstr "impossible de créer les répertoires de premier niveau dans '%s'" #: builtin/clone.c:821 #, c-format msgid "could not create work tree dir '%s'." -msgstr "impossible de créer le répertoire de la copie de travail '%s'" +msgstr "impossible de créer le répertoire de la copie de travail '%s'." #: builtin/clone.c:840 #, c-format @@ -3510,12 +3574,12 @@ msgstr "Clonage dans le dépôt nu '%s'\n" #: builtin/clone.c:842 #, c-format msgid "Cloning into '%s'...\n" -msgstr "Clonage dans '%s'\n" +msgstr "Clonage dans '%s'...\n" #: builtin/clone.c:877 #, c-format msgid "Don't know how to clone %s" -msgstr "Je ne sais pas cloner '%s'" +msgstr "Je ne sais pas cloner %s" #: builtin/clone.c:926 #, c-format @@ -3544,15 +3608,15 @@ msgstr "Largeur maximale" #: builtin/column.c:30 msgid "Padding space on left border" -msgstr "Tabulation sur le côté gauche" +msgstr "Remplissage d'espace sur la bordure gauche" #: builtin/column.c:31 msgid "Padding space on right border" -msgstr "Tabulation sur le côté droit" +msgstr "Remplissage d'espace sur le côté droit" #: builtin/column.c:32 msgid "Padding space between columns" -msgstr "Tabulation entre colonnes" +msgstr "Remplissage d'espace entre les colonnes" #: builtin/column.c:51 msgid "--command must be the first argument" @@ -3560,11 +3624,11 @@ msgstr "--command doit être le premier argument" #: builtin/commit.c:34 msgid "git commit [options] [--] <pathspec>..." -msgstr "git commit [options] [--] <spécification de chemin>..." +msgstr "git commit [options] [--] <spécification-de-chemin>..." #: builtin/commit.c:39 msgid "git status [options] [--] <pathspec>..." -msgstr "git status [options] [--] <spécification de chemin>..." +msgstr "git status [options] [--] <spécification-de-chemin>..." #: builtin/commit.c:44 msgid "" @@ -3578,12 +3642,13 @@ msgid "" "After doing this, you may fix the identity used for this commit with:\n" "\n" " git commit --amend --reset-author\n" -msgstr "Votre nom et votre adresse e-mail ont été configurés automatiquement en se fondant\n" +msgstr "" +"Votre nom et votre adresse e-mail ont été configurés automatiquement en se fondant\n" "sur votre nom d'utilisateur et votre nom d'ordinateur. Veuillez vérifier qu'ils sont corrects.\n" "Vous pouvez supprimer ce message en les paramétrant explicitement :\n" "\n" " git config --global user.name \"Votre Nom\"\n" -" git config --global user.email \"vous@exemple.com\n" +" git config --global user.email vous@exemple.com\n" "\n" "Après ceci, vous pouvez corriger l'identité utilisée pour ce commit avec :\n" "\n" @@ -3606,7 +3671,8 @@ msgid "" " git commit --allow-empty\n" "\n" "Otherwise, please use 'git reset'\n" -msgstr "Le picorage précédent est à présent vide, vraisemblablement du à une résolution de conflit.\n" +msgstr "" +"Le picorage précédent est à présent vide, vraisemblablement dû à une résolution de conflit.\n" "Si vous souhaitez tout de même le valider, utilisez :\n" "\n" " git commit --allow-empty\n" @@ -3615,7 +3681,7 @@ msgstr "Le picorage précédent est à présent vide, vraisemblablement du à un #: builtin/commit.c:260 msgid "failed to unpack HEAD tree object" -msgstr "echec du dépaquetage de l'objet arbre HEAD" +msgstr "échec du dépaquetage de l'objet arbre HEAD" #: builtin/commit.c:302 msgid "unable to create temporary index" @@ -3623,7 +3689,7 @@ msgstr "impossible de créer l'index temporaire" #: builtin/commit.c:308 msgid "interactive add failed" -msgstr "echec de l'ajout interactif" +msgstr "échec de l'ajout interactif" #: builtin/commit.c:341 builtin/commit.c:362 builtin/commit.c:412 msgid "unable to write new_index file" @@ -3657,7 +3723,7 @@ msgstr "paramètre --author mal formé" #: builtin/commit.c:562 #, c-format msgid "Malformed ident string: '%s'" -msgstr "chaîne ident mal formée : '%s'" +msgstr "Chaîne ident mal formée : '%s'" #: builtin/commit.c:600 builtin/commit.c:633 builtin/commit.c:956 #, c-format @@ -3707,7 +3773,8 @@ msgid "" "If this is not correct, please remove the file\n" "\t%s\n" "and try again.\n" -msgstr "\n" +msgstr "" +"\n" "Il semble que vous validiez une fusion.\n" "Si ce n'est pas le cas, veuillez supprimer le fichier\n" "\t%s\n" @@ -3721,7 +3788,8 @@ msgid "" "If this is not correct, please remove the file\n" "\t%s\n" "and try again.\n" -msgstr "\n" +msgstr "" +"\n" "Il semble que vous validiez un picorage.\n" "Si ce n'est pas le cas, veuillez supprimer le fichier\n" "\t%s\n" @@ -3732,9 +3800,9 @@ msgstr "\n" msgid "" "Please enter the commit message for your changes. Lines starting\n" "with '%c' will be ignored, and an empty message aborts the commit.\n" -msgstr "Veuillez saisir le message de validation pour vos modifications. Les lignes\n" -"commençant par '%c' seront ignorées, et un message vide abandonne la\n" -"validation.\n" +msgstr "" +"Veuillez saisir le message de validation pour vos modifications. Les lignes\n" +"commençant par '%c' seront ignorées, et un message vide abandonne la validation.\n" #: builtin/commit.c:742 #, c-format @@ -3742,19 +3810,20 @@ msgid "" "Please enter the commit message for your changes. Lines starting\n" "with '%c' will be kept; you may remove them yourself if you want to.\n" "An empty message aborts the commit.\n" -msgstr "Veuillez saisir le message de validation pour vos modifications. Les lignes\n" -"commençant par '%c' seront conservées ; vous pouvez les supprimer vous-\n" -"même si vous le souhaitez. Un message vide abandonne la validation.\n" +msgstr "" +"Veuillez saisir le message de validation pour vos modifications. Les lignes\n" +"commençant par '%c' seront conservées ; vous pouvez les supprimer vous-même\n" +"si vous le souhaitez. Un message vide abandonne la validation.\n" #: builtin/commit.c:755 #, c-format msgid "%sAuthor: %s" -msgstr "%sAuteur : %s" +msgstr "%sAuteur : %s" #: builtin/commit.c:762 #, c-format msgid "%sCommitter: %s" -msgstr "%s Validateur: %s" +msgstr "%sValidateur : %s" #: builtin/commit.c:782 msgid "Cannot read index" @@ -3781,7 +3850,7 @@ msgstr "Mode de fichier non suivi invalide '%s'" #: builtin/commit.c:976 msgid "Using both --reset-author and --author does not make sense" -msgstr "L'utilisation concurrente de --reset-author et --author n'a pas de sens" +msgstr "L'utilisation simultanée de --reset-author et --author n'a pas de sens" #: builtin/commit.c:987 msgid "You have nothing to amend." @@ -3797,7 +3866,7 @@ msgstr "Vous êtes en plein picorage -- impossible de corriger (amend)." #: builtin/commit.c:995 msgid "Options --squash and --fixup cannot be used together" -msgstr "Les options --squash et --fixup sont incompatibles" +msgstr "Les options --squash et --fixup ne peuvent pas être utilisées ensemble" #: builtin/commit.c:1005 msgid "Only one of -c/-C/-F/--fixup can be used." @@ -3821,7 +3890,7 @@ msgstr "Aucun chemin avec les options --include/--only n'a pas de sens." #: builtin/commit.c:1036 msgid "Clever... amending the last one with dirty index." -msgstr "Malin... correction du dernier commit avec un index sale." +msgstr "Malin... correction du dernier avec un index sale." #: builtin/commit.c:1038 msgid "Explicit paths specified without -i nor -o; assuming --only paths..." @@ -7376,150 +7445,152 @@ msgstr "" #: builtin/remote.c:11 msgid "git remote [-v | --verbose]" -msgstr "" +msgstr "git remote [-v | --verbose]" #: builtin/remote.c:12 msgid "" "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--" "mirror=<fetch|push>] <name> <url>" -msgstr "" +msgstr "git remote add [-t <branche>] [-m <maîtresse>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <nom> <URL>" #: builtin/remote.c:13 builtin/remote.c:32 msgid "git remote rename <old> <new>" -msgstr "" +msgstr "git remote rename <ancienne> <nouvelle>" #: builtin/remote.c:14 builtin/remote.c:37 msgid "git remote remove <name>" -msgstr "" +msgstr "git remote remove <nom>" #: builtin/remote.c:15 builtin/remote.c:42 msgid "git remote set-head <name> (-a | -d | <branch>)" -msgstr "" +msgstr "git remote set-head <nom> (-a | -d | <branche>)" #: builtin/remote.c:16 msgid "git remote [-v | --verbose] show [-n] <name>" -msgstr "" +msgstr "git remote [-v | --verbose] show [-n] <nom>" #: builtin/remote.c:17 msgid "git remote prune [-n | --dry-run] <name>" -msgstr "" +msgstr "git remote prune [-n | --dry-run] <nom>" #: builtin/remote.c:18 msgid "" "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]" -msgstr "" +msgstr "git remote [-v | --verbose] update [-p | --prune] [(<groupe> | <distante>)...]" #: builtin/remote.c:19 msgid "git remote set-branches [--add] <name> <branch>..." -msgstr "" +msgstr "git remote set-branches [--add] <nom> <branche>..." #: builtin/remote.c:20 builtin/remote.c:68 msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]" -msgstr "" +msgstr "git remote set-url [--push] <nom> <nouvelle-URL> [<ancienne-URL>]" #: builtin/remote.c:21 builtin/remote.c:69 msgid "git remote set-url --add <name> <newurl>" -msgstr "" +msgstr "git remote set-url --add <nom> <nouvelle-URL>" #: builtin/remote.c:22 builtin/remote.c:70 msgid "git remote set-url --delete <name> <url>" -msgstr "" +msgstr "git remote set-url --delete <nom> <URL>" #: builtin/remote.c:27 msgid "git remote add [<options>] <name> <url>" -msgstr "" +msgstr "git remote add [<options>] <nom> <URL>" #: builtin/remote.c:47 msgid "git remote set-branches <name> <branch>..." -msgstr "" +msgstr "git remote set-branches <nom> <branche>..." #: builtin/remote.c:48 msgid "git remote set-branches --add <name> <branch>..." -msgstr "" +msgstr "git remote set-branches --add <nom> <branche>..." #: builtin/remote.c:53 msgid "git remote show [<options>] <name>" -msgstr "" +msgstr "git remote show [<options>] <nom>" #: builtin/remote.c:58 msgid "git remote prune [<options>] <name>" -msgstr "" +msgstr "git remote prune [<options>] <nom>" #: builtin/remote.c:63 msgid "git remote update [<options>] [<group> | <remote>]..." -msgstr "" +msgstr "git remote update [<options>] [<groupe> | <distante>]..." #: builtin/remote.c:98 #, c-format msgid "Updating %s" -msgstr "" +msgstr "Mise à jour de %s" #: builtin/remote.c:130 msgid "" "--mirror is dangerous and deprecated; please\n" "\t use --mirror=fetch or --mirror=push instead" msgstr "" +"--mirror est dangereux et obsolète ; merci\n" +"\t d'utiliser --mirror=fetch ou --mirror=push à la place" #: builtin/remote.c:147 #, c-format msgid "unknown mirror argument: %s" -msgstr "" +msgstr "argument miroir inconnu : %s" #: builtin/remote.c:163 msgid "fetch the remote branches" -msgstr "" +msgstr "rapatrier les branches distantes" #: builtin/remote.c:165 msgid "import all tags and associated objects when fetching" -msgstr "" +msgstr "importer toutes les étiquettes et les objets associés lors du rapatriement" #: builtin/remote.c:168 msgid "or do not fetch any tag at all (--no-tags)" -msgstr "" +msgstr "ou ne rapatrier aucune étiquette (--no-tags)" #: builtin/remote.c:170 msgid "branch(es) to track" -msgstr "" +msgstr "branche(s) à suivre" #: builtin/remote.c:171 msgid "master branch" -msgstr "" +msgstr "branche maîtresse" #: builtin/remote.c:172 msgid "push|fetch" -msgstr "" +msgstr "push|fetch" #: builtin/remote.c:173 msgid "set up remote as a mirror to push to or fetch from" -msgstr "" +msgstr "paramétrer la distante comme miroir pour pousser ou pour rapatrier depuis" #: builtin/remote.c:185 msgid "specifying a master branch makes no sense with --mirror" -msgstr "" +msgstr "spécifier une branche maîtresse n'a pas de sens avec --mirror" #: builtin/remote.c:187 msgid "specifying branches to track makes sense only with fetch mirrors" -msgstr "" +msgstr "spécifier les branches à suivre n'a de sens qu'avec des miroirs de rapatriement" #: builtin/remote.c:195 builtin/remote.c:646 #, c-format msgid "remote %s already exists." -msgstr "" +msgstr "la distante %s existe déjà ." #: builtin/remote.c:199 builtin/remote.c:650 #, c-format msgid "'%s' is not a valid remote name" -msgstr "" +msgstr "'%s' n'est pas un nom valide de distante" #: builtin/remote.c:243 #, c-format msgid "Could not setup master '%s'" -msgstr "" +msgstr "Impossible de paramétrer la maîtresse '%s'" #: builtin/remote.c:299 #, c-format msgid "more than one %s" -msgstr "" +msgstr "plus d'un %s" #: builtin/remote.c:339 #, c-format @@ -7528,31 +7599,31 @@ msgstr "" #: builtin/remote.c:440 builtin/remote.c:448 msgid "(matching)" -msgstr "" +msgstr "(correspond)" #: builtin/remote.c:452 msgid "(delete)" -msgstr "" +msgstr "(supprimer)" #: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607 #, c-format msgid "Could not append '%s' to '%s'" -msgstr "" +msgstr "Impossible d'ajouter '%s' à '%s'" #: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890 #, c-format msgid "No such remote: %s" -msgstr "" +msgstr "Distante inconnue : %s" #: builtin/remote.c:656 #, c-format msgid "Could not rename config section '%s' to '%s'" -msgstr "" +msgstr "Impossible de renommer la section de configuration '%s' en '%s'" #: builtin/remote.c:662 builtin/remote.c:799 #, c-format msgid "Could not remove config section '%s'" -msgstr "" +msgstr "Impossible de supprimer la section de configuration '%s'" #: builtin/remote.c:677 #, c-format @@ -7561,31 +7632,34 @@ msgid "" "\t%s\n" "\tPlease update the configuration manually if necessary." msgstr "" +"Pas de mise à jour du refspec de rapatriement qui n'est pas par défaut\n" +"\t%s\n" +"\tMerci de mettre à jour la configuration si nécessaire." #: builtin/remote.c:683 #, c-format msgid "Could not append '%s'" -msgstr "" +msgstr "Impossible d'ajouter '%s'" #: builtin/remote.c:694 #, c-format msgid "Could not set '%s'" -msgstr "" +msgstr "Impossible de définir '%s'" #: builtin/remote.c:716 #, c-format msgid "deleting '%s' failed" -msgstr "" +msgstr "échec de suppression de '%s'" #: builtin/remote.c:750 #, c-format msgid "creating '%s' failed" -msgstr "" +msgstr "échec de création de '%s'" #: builtin/remote.c:764 #, c-format msgid "Could not remove branch %s" -msgstr "" +msgstr "Impossible de supprimer la branche %s" #: builtin/remote.c:834 msgid "" @@ -7595,258 +7669,262 @@ msgid_plural "" "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n" "to delete them, use:" msgstr[0] "" +"Note : Une branche en dehors de refs/remotes/ n'a pas été supprimée ;\n" +"pour la supprimer, utilisez :" msgstr[1] "" +"Note : Plusieurs branches en dehors de refs/remotes/ n'ont pas été supprimées ;\n" +"pour les supprimer, utilisez :" #: builtin/remote.c:943 #, c-format msgid " new (next fetch will store in remotes/%s)" -msgstr "" +msgstr " nouveau (le prochain rapatriement (fetch) stockera dans remotes/%s)" #: builtin/remote.c:946 msgid " tracked" -msgstr "" +msgstr " suivi" #: builtin/remote.c:948 msgid " stale (use 'git remote prune' to remove)" -msgstr "" +msgstr " dépassé (utilisez 'git remote prune' pour supprimer)" #: builtin/remote.c:950 msgid " ???" -msgstr "" +msgstr " ???" #: builtin/remote.c:991 #, c-format msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch" -msgstr "" +msgstr "branch.%s.merge invalide ; ne peut pas rebaser sur plus d'une branche" #: builtin/remote.c:998 #, c-format msgid "rebases onto remote %s" -msgstr "" +msgstr "rebase sur la distante %s" #: builtin/remote.c:1001 #, c-format msgid " merges with remote %s" -msgstr "" +msgstr " fusionne avec la distante %s" #: builtin/remote.c:1002 msgid " and with remote" -msgstr "" +msgstr " et avec la distante" #: builtin/remote.c:1004 #, c-format msgid "merges with remote %s" -msgstr "" +msgstr "fusionne avec la distante %s" #: builtin/remote.c:1005 msgid " and with remote" -msgstr "" +msgstr " et avec la distante" #: builtin/remote.c:1051 msgid "create" -msgstr "" +msgstr "créer" #: builtin/remote.c:1054 msgid "delete" -msgstr "" +msgstr "supprimer" #: builtin/remote.c:1058 msgid "up to date" -msgstr "" +msgstr "à jour" #: builtin/remote.c:1061 msgid "fast-forwardable" -msgstr "" +msgstr "peut être mis à jour en avance rapide" #: builtin/remote.c:1064 msgid "local out of date" -msgstr "" +msgstr "le local n'est pas à jour" #: builtin/remote.c:1071 #, c-format msgid " %-*s forces to %-*s (%s)" -msgstr "" +msgstr " %-*s force vers %-*s (%s)" #: builtin/remote.c:1074 #, c-format msgid " %-*s pushes to %-*s (%s)" -msgstr "" +msgstr " %-*s pousse vers %-*s (%s)" #: builtin/remote.c:1078 #, c-format msgid " %-*s forces to %s" -msgstr "" +msgstr " %-*s force vers %s" #: builtin/remote.c:1081 #, c-format msgid " %-*s pushes to %s" -msgstr "" +msgstr " %-*s pousse vers %s" #: builtin/remote.c:1091 msgid "do not query remotes" -msgstr "" +msgstr "ne pas interroger les distantes" #: builtin/remote.c:1118 #, c-format msgid "* remote %s" -msgstr "" +msgstr "* distante %s" #: builtin/remote.c:1119 #, c-format msgid " Fetch URL: %s" -msgstr "" +msgstr " URL de rapatriement : %s" #: builtin/remote.c:1120 builtin/remote.c:1285 msgid "(no URL)" -msgstr "" +msgstr "(pas d'URL)" #: builtin/remote.c:1129 builtin/remote.c:1131 #, c-format msgid " Push URL: %s" -msgstr "" +msgstr " URL push : %s" #: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137 #, c-format msgid " HEAD branch: %s" -msgstr "" +msgstr " Branche HEAD : %s" #: builtin/remote.c:1139 #, c-format msgid "" " HEAD branch (remote HEAD is ambiguous, may be one of the following):\n" -msgstr "" +msgstr " Branche HEAD (la HEAD distante est ambiguë, peut être l'une des suivantes) :\n" #: builtin/remote.c:1151 #, c-format msgid " Remote branch:%s" msgid_plural " Remote branches:%s" -msgstr[0] "" -msgstr[1] "" +msgstr[0] " Branche distante :%s" +msgstr[1] " Branches distantes :%s" #: builtin/remote.c:1154 builtin/remote.c:1181 msgid " (status not queried)" -msgstr "" +msgstr " (statut non demandé)" #: builtin/remote.c:1163 msgid " Local branch configured for 'git pull':" msgid_plural " Local branches configured for 'git pull':" -msgstr[0] "" -msgstr[1] "" +msgstr[0] " Branche locale configurée pour 'git pull' :" +msgstr[1] " Branches locales configurées pour 'git pull' :" #: builtin/remote.c:1171 msgid " Local refs will be mirrored by 'git push'" -msgstr "" +msgstr " Les références locales seront reflétées par 'git push'" #: builtin/remote.c:1178 #, c-format msgid " Local ref configured for 'git push'%s:" msgid_plural " Local refs configured for 'git push'%s:" -msgstr[0] "" -msgstr[1] "" +msgstr[0] " Référence locale configurée pour 'git push'%s :" +msgstr[1] " Références locales configurées pour 'git push'%s :" #: builtin/remote.c:1199 msgid "set refs/remotes/<name>/HEAD according to remote" -msgstr "" +msgstr "définir refs/remotes/<nom>/HEAD selon la distante" #: builtin/remote.c:1201 msgid "delete refs/remotes/<name>/HEAD" -msgstr "" +msgstr "supprimer refs/remotes/<nom>/HEAD" #: builtin/remote.c:1216 msgid "Cannot determine remote HEAD" -msgstr "" +msgstr "Impossible de déterminer la HEAD distante" #: builtin/remote.c:1218 msgid "Multiple remote HEAD branches. Please choose one explicitly with:" -msgstr "" +msgstr "Il y a de multiples branches HEAD distantes. Merci d'en choisir une explicitement avec :" #: builtin/remote.c:1228 #, c-format msgid "Could not delete %s" -msgstr "" +msgstr "Impossible de supprimer %s" #: builtin/remote.c:1236 #, c-format msgid "Not a valid ref: %s" -msgstr "" +msgstr "Référence non valide : %s" #: builtin/remote.c:1238 #, c-format msgid "Could not setup %s" -msgstr "" +msgstr "Impossible de paramétrer %s" #: builtin/remote.c:1274 #, c-format msgid " %s will become dangling!" -msgstr "" +msgstr " %s deviendra en suspens !" #: builtin/remote.c:1275 #, c-format msgid " %s has become dangling!" -msgstr "" +msgstr " %s est devenu en suspens !" #: builtin/remote.c:1281 #, c-format msgid "Pruning %s" -msgstr "" +msgstr "Élagage de %s" #: builtin/remote.c:1282 #, c-format msgid "URL: %s" -msgstr "" +msgstr "URL : %s" #: builtin/remote.c:1295 #, c-format msgid " * [would prune] %s" -msgstr "" +msgstr " * [serait élagué] %s" #: builtin/remote.c:1298 #, c-format msgid " * [pruned] %s" -msgstr "" +msgstr " * [élagué] %s" #: builtin/remote.c:1321 msgid "prune remotes after fetching" -msgstr "" +msgstr "élaguer les distants après le rapatriement" #: builtin/remote.c:1387 builtin/remote.c:1461 #, c-format msgid "No such remote '%s'" -msgstr "" +msgstr "Pas de serveur remote '%s'" #: builtin/remote.c:1407 msgid "add branch" -msgstr "" +msgstr "ajouter une branche" #: builtin/remote.c:1414 msgid "no remote specified" -msgstr "" +msgstr "pas de serveur distant spécifié" #: builtin/remote.c:1436 msgid "manipulate push URLs" -msgstr "" +msgstr "manipuler les URLs push" #: builtin/remote.c:1438 msgid "add URL" -msgstr "" +msgstr "ajouter une URL" #: builtin/remote.c:1440 msgid "delete URLs" -msgstr "" +msgstr "supprimer des URLs" #: builtin/remote.c:1447 msgid "--add --delete doesn't make sense" -msgstr "" +msgstr "--add --delete n'a aucun sens" #: builtin/remote.c:1487 #, c-format msgid "Invalid old URL pattern: %s" -msgstr "" +msgstr "Motif d'URL ancien invalide : %s" #: builtin/remote.c:1495 #, c-format msgid "No such URL found: %s" -msgstr "" +msgstr "Pas d'URL trouvée : %s" #: builtin/remote.c:1497 msgid "Will not delete all non-push URLs" @@ -7854,173 +7932,173 @@ msgstr "" #: builtin/remote.c:1569 msgid "be verbose; must be placed before a subcommand" -msgstr "" +msgstr "être verbeux : doit être placé avant une sous-commande" #: builtin/replace.c:17 msgid "git replace [-f] <object> <replacement>" -msgstr "" +msgstr "git replace [-f] <objet> <remplacement>" #: builtin/replace.c:18 msgid "git replace -d <object>..." -msgstr "" +msgstr "git replace -d <objet>..." #: builtin/replace.c:19 msgid "git replace -l [<pattern>]" -msgstr "" +msgstr "git replace -l [<motif>]" #: builtin/replace.c:121 msgid "list replace refs" -msgstr "" +msgstr "afficher les références de remplacement" #: builtin/replace.c:122 msgid "delete replace refs" -msgstr "" +msgstr "supprimer les références de remplacement" #: builtin/replace.c:123 msgid "replace the ref if it exists" -msgstr "" +msgstr "remplacer la référence si elle existe" #: builtin/rerere.c:11 msgid "git rerere [clear | forget path... | status | remaining | diff | gc]" -msgstr "" +msgstr "git rerere [clear | forget chemin... | status | remaining | diff | gc]" #: builtin/rerere.c:56 msgid "register clean resolutions in index" -msgstr "" +msgstr "enregistrer des résolutions propres dans l'index" #: builtin/reset.c:25 msgid "" "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]" -msgstr "" +msgstr "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]" #: builtin/reset.c:26 msgid "git reset [-q] <tree-ish> [--] <paths>..." -msgstr "" +msgstr "git reset [-q] <arbre> [--] <chemins>..." #: builtin/reset.c:27 msgid "git reset --patch [<tree-ish>] [--] [<paths>...]" -msgstr "" +msgstr "git reset --patch [<arbre>] [--] [<chemins>...]" #: builtin/reset.c:33 msgid "mixed" -msgstr "" +msgstr "mixed" #: builtin/reset.c:33 msgid "soft" -msgstr "" +msgstr "soft" #: builtin/reset.c:33 msgid "hard" -msgstr "" +msgstr "hard" #: builtin/reset.c:33 msgid "merge" -msgstr "" +msgstr "merge" #: builtin/reset.c:33 msgid "keep" -msgstr "" +msgstr "keep" #: builtin/reset.c:73 msgid "You do not have a valid HEAD." -msgstr "" +msgstr "Vous n'avez pas une HEAD valide." #: builtin/reset.c:75 msgid "Failed to find tree of HEAD." -msgstr "" +msgstr "Impossible de trouver l'arbre pour HEAD." #: builtin/reset.c:81 #, c-format msgid "Failed to find tree of %s." -msgstr "" +msgstr "Impossible de trouver l'arbre pour %s." #: builtin/reset.c:98 #, c-format msgid "HEAD is now at %s" -msgstr "" +msgstr "HEAD est maintenant à %s" #: builtin/reset.c:169 #, c-format msgid "Cannot do a %s reset in the middle of a merge." -msgstr "" +msgstr "Impossible de faire un \"reset %s\" au milieu d'une fusion." #: builtin/reset.c:248 msgid "be quiet, only report errors" -msgstr "" +msgstr "être silencieux, afficher seulement les erreurs" #: builtin/reset.c:250 msgid "reset HEAD and index" -msgstr "" +msgstr "réinitialiser HEAD et l'index" #: builtin/reset.c:251 msgid "reset only HEAD" -msgstr "" +msgstr "réinitialiser seulement HEAD" #: builtin/reset.c:253 builtin/reset.c:255 msgid "reset HEAD, index and working tree" -msgstr "" +msgstr "réinitialiser HEAD, l'index et la copie de travail" #: builtin/reset.c:257 msgid "reset HEAD but keep local changes" -msgstr "" +msgstr "réinitialiser HEAD mais garder les changements locaux" #: builtin/reset.c:275 #, c-format msgid "Failed to resolve '%s' as a valid revision." -msgstr "" +msgstr "Échec de résolution de '%s' comme une révision valide." #: builtin/reset.c:278 builtin/reset.c:286 #, c-format msgid "Could not parse object '%s'." -msgstr "" +msgstr "Impossible d'analyser l'objet '%s'." #: builtin/reset.c:283 #, c-format msgid "Failed to resolve '%s' as a valid tree." -msgstr "" +msgstr "Échec de résolution de '%s' comme un arbre valide." #: builtin/reset.c:292 msgid "--patch is incompatible with --{hard,mixed,soft}" -msgstr "" +msgstr "--patch est incompatible avec --{hard,mixed,soft}" #: builtin/reset.c:301 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead." -msgstr "" +msgstr "--mixed avec des chemins est obsolète ; utilisez 'git reset -- <paths>' à la place." #: builtin/reset.c:303 #, c-format msgid "Cannot do %s reset with paths." -msgstr "" +msgstr "Impossible de faire un \"%s reset\" avec des chemins." #: builtin/reset.c:313 #, c-format msgid "%s reset is not allowed in a bare repository" -msgstr "RAZ de %s n'est pas permis dans un dépôt nu" +msgstr "Le \"%s reset\" n'est pas permis dans un dépôt nu" #: builtin/reset.c:333 #, c-format msgid "Could not reset index file to revision '%s'." -msgstr "" +msgstr "Impossible de réinitialiser le fichier d'index à la révision '%s'." #: builtin/reset.c:339 msgid "Unstaged changes after reset:" -msgstr "" +msgstr "Modifications non indexées après reset :" #: builtin/reset.c:344 msgid "Could not write new index file." -msgstr "" +msgstr "Impossible d'écrire le nouveau fichier d'index." #: builtin/rev-parse.c:339 msgid "git rev-parse --parseopt [options] -- [<args>...]" -msgstr "" +msgstr "git rev-parse --parseopt [options] -- [<arguments>...]" #: builtin/rev-parse.c:344 msgid "keep the `--` passed as an arg" -msgstr "" +msgstr "garder le `--` passé en argument" #: builtin/rev-parse.c:346 msgid "stop parsing after the first non-option argument" -msgstr "" +msgstr "arrêt de l'analyse après le premier argument qui n'est pas une option" #: builtin/rev-parse.c:464 msgid "" @@ -8030,22 +8108,27 @@ msgid "" "\n" "Run \"git rev-parse --parseopt -h\" for more information on the first usage." msgstr "" +"git rev-parse --parseopt [options] -- [<arguments>...]\n" +" ou : git rev-parse --sq-quote [<argument>...]\n" +" ou : git rev-parse [options] [<argument>...]\n" +"\n" +"Lancez \"git rev-parse --parseopt -h\" pour plus d'information sur l'utilisation principale." #: builtin/revert.c:22 msgid "git revert [options] <commit-ish>..." -msgstr "" +msgstr "git revert [options] <commit>..." #: builtin/revert.c:23 msgid "git revert <subcommand>" -msgstr "" +msgstr "git revert <sous-commande>" #: builtin/revert.c:28 msgid "git cherry-pick [options] <commit-ish>..." -msgstr "" +msgstr "git cherry-pick [options] <commit>..." #: builtin/revert.c:29 msgid "git cherry-pick <subcommand>" -msgstr "" +msgstr "git cherry-pick <sous-commande>" #: builtin/revert.c:70 builtin/revert.c:92 #, c-format @@ -8054,75 +8137,75 @@ msgstr "%s : %s ne peut pas être utilisé avec %s" #: builtin/revert.c:103 msgid "end revert or cherry-pick sequence" -msgstr "" +msgstr "mettre fin au retour ou picorage" #: builtin/revert.c:104 msgid "resume revert or cherry-pick sequence" -msgstr "" +msgstr "reprendre le retour ou picorage" #: builtin/revert.c:105 msgid "cancel revert or cherry-pick sequence" -msgstr "" +msgstr "annuler le retour ou picorage" #: builtin/revert.c:106 msgid "don't automatically commit" -msgstr "" +msgstr "ne pas valider automatiquement" #: builtin/revert.c:107 msgid "edit the commit message" -msgstr "" +msgstr "éditer le message de validation" #: builtin/revert.c:110 msgid "parent number" -msgstr "" +msgstr "numéro de parent" #: builtin/revert.c:112 msgid "merge strategy" -msgstr "" +msgstr "stratégie de fusion" #: builtin/revert.c:113 msgid "option" -msgstr "" +msgstr "option" #: builtin/revert.c:114 msgid "option for merge strategy" -msgstr "" +msgstr "option pour la stratégie de fusion" #: builtin/revert.c:125 msgid "append commit name" -msgstr "" +msgstr "ajouter le nom de validation" #: builtin/revert.c:126 msgid "allow fast-forward" -msgstr "" +msgstr "autoriser l'avance rapide" #: builtin/revert.c:127 msgid "preserve initially empty commits" -msgstr "" +msgstr "préserver les validations vides initialement" #: builtin/revert.c:128 msgid "allow commits with empty messages" -msgstr "" +msgstr "autoriser les validations avec des messages vides" #: builtin/revert.c:129 msgid "keep redundant, empty commits" -msgstr "" +msgstr "garder les validations redondantes, vides" #: builtin/revert.c:133 msgid "program error" -msgstr "" +msgstr "erreur du programme" #: builtin/revert.c:223 msgid "revert failed" -msgstr "" +msgstr "revert a échoué" #: builtin/revert.c:238 msgid "cherry-pick failed" -msgstr "" +msgstr "le picorage a échoué" #: builtin/rm.c:15 msgid "git rm [options] [--] <file>..." -msgstr "" +msgstr "git rm [options] [--] <fichier>..." #: builtin/rm.c:64 builtin/rm.c:186 #, c-format @@ -8130,6 +8213,8 @@ msgid "" "submodule '%s' (or one of its nested submodules) uses a .git directory\n" "(use 'rm -rf' if you really want to remove it including all of its history)" msgstr "" +"le sous-module '%s' (ou un des sous-modules imbriqués) utilise un répertoire .git\n" +"(utilisez 'rm -rf' si vous souhaitez vraiment le supprimer avec tout son historique)" #: builtin/rm.c:174 #, c-format @@ -8137,6 +8222,8 @@ msgid "" "'%s' has staged content different from both the file and the HEAD\n" "(use -f to force removal)" msgstr "" +"'%s' a du contenu indexé différent du fichier et de HEAD\n" +"(utilisez -f pour forcer la suppression)" #: builtin/rm.c:180 #, c-format @@ -8144,6 +8231,8 @@ msgid "" "'%s' has changes staged in the index\n" "(use --cached to keep the file, or -f to force removal)" msgstr "" +"'%s' a des changements dans l'index\n" +"(utilisez --cached pour garder le fichier, ou -f pour forcer la suppression)" #: builtin/rm.c:191 #, c-format @@ -8151,65 +8240,67 @@ msgid "" "'%s' has local modifications\n" "(use --cached to keep the file, or -f to force removal)" msgstr "" +"'%s' a des modifications locales\n" +"(utilisez --cached pour garder le fichier, ou -f pour forcer la suppression)" #: builtin/rm.c:207 msgid "do not list removed files" -msgstr "" +msgstr "ne pas afficher les fichiers supprimés" #: builtin/rm.c:208 msgid "only remove from the index" -msgstr "" +msgstr "supprimer seulement de l'index" #: builtin/rm.c:209 msgid "override the up-to-date check" -msgstr "" +msgstr "outrepasser la vérification des fichiers à jour" #: builtin/rm.c:210 msgid "allow recursive removal" -msgstr "" +msgstr "autoriser la suppression récursive" #: builtin/rm.c:212 msgid "exit with a zero status even if nothing matched" -msgstr "" +msgstr "sortir avec un statut zéro même si rien ne correspondait" #: builtin/rm.c:283 #, c-format msgid "not removing '%s' recursively without -r" -msgstr "" +msgstr "pas de suppression récursive de '%s' sans -r" #: builtin/rm.c:322 #, c-format msgid "git rm: unable to remove %s" -msgstr "" +msgstr "git rm : impossible de supprimer %s" #: builtin/shortlog.c:13 msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]" -msgstr "" +msgstr "git shortlog [<options>] [<intervalle révisions>] [[--] [<chemin>...]]" #: builtin/shortlog.c:131 #, c-format msgid "Missing author: %s" -msgstr "" +msgstr "Auteur manquant : %s" #: builtin/shortlog.c:227 msgid "sort output according to the number of commits per author" -msgstr "" +msgstr "trier la sortie sur le nombre de validations par auteur" #: builtin/shortlog.c:229 msgid "Suppress commit descriptions, only provides commit count" -msgstr "" +msgstr "Supprimer les descriptions de validation, fournit seulement le nombre de validations" #: builtin/shortlog.c:231 msgid "Show the email address of each author" -msgstr "" +msgstr "Afficher l'adresse e-mail de chaque auteur" #: builtin/shortlog.c:232 msgid "w[,i1[,i2]]" -msgstr "" +msgstr "w[,i1[,i2]]" #: builtin/shortlog.c:233 msgid "Linewrap output" -msgstr "" +msgstr "Couper les lignes" #: builtin/show-branch.c:9 msgid "" @@ -8217,31 +8308,31 @@ msgid "" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" -msgstr "" +msgstr "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<quand>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<révision> | <glob>)...]" #: builtin/show-branch.c:10 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]" -msgstr "" +msgstr "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<référence>]" #: builtin/show-branch.c:650 msgid "show remote-tracking and local branches" -msgstr "" +msgstr "afficher les branches de suivi distantes et les branches locales" #: builtin/show-branch.c:652 msgid "show remote-tracking branches" -msgstr "" +msgstr "afficher les branches de suivi distantes" #: builtin/show-branch.c:654 msgid "color '*!+-' corresponding to the branch" -msgstr "" +msgstr "couleur '*!+-' correspondant à la branche" #: builtin/show-branch.c:656 msgid "show <n> more commits after the common ancestor" -msgstr "" +msgstr "afficher <n> validations de plus après l'ancêtre commun" #: builtin/show-branch.c:658 msgid "synonym to more=-1" -msgstr "" +msgstr "synonyme de more=-1" #: builtin/show-branch.c:659 msgid "suppress naming strings" @@ -8249,157 +8340,159 @@ msgstr "" #: builtin/show-branch.c:661 msgid "include the current branch" -msgstr "" +msgstr "inclure la branche courante" #: builtin/show-branch.c:663 msgid "name commits with their object names" -msgstr "" +msgstr "nommer les validations avec leurs noms d'objet" #: builtin/show-branch.c:665 msgid "show possible merge bases" -msgstr "" +msgstr "afficher les bases possibles de fusion" #: builtin/show-branch.c:667 msgid "show refs unreachable from any other ref" -msgstr "" +msgstr "afficher les références non accessibles depuis toute autre référence" #: builtin/show-branch.c:669 msgid "show commits in topological order" -msgstr "" +msgstr "afficher les validations dans l'ordre topologique" #: builtin/show-branch.c:671 msgid "show only commits not on the first branch" -msgstr "" +msgstr "afficher seulement les validations qui ne sont pas sur la première branche" #: builtin/show-branch.c:673 msgid "show merges reachable from only one tip" -msgstr "" +msgstr "afficher les fusions accessibles depuis une seule pointe" #: builtin/show-branch.c:675 msgid "show commits where no parent comes before its children" -msgstr "" +msgstr "afficher les validations où aucun parent ne vient avant ses enfants" #: builtin/show-branch.c:677 msgid "<n>[,<base>]" -msgstr "" +msgstr "<n>[,<base>]" #: builtin/show-branch.c:678 msgid "show <n> most recent ref-log entries starting at base" -msgstr "" +msgstr "afficher les <n> plus récentes entrées de ref-log en commençant à la base" #: builtin/show-ref.c:10 msgid "" "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--" "hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] " -msgstr "" +msgstr "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [motif*] " #: builtin/show-ref.c:11 msgid "git show-ref --exclude-existing[=pattern] < ref-list" -msgstr "" +msgstr "git show-ref --exclude-existing[=motif] < liste-références" #: builtin/show-ref.c:165 msgid "only show tags (can be combined with heads)" -msgstr "" +msgstr "afficher seulement les étiquettes (peut être combiné avec des têtes)" #: builtin/show-ref.c:166 msgid "only show heads (can be combined with tags)" -msgstr "" +msgstr "afficher seulement les têtes (peut être combiné avec des étiquettes)" #: builtin/show-ref.c:167 msgid "stricter reference checking, requires exact ref path" -msgstr "" +msgstr "vérification de référence plus stricte, requiert un chemin de référence exact" #: builtin/show-ref.c:170 builtin/show-ref.c:172 msgid "show the HEAD reference" -msgstr "" +msgstr "afficher la référence HEAD" #: builtin/show-ref.c:174 msgid "dereference tags into object IDs" -msgstr "" +msgstr "déréférencer les étiquettes en IDs d'objet" #: builtin/show-ref.c:176 msgid "only show SHA1 hash using <n> digits" -msgstr "" +msgstr "afficher seulement le hachage SHA1 en utilisant <n> chiffres" #: builtin/show-ref.c:180 msgid "do not print results to stdout (useful with --verify)" -msgstr "" +msgstr "ne pas afficher les résultats sur la sortie standard (pratique avec --verify)" #: builtin/show-ref.c:182 msgid "show refs from stdin that aren't in local repository" -msgstr "" +msgstr "afficher les références de l'entrée standard qui ne sont pas dans le dépôt local" #: builtin/symbolic-ref.c:7 msgid "git symbolic-ref [options] name [ref]" -msgstr "" +msgstr "git symbolic-ref [options] nom [référence]" #: builtin/symbolic-ref.c:8 msgid "git symbolic-ref -d [-q] name" -msgstr "" +msgstr "git symbolic-ref -d [-q] nom" #: builtin/symbolic-ref.c:40 msgid "suppress error message for non-symbolic (detached) refs" -msgstr "" +msgstr "supprimer le message d'erreur pour une référence non symbolique (détachée)" #: builtin/symbolic-ref.c:41 msgid "delete symbolic ref" -msgstr "" +msgstr "supprimer la référence symbolique" #: builtin/symbolic-ref.c:42 msgid "shorten ref output" -msgstr "" +msgstr "raccourcir l'affichage de la référence" #: builtin/symbolic-ref.c:43 builtin/update-ref.c:18 msgid "reason" -msgstr "" +msgstr "raison" #: builtin/symbolic-ref.c:43 builtin/update-ref.c:18 msgid "reason of the update" -msgstr "" +msgstr "raison de la mise à jour" #: builtin/tag.c:22 msgid "" "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]" -msgstr "" +msgstr "git tag [-a|-s|-u <id-clé>] [-f] [-m <msg>|-F <file>] <nométiquette> [<head>]" #: builtin/tag.c:23 msgid "git tag -d <tagname>..." -msgstr "" +msgstr "git tag -d <nométiquette>..." #: builtin/tag.c:24 msgid "" "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] \n" "\t\t[<pattern>...]" msgstr "" +"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <objet>] \n" +"\t\t[<motif>...]" #: builtin/tag.c:26 msgid "git tag -v <tagname>..." -msgstr "" +msgstr "git tag -v <nométiquette>..." #: builtin/tag.c:60 #, c-format msgid "malformed object at '%s'" -msgstr "" +msgstr "objet malformé à '%s'" #: builtin/tag.c:207 #, c-format msgid "tag name too long: %.*s..." -msgstr "" +msgstr "nom d'étiquette trop long : %.*s..." #: builtin/tag.c:212 #, c-format msgid "tag '%s' not found." -msgstr "" +msgstr "étiquette '%s' non trouvée." #: builtin/tag.c:227 #, c-format msgid "Deleted tag '%s' (was %s)\n" -msgstr "" +msgstr "Étiquette '%s' supprimée (elle était %s)\n" #: builtin/tag.c:239 #, c-format msgid "could not verify the tag '%s'" -msgstr "" +msgstr "impossible de vérifier l'étiquette '%s'" #: builtin/tag.c:249 #, c-format @@ -8408,6 +8501,9 @@ msgid "" "Write a tag message\n" "Lines starting with '%c' will be ignored.\n" msgstr "" +"\n" +"Écrire un message pour l'étiquette\n" +"Les lignes commençant par '%c' seront ignorées.\n" #: builtin/tag.c:253 #, c-format @@ -8417,130 +8513,133 @@ msgid "" "Lines starting with '%c' will be kept; you may remove them yourself if you " "want to.\n" msgstr "" +"\n" +"Écrire un message pour l'étiquette\n" +"Les lignes commençant par '%c' seront gardées ; vous pouvez les retirer vous-même si vous le souhaitez.\n" #: builtin/tag.c:292 msgid "unable to sign the tag" -msgstr "" +msgstr "impossible de signer l'étiquette" #: builtin/tag.c:294 msgid "unable to write tag file" -msgstr "" +msgstr "impossible d'écrire le fichier d'étiquettes" #: builtin/tag.c:319 msgid "bad object type." -msgstr "" +msgstr "mauvais type d'objet." #: builtin/tag.c:332 msgid "tag header too big." -msgstr "" +msgstr "en-tête d'étiquette trop gros." #: builtin/tag.c:368 msgid "no tag message?" -msgstr "" +msgstr "pas de message pour l'étiquette ?" #: builtin/tag.c:374 #, c-format msgid "The tag message has been left in %s\n" -msgstr "" +msgstr "Le message pour l'étiquette a été laissé dans %s\n" #: builtin/tag.c:423 msgid "switch 'points-at' requires an object" -msgstr "" +msgstr "le commutateur 'points-at' requiert un objet" #: builtin/tag.c:425 #, c-format msgid "malformed object name '%s'" -msgstr "" +msgstr "nom d'objet malformé '%s'" #: builtin/tag.c:445 msgid "list tag names" -msgstr "" +msgstr "afficher les noms des étiquettes" #: builtin/tag.c:447 msgid "print <n> lines of each tag message" -msgstr "" +msgstr "affiche <n> lignes de chaque message d'étiquette" #: builtin/tag.c:449 msgid "delete tags" -msgstr "" +msgstr "supprimer des étiquettes" #: builtin/tag.c:450 msgid "verify tags" -msgstr "" +msgstr "vérifier des étiquettes" #: builtin/tag.c:452 msgid "Tag creation options" -msgstr "" +msgstr "Options de création de l'étiquette" #: builtin/tag.c:454 msgid "annotated tag, needs a message" -msgstr "" +msgstr "étiquette annotée, nécessite un message" #: builtin/tag.c:456 msgid "tag message" -msgstr "" +msgstr "message pour l'étiquette" #: builtin/tag.c:458 msgid "annotated and GPG-signed tag" -msgstr "" +msgstr "étiquette annotée et signée avec GPG" #: builtin/tag.c:462 msgid "use another key to sign the tag" -msgstr "" +msgstr "utiliser une autre clé pour signer l'étiquette" #: builtin/tag.c:463 msgid "replace the tag if exists" -msgstr "" +msgstr "remplacer l'étiquette si elle existe" #: builtin/tag.c:464 msgid "show tag list in columns" -msgstr "" +msgstr "afficher la liste des étiquettes sous forme de colonnes" #: builtin/tag.c:466 msgid "Tag listing options" -msgstr "" +msgstr "Options d'affichage des étiquettes" #: builtin/tag.c:469 msgid "print only tags that contain the commit" -msgstr "" +msgstr "afficher seulement les étiquettes qui contiennent la validation" #: builtin/tag.c:475 msgid "print only tags of the object" -msgstr "" +msgstr "afficher seulement les étiquettes de l'objet" #: builtin/tag.c:504 msgid "--column and -n are incompatible" -msgstr "" +msgstr "--column et -n sont incompatibles" #: builtin/tag.c:521 msgid "-n option is only allowed with -l." -msgstr "" +msgstr "l'option -n est autorisée seulement avec -l." #: builtin/tag.c:523 msgid "--contains option is only allowed with -l." -msgstr "" +msgstr "l'option --contains est autorisée seulement avec -l." #: builtin/tag.c:525 msgid "--points-at option is only allowed with -l." -msgstr "" +msgstr "l'option --points-at est autorisée seulement avec -l." #: builtin/tag.c:533 msgid "only one -F or -m option is allowed." -msgstr "" +msgstr "une seule option -F ou -m est autorisée." #: builtin/tag.c:553 msgid "too many params" -msgstr "" +msgstr "trop de paramètres" #: builtin/tag.c:559 #, c-format msgid "'%s' is not a valid tag name." -msgstr "" +msgstr "'%s' n'est pas un nom d'étiquette valide." #: builtin/tag.c:564 #, c-format msgid "tag '%s' already exists" -msgstr "" +msgstr "l'étiquette '%s' existe déjà " #: builtin/tag.c:582 #, c-format @@ -8555,63 +8654,63 @@ msgstr "%s : impossible de mettre à jour la référence" #: builtin/tag.c:586 #, c-format msgid "Updated tag '%s' (was %s)\n" -msgstr "" +msgstr "Étiquette '%s' mise à jour (elle était %s)\n" #: builtin/update-index.c:401 msgid "git update-index [options] [--] [<file>...]" -msgstr "" +msgstr "git update-index [options] [--] [<fichier>...]" #: builtin/update-index.c:718 msgid "continue refresh even when index needs update" -msgstr "" +msgstr "continuer de rafraîchir même si l'index a besoin d'une mise à jour" #: builtin/update-index.c:721 msgid "refresh: ignore submodules" -msgstr "" +msgstr "rafraîchir : ignorer les sous-modules" #: builtin/update-index.c:724 msgid "do not ignore new files" -msgstr "" +msgstr "ne pas ignorer les nouveaux fichiers" #: builtin/update-index.c:726 msgid "let files replace directories and vice-versa" -msgstr "" +msgstr "laisser les fichiers remplacer des répertoires et vice-versa" #: builtin/update-index.c:728 msgid "notice files missing from worktree" -msgstr "" +msgstr "aviser des fichiers manquants dans la copie de travail" #: builtin/update-index.c:730 msgid "refresh even if index contains unmerged entries" -msgstr "" +msgstr "rafraîchir même si l'index contient des entrées non fusionnées" #: builtin/update-index.c:733 msgid "refresh stat information" -msgstr "" +msgstr "rafraîchir l'information de stat" #: builtin/update-index.c:737 msgid "like --refresh, but ignore assume-unchanged setting" -msgstr "" +msgstr "comme --refresh, mais en ignorant l'option assume-unchanged" #: builtin/update-index.c:741 msgid "<mode> <object> <path>" -msgstr "" +msgstr "<mode> <objet> <chemin>" #: builtin/update-index.c:742 msgid "add the specified entry to the index" -msgstr "" +msgstr "ajouter l'entrée spécifiée dans l'index" #: builtin/update-index.c:746 msgid "(+/-)x" -msgstr "" +msgstr "(+/-)x" #: builtin/update-index.c:747 msgid "override the executable bit of the listed files" -msgstr "" +msgstr "outrepasser le bit exécutable pour les fichiers listés" #: builtin/update-index.c:751 msgid "mark files as \"not changing\"" -msgstr "" +msgstr "marquer les fichiers comme \"non changeants\"" #: builtin/update-index.c:754 msgid "clear assumed-unchanged bit" @@ -8619,7 +8718,7 @@ msgstr "" #: builtin/update-index.c:757 msgid "mark files as \"index-only\"" -msgstr "" +msgstr "marquer les fichiers comme \"index seulement\"" #: builtin/update-index.c:760 msgid "clear skip-worktree bit" @@ -8627,59 +8726,59 @@ msgstr "" #: builtin/update-index.c:763 msgid "add to index only; do not add content to object database" -msgstr "" +msgstr "ajouter seulement à l'index ; ne pas ajouter le contenu dans la base de données des objets" #: builtin/update-index.c:765 msgid "remove named paths even if present in worktree" -msgstr "" +msgstr "supprimer les chemins nommés même s'ils sont présents dans la copie de travail" #: builtin/update-index.c:767 msgid "with --stdin: input lines are terminated by null bytes" -msgstr "" +msgstr "avec --stdin : les lignes en entrée sont terminées par des octets nuls" #: builtin/update-index.c:769 msgid "read list of paths to be updated from standard input" -msgstr "" +msgstr "lire la liste des chemins à mettre à jour depuis l'entrée standard" #: builtin/update-index.c:773 msgid "add entries from standard input to the index" -msgstr "" +msgstr "ajouter les entrées depuis l'entrée standard à l'index" #: builtin/update-index.c:777 msgid "repopulate stages #2 and #3 for the listed paths" -msgstr "" +msgstr "repeupler les étapes n°2 et n°3 pour les chemins listés" #: builtin/update-index.c:781 msgid "only update entries that differ from HEAD" -msgstr "" +msgstr "mettre à jour seulement les entrées qui diffèrent de HEAD" #: builtin/update-index.c:785 msgid "ignore files missing from worktree" -msgstr "" +msgstr "ignorer les fichiers manquants dans la copie de travail" #: builtin/update-index.c:788 msgid "report actions to standard output" -msgstr "" +msgstr "afficher les actions sur la sortie standard" #: builtin/update-index.c:790 msgid "(for porcelains) forget saved unresolved conflicts" -msgstr "" +msgstr "(pour porcelaines) oublier les conflits sauvés et non résolus" #: builtin/update-index.c:794 msgid "write index in this format" -msgstr "" +msgstr "écrire l'index dans ce format" #: builtin/update-ref.c:7 msgid "git update-ref [options] -d <refname> [<oldval>]" -msgstr "" +msgstr "git update-ref [options] -d <nomréférence> [<anciennevaleur>]" #: builtin/update-ref.c:8 msgid "git update-ref [options] <refname> <newval> [<oldval>]" -msgstr "" +msgstr "git update-ref [options] <nomréférence> <nouvellevaleur> [<anciennevaleur>]" #: builtin/update-ref.c:19 msgid "delete the reference" -msgstr "" +msgstr "supprimer la référence" #: builtin/update-ref.c:21 msgid "update <refname> not the one it points to" @@ -8687,47 +8786,47 @@ msgstr "" #: builtin/update-server-info.c:6 msgid "git update-server-info [--force]" -msgstr "" +msgstr "git update-server-info [--force]" #: builtin/update-server-info.c:14 msgid "update the info files from scratch" -msgstr "" +msgstr "mettre à jour les fichiers d'information à partir de zéro" #: builtin/verify-pack.c:56 msgid "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..." -msgstr "" +msgstr "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..." #: builtin/verify-pack.c:66 msgid "verbose" -msgstr "" +msgstr "verbeux" #: builtin/verify-pack.c:68 msgid "show statistics only" -msgstr "" +msgstr "afficher seulement les statistiques" #: builtin/verify-tag.c:17 msgid "git verify-tag [-v|--verbose] <tag>..." -msgstr "" +msgstr "git verify-tag [-v|--verbose] <étiquette>..." #: builtin/verify-tag.c:73 msgid "print tag contents" -msgstr "" +msgstr "afficher le contenu de l'étiquette" #: builtin/write-tree.c:13 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]" -msgstr "" +msgstr "git write-tree [--missing-ok] [--prefix=<préfixe>/]" #: builtin/write-tree.c:26 msgid "<prefix>/" -msgstr "" +msgstr "<préfixe>/" #: builtin/write-tree.c:27 msgid "write tree object for a subdirectory <prefix>" -msgstr "" +msgstr "écrire l'objet arbre pour un sous-répertoire <préfixe>" #: builtin/write-tree.c:30 msgid "only useful for debugging" -msgstr "" +msgstr "seulement utile pour le débogage" #: git.c:16 msgid "" @@ -8735,116 +8834,121 @@ msgid "" "concept guides. See 'git help <command>' or 'git help <concept>'\n" "to read about a specific subcommand or concept." msgstr "" +"'git help -a' et 'git help -g' listent les sous-commandes disponibles et\n" +"quelques concepts. Voir 'git help <command>' ou 'git help <concept>'\n" +"pour en lire plus à propos d'une commande spécifique ou d'un concept." #: parse-options.h:156 msgid "no-op (backward compatibility)" -msgstr "" +msgstr "sans action (rétrocompatibilité)" #: parse-options.h:232 msgid "be more verbose" -msgstr "" +msgstr "être plus verbeux" #: parse-options.h:234 msgid "be more quiet" -msgstr "" +msgstr "être plus silencieux" #: parse-options.h:240 msgid "use <n> digits to display SHA-1s" -msgstr "" +msgstr "utiliser <n> chiffres pour afficher les SHA-1s" #: common-cmds.h:8 msgid "Add file contents to the index" -msgstr "" +msgstr "Ajouter le contenu du fichier dans l'index" #: common-cmds.h:9 msgid "Find by binary search the change that introduced a bug" -msgstr "" +msgstr "Rechercher de manière binaire le changement qui a introduit un bogue" #: common-cmds.h:10 msgid "List, create, or delete branches" -msgstr "" +msgstr "Lister, créer ou supprimer des branches" #: common-cmds.h:11 msgid "Checkout a branch or paths to the working tree" -msgstr "" +msgstr "Extraire une branche ou des chemins dans la copie de travail" #: common-cmds.h:12 msgid "Clone a repository into a new directory" -msgstr "" +msgstr "Cloner un dépôt dans un nouveau répertoire" #: common-cmds.h:13 msgid "Record changes to the repository" -msgstr "" +msgstr "Enregistrer les changements dans le dépôt" #: common-cmds.h:14 msgid "Show changes between commits, commit and working tree, etc" -msgstr "" +msgstr "Afficher les changements entre les validations, entre validation et copie de travail, etc" #: common-cmds.h:15 msgid "Download objects and refs from another repository" -msgstr "" +msgstr "Télécharger les objets et références depuis un autre dépôt" #: common-cmds.h:16 msgid "Print lines matching a pattern" -msgstr "" +msgstr "Afficher les lignes correspondant à un motif" #: common-cmds.h:17 msgid "Create an empty Git repository or reinitialize an existing one" -msgstr "" +msgstr "Créer un dépôt Git vide ou réinitialiser un existant" #: common-cmds.h:18 msgid "Show commit logs" -msgstr "" +msgstr "Afficher l'historique des validations" #: common-cmds.h:19 msgid "Join two or more development histories together" -msgstr "" +msgstr "Joindre deux ou plusieurs historiques de développement ensemble" #: common-cmds.h:20 msgid "Move or rename a file, a directory, or a symlink" -msgstr "" +msgstr "Déplacer ou renommer un fichier, un répertoire, ou un lien symbolique" #: common-cmds.h:21 msgid "Fetch from and merge with another repository or a local branch" -msgstr "" +msgstr "Rapatrier et fusionner avec un autre dépôt ou une branche locale" #: common-cmds.h:22 msgid "Update remote refs along with associated objects" -msgstr "" +msgstr "Mettre à jour les références distantes ainsi que les objets associés" #: common-cmds.h:23 msgid "Forward-port local commits to the updated upstream head" -msgstr "" +msgstr "Reporter en avant les validations locales dans la tête en amont mise à jour" #: common-cmds.h:24 msgid "Reset current HEAD to the specified state" -msgstr "" +msgstr "Réinitialiser la HEAD courante à l'état spécifié" #: common-cmds.h:25 msgid "Remove files from the working tree and from the index" -msgstr "" +msgstr "Supprimer les fichiers de la copie de travail et de l'index" #: common-cmds.h:26 msgid "Show various types of objects" -msgstr "" +msgstr "Afficher différents types d'objects" #: common-cmds.h:27 msgid "Show the working tree status" -msgstr "" +msgstr "Afficher le statut de la copie de travail" #: common-cmds.h:28 msgid "Create, list, delete or verify a tag object signed with GPG" -msgstr "" +msgstr "Créer, lister, supprimer ou vérifier un objet d'étiquette signé avec GPG" #: git-am.sh:50 msgid "You need to set your committer info first" -msgstr "" +msgstr "Vous devez d'abord définir vos informations de validateur" #: git-am.sh:95 msgid "" "You seem to have moved HEAD since the last 'am' failure.\n" "Not rewinding to ORIG_HEAD" msgstr "" +"Vous semblez avoir déplacé la HEAD depuis le dernier échec de 'am'.\n" +"Pas de retour à ORIG_HEAD" #: git-am.sh:105 #, sh-format @@ -8853,69 +8957,76 @@ msgid "" "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n" "To restore the original branch and stop patching, run \"$cmdline --abort\"." msgstr "" +"Lorsque vous aurez résolu ce problème, lancez \"$cmdline --resolved\".\n" +"Si vous préférez sauter ce patch, lancez \"$cmdline --skip\" à la place.\n" +"Pour restaurer la branche d'origine et stopper le patchage, lancez \"$cmdline --abort\"." #: git-am.sh:121 msgid "Cannot fall back to three-way merge." -msgstr "" +msgstr "Impossible de retourner à une fusion 3-way." #: git-am.sh:137 msgid "Repository lacks necessary blobs to fall back on 3-way merge." -msgstr "" +msgstr "Le dépôt n'a pas les blobs nécessaires pour un retour à une fusion 3-way." #: git-am.sh:139 msgid "Using index info to reconstruct a base tree..." -msgstr "" +msgstr "Utilisation de l'information de l'index pour reconstruire un arbre de base..." #: git-am.sh:154 msgid "" "Did you hand edit your patch?\n" "It does not apply to blobs recorded in its index." msgstr "" +"Avez-vous édité le patch à la main ?\n" +"Il ne s'applique pas aux blobs enregistrés dans son index." #: git-am.sh:163 msgid "Falling back to patching base and 3-way merge..." -msgstr "" +msgstr "Retour à un patch de la base et fusion 3-way..." #: git-am.sh:179 msgid "Failed to merge in the changes." -msgstr "" +msgstr "Échec de fusion dans les changements." #: git-am.sh:274 msgid "Only one StGIT patch series can be applied at once" -msgstr "" +msgstr "Seulement une série de patches StGIT peut être appliquée à la fois" #: git-am.sh:361 #, sh-format msgid "Patch format $patch_format is not supported." -msgstr "" +msgstr "Le format de patch $patch_format n'est pas supporté." #: git-am.sh:363 msgid "Patch format detection failed." -msgstr "" +msgstr "Échec de détection du format du patch." #: git-am.sh:389 msgid "" "The -b/--binary option has been a no-op for long time, and\n" "it will be removed. Please do not use it anymore." msgstr "" +"L'option -b/--binary ne fait plus rien depuis longtemps,\n" +"et elle sera supprimée. Merci de ne plus l'utiliser." #: git-am.sh:477 #, sh-format msgid "previous rebase directory $dotest still exists but mbox given." -msgstr "" +msgstr "le répertoire précédent de rebasage $dotest existe toujours mais mbox donnée." #: git-am.sh:482 msgid "Please make up your mind. --skip or --abort?" -msgstr "" +msgstr "Décidez-vous. --skip ou --abort ?" #: git-am.sh:509 msgid "Resolve operation not in progress, we are not resuming." -msgstr "" +msgstr "Pas de résolution de l'opération en cours, nous ne sommes pas dans une reprise." #: git-am.sh:575 #, sh-format msgid "Dirty index: cannot apply patches (dirty: $files)" -msgstr "" +msgstr "Index sale : impossible d'appliquer des patches (sales : $files)" #: git-am.sh:679 #, sh-format @@ -8924,30 +9035,33 @@ msgid "" "If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n" "To restore the original branch and stop patching run \"$cmdline --abort\"." msgstr "" +"Le patch est vide. Était-il mal découpé ?\n" +"Si vous préférez sauter ce patch, lancez plutôt \"$cmdline --skip\".\n" +"Pour restaurer la branche d'origine et stopper le patchage, lancez \"$cmdline --abort\"." #: git-am.sh:706 msgid "Patch does not have a valid e-mail address." -msgstr "" +msgstr "Le patch n'a pas d'adresse e-mail valide." #: git-am.sh:753 msgid "cannot be interactive without stdin connected to a terminal." -msgstr "" +msgstr "impossible d'être interactif sans entrée standard connectée à un terminal." #: git-am.sh:757 msgid "Commit Body is:" -msgstr "" +msgstr "Le corps de la validation est :" #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a] #. in your translation. The program will only accept English #. input at this point. #: git-am.sh:764 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " -msgstr "" +msgstr "Appliquer ? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " #: git-am.sh:800 #, sh-format msgid "Applying: $FIRSTLINE" -msgstr "" +msgstr "Application : $FIRSTLINE" #: git-am.sh:821 msgid "" @@ -8961,15 +9075,17 @@ msgid "" "You still have unmerged paths in your index\n" "did you forget to use 'git add'?" msgstr "" +"Vous avez toujours des chemins non fusionnés dans votre index\n" +"auriez-vous oublié de faire 'git add' ?" #: git-am.sh:845 msgid "No changes -- Patch already applied." -msgstr "" +msgstr "Pas de changement -- Patch déjà appliqué." #: git-am.sh:855 #, sh-format msgid "Patch failed at $msgnum $FIRSTLINE" -msgstr "" +msgstr "Le patch a échoué à $msgnum $FIRSTLINE" #: git-am.sh:858 #, sh-format @@ -8977,26 +9093,28 @@ msgid "" "The copy of the patch that failed is found in:\n" " $dotest/patch" msgstr "" +"La copie du patch qui a échoué se trouve dans :\n" +" $dotest/patch" #: git-am.sh:876 msgid "applying to an empty history" -msgstr "" +msgstr "application à un historique vide" #: git-bisect.sh:48 msgid "You need to start by \"git bisect start\"" -msgstr "" +msgstr "Vous devez démarrer avec \"git bisect start\"" #. TRANSLATORS: Make sure to include [Y] and [n] in your #. translation. The program will only accept English input #. at this point. #: git-bisect.sh:54 msgid "Do you want me to do it for you [Y/n]? " -msgstr "" +msgstr "Souhaitez-vous que je le fasse pour vous [Y/n] ? " #: git-bisect.sh:95 #, sh-format msgid "unrecognised option: '$arg'" -msgstr "" +msgstr "option inconnue : '$arg'" #: git-bisect.sh:99 #, sh-format @@ -9005,13 +9123,13 @@ msgstr "'$arg' ne semble être une révision valide" #: git-bisect.sh:117 msgid "Bad HEAD - I need a HEAD" -msgstr "" +msgstr "Mauvaise HEAD - j'ai besoin d'une HEAD" #: git-bisect.sh:130 #, sh-format msgid "" "Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'." -msgstr "" +msgstr "L'extraction de '$start_head' a échoué. Essayez 'git bisect reset <branchevalide>'." #: git-bisect.sh:140 msgid "won't bisect on seeked tree" @@ -9019,49 +9137,51 @@ msgstr "" #: git-bisect.sh:144 msgid "Bad HEAD - strange symbolic ref" -msgstr "" +msgstr "Mauvaise HEAD - référence symbolique douteuse" #: git-bisect.sh:189 #, sh-format msgid "Bad bisect_write argument: $state" -msgstr "" +msgstr "Mauvais argument pour bisect_write : $state" #: git-bisect.sh:218 #, sh-format msgid "Bad rev input: $arg" -msgstr "" +msgstr "Mauvaise révision en entrée : $arg" #: git-bisect.sh:232 msgid "Please call 'bisect_state' with at least one argument." -msgstr "" +msgstr "Merci d'appeler 'bisect_state' avec au moins un argument." #: git-bisect.sh:244 #, sh-format msgid "Bad rev input: $rev" -msgstr "" +msgstr "Mauvaise révision en entrée : $rev" #: git-bisect.sh:250 msgid "'git bisect bad' can take only one argument." -msgstr "" +msgstr "'git bisect bad' n'accepte qu'un seul argument." #. have bad but not good. we could bisect although #. this is less optimum. #: git-bisect.sh:273 msgid "Warning: bisecting only with a bad commit." -msgstr "" +msgstr "Attention : bissection avec seulement une mauvaise validation." #. TRANSLATORS: Make sure to include [Y] and [n] in your #. translation. The program will only accept English input #. at this point. #: git-bisect.sh:279 msgid "Are you sure [Y/n]? " -msgstr "" +msgstr "Êtes-vous sûr [Y/n] ? " #: git-bisect.sh:289 msgid "" "You need to give me at least one good and one bad revisions.\n" "(You can use \"git bisect bad\" and \"git bisect good\" for that.)" msgstr "" +"Vous devez me donner au moins une bonne et une mauvaise révision.\n" +"(Vous pouvez utiliser \"git bisect bad\" et \"git bisect good\" pour cela.)" #: git-bisect.sh:292 msgid "" @@ -9069,15 +9189,18 @@ msgid "" "You then need to give me at least one good and one bad revisions.\n" "(You can use \"git bisect bad\" and \"git bisect good\" for that.)" msgstr "" +"Vous devez démarrer avec \"git bisect start\".\n" +"Puis vous devez me donner au moins une bonne et une mauvaise révision.\n" +"(Vous pouvez utiliser \"git bisect bad\" et \"git bisect good\" pour cela.)" #: git-bisect.sh:363 git-bisect.sh:490 msgid "We are not bisecting." -msgstr "" +msgstr "Pas de bissection en cours." #: git-bisect.sh:370 #, sh-format msgid "'$invalid' is not a valid commit" -msgstr "'$invalid' n'est pas un commit valide" +msgstr "'$invalid' n'est pas une validation valide" #: git-bisect.sh:379 #, sh-format @@ -9085,24 +9208,26 @@ msgid "" "Could not check out original HEAD '$branch'.\n" "Try 'git bisect reset <commit>'." msgstr "" +"Échec d'extraction de la HEAD d'origine '$branch'.\n" +"Essayez 'git bisect reset <commit>'." #: git-bisect.sh:406 msgid "No logfile given" -msgstr "" +msgstr "Pas de fichier de log donné" #: git-bisect.sh:407 #, sh-format msgid "cannot read $file for replaying" -msgstr "" +msgstr "impossible de lire $file pour rejouer" #: git-bisect.sh:424 msgid "?? what are you talking about?" -msgstr "" +msgstr "?? de quoi parlez-vous ?" #: git-bisect.sh:436 #, sh-format msgid "running $command" -msgstr "" +msgstr "lancement de $command" #: git-bisect.sh:443 #, sh-format @@ -9110,10 +9235,12 @@ msgid "" "bisect run failed:\n" "exit code $res from '$command' is < 0 or >= 128" msgstr "" +"la bissection a échoué :\n" +"le code retour $res de '$command' est < 0 ou >= 128" #: git-bisect.sh:469 msgid "bisect run cannot continue any more" -msgstr "" +msgstr "la bissection ne peut plus continuer" #: git-bisect.sh:475 #, sh-format @@ -9121,10 +9248,12 @@ msgid "" "bisect run failed:\n" "'bisect_state $state' exited with error code $res" msgstr "" +"la bissection a échoué :\n" +"'bisect_state $state' a retourné le code erreur $res" #: git-bisect.sh:482 msgid "bisect run success" -msgstr "" +msgstr "succès de la bissection" #: git-pull.sh:21 msgid "" @@ -9132,14 +9261,17 @@ msgid "" "Please, fix them up in the work tree, and then use 'git add/rm <file>'\n" "as appropriate to mark resolution, or use 'git commit -a'." msgstr "" +"Le pull n'est pas possible car vous avez des fichiers non fusionnés.\n" +"Merci de corriger dans votre copie de travail, et utilisez alors 'git add/rm <file>'\n" +"si nécessaire pour marquer comme résolu, ou utilisez 'git commit -a'." #: git-pull.sh:25 msgid "Pull is not possible because you have unmerged files." -msgstr "" +msgstr "Le pull n'est pas possible car vous avez des fichiers non fusionnés." #: git-pull.sh:203 msgid "updating an unborn branch with changes added to the index" -msgstr "" +msgstr "mise à jour d'une branche non encore créée avec les changements ajoutés dans l'index" #. The fetch involved updating the current branch. #. The working tree and the index file is still based on the @@ -9152,14 +9284,17 @@ msgid "" "Warning: fast-forwarding your working tree from\n" "Warning: commit $orig_head." msgstr "" +"Attention : fetch a mis à jour la tête de la branche courante.\n" +"Attention : mise à jour en avance rapide de votre copie de travail\n" +"Attention : depuis la validation $orig_head." #: git-pull.sh:260 msgid "Cannot merge multiple branches into empty head" -msgstr "" +msgstr "Impossible de fusionner de multiples branches sur une tête vide" #: git-pull.sh:264 msgid "Cannot rebase onto multiple branches" -msgstr "" +msgstr "Impossible de rebaser sur de multiples branches" #: git-rebase.sh:53 msgid "" @@ -9168,41 +9303,46 @@ msgid "" "To check out the original branch and stop rebasing, run \"git rebase --abort" "\"." msgstr "" +"Lorsque vous aurez résolu ce problème, lancez \"git rebase --continue\".\n" +"Si vous préférez sauter ce patch, lancez \"git rebase --skip\" à la place.\n" +"Pour extraire la branche d'origine et stopper le rebasage, lancez \"git rebase --abort\"." #: git-rebase.sh:160 msgid "The pre-rebase hook refused to rebase." -msgstr "" +msgstr "Le hook pre-rebase a refusé de rebaser." #: git-rebase.sh:165 msgid "It looks like git-am is in progress. Cannot rebase." -msgstr "" +msgstr "Il semble que git-am soit en cours. Impossible de rebaser." #: git-rebase.sh:296 msgid "The --exec option must be used with the --interactive option" -msgstr "" +msgstr "L'option --exec doit être utilisée avec l'option --interactive" #: git-rebase.sh:301 msgid "No rebase in progress?" -msgstr "" +msgstr "Pas de rebasage en cours ?" #: git-rebase.sh:312 msgid "The --edit-todo action can only be used during interactive rebase." -msgstr "" +msgstr "L'action --edit-todo peut seulement être utilisée lors d'un rebasage interactif." #: git-rebase.sh:319 msgid "Cannot read HEAD" -msgstr "" +msgstr "Impossible de lire HEAD" #: git-rebase.sh:322 msgid "" "You must edit all merge conflicts and then\n" "mark them as resolved using git add" msgstr "" +"Vous devez éditer tous les conflits de fusion et\n" +"les marquer comme résolus avec git add" #: git-rebase.sh:340 #, sh-format msgid "Could not move back to $head_name" -msgstr "" +msgstr "Impossible de revenir à $head_name" #: git-rebase.sh:359 #, sh-format @@ -9216,88 +9356,96 @@ msgid "" "and run me again. I am stopping in case you still have something\n" "valuable there." msgstr "" +"Il semble qu'il y ait déjà un répertoire $state_dir_base, et je me demande\n" +"si vous n'êtes pas au milieu d'un autre rebasage. Si c'est le cas,\n" +"merci d'essayer\n" +"\t$cmd_live_rebase\n" +"Si ça n'est pas le cas, merci de\n" +"\t$cmd_clear_stale_rebase\n" +"et relancer à nouveau. Je m'arrête au cas où vous auriez quelque chose\n" +"d'important ici." #: git-rebase.sh:404 #, sh-format msgid "invalid upstream $upstream_name" -msgstr "" +msgstr "invalide $upstream_name en amont" #: git-rebase.sh:428 #, sh-format msgid "$onto_name: there are more than one merge bases" -msgstr "" +msgstr "$onto_name : il y a plus d'une base de fusion" #: git-rebase.sh:431 git-rebase.sh:435 #, sh-format msgid "$onto_name: there is no merge base" -msgstr "" +msgstr "$onto_name : il n'y a pas de base de fusion" #: git-rebase.sh:440 #, sh-format msgid "Does not point to a valid commit: $onto_name" -msgstr "" +msgstr "Ne pointe pas sur une validation valide : $onto_name" #: git-rebase.sh:463 #, sh-format msgid "fatal: no such branch: $branch_name" -msgstr "" +msgstr "fatal : pas de branche : $branch_name" #: git-rebase.sh:483 msgid "Please commit or stash them." -msgstr "" +msgstr "Merci de les valider ou de les remiser." #: git-rebase.sh:501 #, sh-format msgid "Current branch $branch_name is up to date." -msgstr "" +msgstr "La branche courante $branch_name est à jour." #: git-rebase.sh:504 #, sh-format msgid "Current branch $branch_name is up to date, rebase forced." -msgstr "" +msgstr "La branche courante $branch_name est à jour, rebasage forcé." #: git-rebase.sh:515 #, sh-format msgid "Changes from $mb to $onto:" -msgstr "" +msgstr "Changements de $mb sur $onto :" #. Detach HEAD and reset the tree #: git-rebase.sh:524 msgid "First, rewinding head to replay your work on top of it..." -msgstr "" +msgstr "Premièrement, retour de head pour rejouer votre travail par-dessus..." #: git-rebase.sh:532 #, sh-format msgid "Fast-forwarded $branch_name to $onto_name." -msgstr "" +msgstr "$branch_name mise à jour en avance rapide sur $onto_name." #: git-stash.sh:51 msgid "git stash clear with parameters is unimplemented" -msgstr "" +msgstr "git stash clear avec des paramètres n'est pas implémenté" #: git-stash.sh:74 msgid "You do not have the initial commit yet" -msgstr "" +msgstr "Vous n'avez pas encore la validation initiale" #: git-stash.sh:89 msgid "Cannot save the current index state" -msgstr "" +msgstr "Impossible de sauver l'état courant de l'index" #: git-stash.sh:123 git-stash.sh:136 msgid "Cannot save the current worktree state" -msgstr "" +msgstr "Impossible de sauver l'état courant de la copie de travail" #: git-stash.sh:140 msgid "No changes selected" -msgstr "" +msgstr "Aucun changement sélectionné" #: git-stash.sh:143 msgid "Cannot remove temporary index (can't happen)" -msgstr "" +msgstr "Impossible de supprimer l'index temporaire (ne peut pas se produire)" #: git-stash.sh:156 msgid "Cannot record working tree state" -msgstr "" +msgstr "Impossible d'enregistrer l'état de la copie de travail" #. TRANSLATORS: $option is an invalid option, like #. `--blah-blah'. The 7 spaces at the beginning of the @@ -9315,41 +9463,43 @@ msgid "" "error: unknown option for 'stash save': $option\n" " To provide a message, use git stash save -- '$option'" msgstr "" +"erreur: option inconnue pour 'stash save': $option\n" +" Pour fournir un message, utilisez git stash save -- '$option'" #: git-stash.sh:223 msgid "No local changes to save" -msgstr "" +msgstr "Pas de changements en local à sauver" #: git-stash.sh:227 msgid "Cannot initialize stash" -msgstr "" +msgstr "Impossible d'initialiser le remisage" #: git-stash.sh:235 msgid "Cannot save the current status" -msgstr "" +msgstr "Impossible de sauver le statut courant" #: git-stash.sh:253 msgid "Cannot remove worktree changes" -msgstr "" +msgstr "Impossible de supprimer les changements de la copie de travail" #: git-stash.sh:352 msgid "No stash found." -msgstr "" +msgstr "Pas de remisage trouvé." #: git-stash.sh:359 #, sh-format msgid "Too many revisions specified: $REV" -msgstr "" +msgstr "Trop de révisions spécifiées : $REV" #: git-stash.sh:365 #, sh-format msgid "$reference is not valid reference" -msgstr "" +msgstr "$reference n'est pas une référence valide" #: git-stash.sh:393 #, sh-format msgid "'$args' is not a stash-like commit" -msgstr "'$args' n'est pas un commit de type remisage" +msgstr "'$args' n'est pas une validation de type remisage" #: git-stash.sh:404 #, sh-format @@ -9358,23 +9508,23 @@ msgstr "'$args' n'est pas une référence de remisage" #: git-stash.sh:412 msgid "unable to refresh index" -msgstr "" +msgstr "impossible de rafraîchir l'index" #: git-stash.sh:416 msgid "Cannot apply a stash in the middle of a merge" -msgstr "" +msgstr "Impossible d'appliquer un remisage au milieu d'une fusion" #: git-stash.sh:424 msgid "Conflicts in index. Try without --index." -msgstr "" +msgstr "Conflits dans l'index. Essayez sans --index." #: git-stash.sh:426 msgid "Could not save index tree" -msgstr "" +msgstr "Impossible de sauver l'arbre d'index" #: git-stash.sh:460 msgid "Cannot unstage modified files" -msgstr "" +msgstr "Impossible de désindexer les fichiers modifiés" #: git-stash.sh:475 msgid "Index was not unstashed." @@ -9383,50 +9533,50 @@ msgstr "" #: git-stash.sh:492 #, sh-format msgid "Dropped ${REV} ($s)" -msgstr "" +msgstr "${REV} supprimé ($s)" #: git-stash.sh:493 #, sh-format msgid "${REV}: Could not drop stash entry" -msgstr "" +msgstr "${REV}: Impossible de supprimer l'entrée de stash" #: git-stash.sh:500 msgid "No branch name specified" -msgstr "" +msgstr "Aucune branche spécifiée" #: git-stash.sh:571 msgid "(To restore them type \"git stash apply\")" -msgstr "" +msgstr "(Pour les restaurer tapez \"git stash apply\")" #: git-submodule.sh:91 #, sh-format msgid "cannot strip one component off url '$remoteurl'" -msgstr "" +msgstr "impossible de supprimer un composant de l'URL '$remoteurl'" #: git-submodule.sh:196 #, sh-format msgid "No submodule mapping found in .gitmodules for path '$sm_path'" -msgstr "" +msgstr "Pas de mappage du sous-module trouvé dans .gitmodules pour le chemin '$sm_path'" #: git-submodule.sh:239 #, sh-format msgid "Clone of '$url' into submodule path '$sm_path' failed" -msgstr "" +msgstr "Le clonage de '$url' dans le chemin de sous-module '$sm_path' a échoué" #: git-submodule.sh:251 #, sh-format msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa" -msgstr "" +msgstr "Le répertoire Git '$a' fait partie du chemin de sous-module '$b' ou vice-versa" #: git-submodule.sh:349 #, sh-format msgid "repo URL: '$repo' must be absolute or begin with ./|../" -msgstr "" +msgstr "L'URL de dépôt '$repo' doit être absolu ou commencer par ./|../" #: git-submodule.sh:366 #, sh-format msgid "'$sm_path' already exists in the index" -msgstr "" +msgstr "'$sm_path' existe déjà dans l'index" #: git-submodule.sh:370 #, sh-format @@ -9435,11 +9585,14 @@ msgid "" "$sm_path\n" "Use -f if you really want to add it." msgstr "" +"Le chemin suivant est ignoré par un de vos fichiers .gitignore :\n" +"$sm_path\n" +"Utilisez -f si vous voulez vraiment l'ajouter." #: git-submodule.sh:388 #, sh-format msgid "Adding existing repo at '$sm_path' to the index" -msgstr "" +msgstr "Ajout du dépôt existant à '$sm_path' dans l'index" #: git-submodule.sh:390 #, sh-format @@ -9449,119 +9602,119 @@ msgstr "'$sm_path' existe déjà et n'est pas un dépôt git valide" #: git-submodule.sh:398 #, sh-format msgid "A git directory for '$sm_name' is found locally with remote(s):" -msgstr "" +msgstr "Un répertoire git pour '$sm_name' est trouvé en local avec le(s) serveur(s) distant(s) :" #: git-submodule.sh:400 #, sh-format msgid "" "If you want to reuse this local git directory instead of cloning again from" -msgstr "" +msgstr "Si vous voulez réutiliser ce répertoire git local au lieu de cloner à nouveau depuis" #: git-submodule.sh:402 #, sh-format msgid "" "use the '--force' option. If the local git directory is not the correct repo" -msgstr "" +msgstr "utilisez l'option '--force'. Si le répertoire local git n'est pas le dépôt correct" #: git-submodule.sh:403 #, sh-format msgid "" "or you are unsure what this means choose another name with the '--name' " "option." -msgstr "" +msgstr "ou vous ne savez pas ce que cela signifie de choisir un autre nom avec l'option '--name'." #: git-submodule.sh:405 #, sh-format msgid "Reactivating local git directory for submodule '$sm_name'." -msgstr "" +msgstr "Réactivation du répertoire git local pour le sous-module '$sm_name'." #: git-submodule.sh:417 #, sh-format msgid "Unable to checkout submodule '$sm_path'" -msgstr "" +msgstr "Impossible d'extraire le sous-module '$sm_path'" #: git-submodule.sh:422 #, sh-format msgid "Failed to add submodule '$sm_path'" -msgstr "" +msgstr "Échec d'ajout du sous-module '$sm_path'" #: git-submodule.sh:431 #, sh-format msgid "Failed to register submodule '$sm_path'" -msgstr "" +msgstr "Échec d'enregistrement du sous-module '$sm_path'" #: git-submodule.sh:474 #, sh-format msgid "Entering '$prefix$sm_path'" -msgstr "" +msgstr "Entrée dans '$prefix$sm_path'" #: git-submodule.sh:488 #, sh-format msgid "Stopping at '$sm_path'; script returned non-zero status." -msgstr "" +msgstr "Arrêt sur '$sm_path' ; le script a retourné un statut non nul." #: git-submodule.sh:532 #, sh-format msgid "No url found for submodule path '$sm_path' in .gitmodules" -msgstr "" +msgstr "URL non trouvé pour le chemin de sous-module '$sm_path' dans .gitmodules" #: git-submodule.sh:541 #, sh-format msgid "Failed to register url for submodule path '$sm_path'" -msgstr "" +msgstr "Échec d'enregistrement de l'URL pour le chemin de sous-module '$sm_path'" #: git-submodule.sh:543 #, sh-format msgid "Submodule '$name' ($url) registered for path '$sm_path'" -msgstr "" +msgstr "Sous-module '$name' ($url) enregistré pour le chemin '$sm_path'" #: git-submodule.sh:551 #, sh-format msgid "Failed to register update mode for submodule path '$sm_path'" -msgstr "" +msgstr "Échec d'enregistrement du mode de mise à jour pour le chemin de sous-module '$sm_path'" #: git-submodule.sh:588 #, sh-format msgid "Use '.' if you really want to deinitialize all submodules" -msgstr "" +msgstr "Utilisez '.' si vous voulez vraiment réinitialiser tous les sous-modules" #: git-submodule.sh:603 #, sh-format msgid "Submodule work tree '$sm_path' contains a .git directory" -msgstr "" +msgstr "La copie de travail du sous-module '$sm_path' contient un répertoire .git" #: git-submodule.sh:604 #, sh-format msgid "" "(use 'rm -rf' if you really want to remove it including all of its history)" -msgstr "" +msgstr "(utilisez 'rm -rf' si vous voulez vraiment le supprimer en incluant tout son historique)" #: git-submodule.sh:610 #, sh-format msgid "" "Submodule work tree '$sm_path' contains local modifications; use '-f' to " "discard them" -msgstr "" +msgstr "La copie de travail du sous-module '$sm_path' contient des modifications locales; utilisez '-f' pour les annuler" #: git-submodule.sh:613 #, sh-format msgid "Cleared directory '$sm_path'" -msgstr "" +msgstr "Répertoire '$sm_path' nettoyé" #: git-submodule.sh:614 #, sh-format msgid "Could not remove submodule work tree '$sm_path'" -msgstr "" +msgstr "Impossible de supprimer la copie de travail du sous-module '$sm_path'" #: git-submodule.sh:617 #, sh-format msgid "Could not create empty submodule directory '$sm_path'" -msgstr "" +msgstr "Impossible de créer le répertoire vide du sous-module '$sm_path'" #: git-submodule.sh:626 #, sh-format msgid "Submodule '$name' ($url) unregistered for path '$sm_path'" -msgstr "" +msgstr "Le sous-module '$name' ($url) n'est pas enregistré pour le chemin '$sm_path'" #: git-submodule.sh:731 #, sh-format @@ -9569,100 +9722,102 @@ msgid "" "Submodule path '$prefix$sm_path' not initialized\n" "Maybe you want to use 'update --init'?" msgstr "" +"Chemin de sous-module '$prefix$sm_path' non initialisé\n" +"Peut-être souhaitez-vous utiliser 'update --init' ?" #: git-submodule.sh:744 #, sh-format msgid "Unable to find current revision in submodule path '$prefix$sm_path'" -msgstr "" +msgstr "Impossible de trouver la révision courante dans le chemin de sous-module '$prefix$sm_path'" #: git-submodule.sh:753 #, sh-format msgid "Unable to fetch in submodule path '$sm_path'" -msgstr "" +msgstr "Impossible de rapatrier dans le chemin de sous-module '$sm_path'" #: git-submodule.sh:777 #, sh-format msgid "Unable to fetch in submodule path '$prefix$sm_path'" -msgstr "" +msgstr "Impossible de rapatrier dans le chemin de sous-module '$prefix$sm_path'" #: git-submodule.sh:791 #, sh-format msgid "Unable to rebase '$sha1' in submodule path '$prefix$sm_path'" -msgstr "" +msgstr "Impossible de rebaser '$sha1' dans le chemin de sous-module '$prefix$sm_path'" #: git-submodule.sh:792 #, sh-format msgid "Submodule path '$prefix$sm_path': rebased into '$sha1'" -msgstr "" +msgstr "Chemin de sous-module '$prefix$sm_path' : rebasé dans '$sha1'" #: git-submodule.sh:797 #, sh-format msgid "Unable to merge '$sha1' in submodule path '$prefix$sm_path'" -msgstr "" +msgstr "Impossible de fusionner '$sha1' dans le chemin de sous-module '$prefix$sm_path'" #: git-submodule.sh:798 #, sh-format msgid "Submodule path '$prefix$sm_path': merged in '$sha1'" -msgstr "" +msgstr "Chemin de sous-module '$prefix$sm_path' : fusionné dans '$sha1'" #: git-submodule.sh:803 #, sh-format msgid "Unable to checkout '$sha1' in submodule path '$prefix$sm_path'" -msgstr "" +msgstr "Impossible d'extraire '$sha1' dans le chemin de sous-module '$prefix$sm_path'" #: git-submodule.sh:804 #, sh-format msgid "Submodule path '$prefix$sm_path': checked out '$sha1'" -msgstr "" +msgstr "Chemin de sous-module '$prefix$sm_path' : '$sha1' extrait" #: git-submodule.sh:831 #, sh-format msgid "Failed to recurse into submodule path '$prefix$sm_path'" -msgstr "" +msgstr "Échec de parcours dans le chemin du sous-module '$prefix$sm_path'" #: git-submodule.sh:939 msgid "The --cached option cannot be used with the --files option" -msgstr "" +msgstr "L'option --cached ne peut pas être utilisée avec l'option --files" #. unexpected type #: git-submodule.sh:979 #, sh-format msgid "unexpected mode $mod_dst" -msgstr "" +msgstr "mode $mod_dst inattendu" #: git-submodule.sh:997 #, sh-format msgid " Warn: $name doesn't contain commit $sha1_src" -msgstr "" +msgstr " Attention : $name ne contient pas la validation $sha1_src" #: git-submodule.sh:1000 #, sh-format msgid " Warn: $name doesn't contain commit $sha1_dst" -msgstr "" +msgstr " Attention : $name ne contient pas la validation $sha1_dst" #: git-submodule.sh:1003 #, sh-format msgid " Warn: $name doesn't contain commits $sha1_src and $sha1_dst" -msgstr "" +msgstr " Attention : $name ne contient pas les validations $sha1_src et $sha1_dst" #: git-submodule.sh:1028 msgid "blob" -msgstr "" +msgstr "blob" #: git-submodule.sh:1066 msgid "Submodules changed but not updated:" -msgstr "" +msgstr "Sous-modules modifiés mais non mis à jour :" #: git-submodule.sh:1068 msgid "Submodule changes to be committed:" -msgstr "" +msgstr "Changements du sous-module à valider :" #: git-submodule.sh:1153 #, sh-format msgid "Failed to recurse into submodule path '$sm_path'" -msgstr "" +msgstr "Échec de parcours dans le chemin du sous-module '$sm_path'" #: git-submodule.sh:1216 #, sh-format msgid "Synchronizing submodule url for '$prefix$sm_path'" -msgstr "" +msgstr "Synchronisation de l'URL sous-module pour '$prefix$sm_path'" diff --git a/preload-index.c b/preload-index.c index 49cb08df96..8c44ceb2c7 100644 --- a/preload-index.c +++ b/preload-index.c @@ -2,9 +2,11 @@ * Copyright (C) 2008 Linus Torvalds */ #include "cache.h" +#include "pathspec.h" #ifdef NO_PTHREADS -static void preload_index(struct index_state *index, const char **pathspec) +static void preload_index(struct index_state *index, + const struct pathspec *pathspec) { ; /* nothing */ } @@ -24,7 +26,7 @@ static void preload_index(struct index_state *index, const char **pathspec) struct thread_data { pthread_t pthread; struct index_state *index; - const char **pathspec; + struct pathspec pathspec; int offset, nr; }; @@ -35,9 +37,7 @@ static void *preload_thread(void *_data) struct index_state *index = p->index; struct cache_entry **cep = index->cache + p->offset; struct cache_def cache; - struct pathspec pathspec; - init_pathspec(&pathspec, p->pathspec); memset(&cache, 0, sizeof(cache)); nr = p->nr; if (nr + p->offset > index->cache_nr) @@ -53,7 +53,7 @@ static void *preload_thread(void *_data) continue; if (ce_uptodate(ce)) continue; - if (!ce_path_match(ce, &pathspec)) + if (!ce_path_match(ce, &p->pathspec)) continue; if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce))) continue; @@ -63,11 +63,11 @@ static void *preload_thread(void *_data) continue; ce_mark_uptodate(ce); } while (--nr > 0); - free_pathspec(&pathspec); return NULL; } -static void preload_index(struct index_state *index, const char **pathspec) +static void preload_index(struct index_state *index, + const struct pathspec *pathspec) { int threads, i, work, offset; struct thread_data data[MAX_PARALLEL]; @@ -82,10 +82,12 @@ static void preload_index(struct index_state *index, const char **pathspec) threads = MAX_PARALLEL; offset = 0; work = DIV_ROUND_UP(index->cache_nr, threads); + memset(&data, 0, sizeof(data)); for (i = 0; i < threads; i++) { struct thread_data *p = data+i; p->index = index; - p->pathspec = pathspec; + if (pathspec) + copy_pathspec(&p->pathspec, pathspec); p->offset = offset; p->nr = work; offset += work; @@ -100,7 +102,8 @@ static void preload_index(struct index_state *index, const char **pathspec) } #endif -int read_index_preload(struct index_state *index, const char **pathspec) +int read_index_preload(struct index_state *index, + const struct pathspec *pathspec) { int retval = read_index(index); @@ -42,23 +42,6 @@ void sq_quote_buf(struct strbuf *dst, const char *src) free(to_free); } -void sq_quote_print(FILE *stream, const char *src) -{ - char c; - - fputc('\'', stream); - while ((c = *src++)) { - if (need_bs_quote(c)) { - fputs("'\\", stream); - fputc(c, stream); - fputc('\'', stream); - } else { - fputc(c, stream); - } - } - fputc('\'', stream); -} - void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) { int i; @@ -408,72 +391,72 @@ int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp) /* quoting as a string literal for other languages */ -void perl_quote_print(FILE *stream, const char *src) +void perl_quote_buf(struct strbuf *sb, const char *src) { const char sq = '\''; const char bq = '\\'; char c; - fputc(sq, stream); + strbuf_addch(sb, sq); while ((c = *src++)) { if (c == sq || c == bq) - fputc(bq, stream); - fputc(c, stream); + strbuf_addch(sb, bq); + strbuf_addch(sb, c); } - fputc(sq, stream); + strbuf_addch(sb, sq); } -void python_quote_print(FILE *stream, const char *src) +void python_quote_buf(struct strbuf *sb, const char *src) { const char sq = '\''; const char bq = '\\'; const char nl = '\n'; char c; - fputc(sq, stream); + strbuf_addch(sb, sq); while ((c = *src++)) { if (c == nl) { - fputc(bq, stream); - fputc('n', stream); + strbuf_addch(sb, bq); + strbuf_addch(sb, 'n'); continue; } if (c == sq || c == bq) - fputc(bq, stream); - fputc(c, stream); + strbuf_addch(sb, bq); + strbuf_addch(sb, c); } - fputc(sq, stream); + strbuf_addch(sb, sq); } -void tcl_quote_print(FILE *stream, const char *src) +void tcl_quote_buf(struct strbuf *sb, const char *src) { char c; - fputc('"', stream); + strbuf_addch(sb, '"'); while ((c = *src++)) { switch (c) { case '[': case ']': case '{': case '}': case '$': case '\\': case '"': - fputc('\\', stream); + strbuf_addch(sb, '\\'); default: - fputc(c, stream); + strbuf_addch(sb, c); break; case '\f': - fputs("\\f", stream); + strbuf_addstr(sb, "\\f"); break; case '\r': - fputs("\\r", stream); + strbuf_addstr(sb, "\\r"); break; case '\n': - fputs("\\n", stream); + strbuf_addstr(sb, "\\n"); break; case '\t': - fputs("\\t", stream); + strbuf_addstr(sb, "\\t"); break; case '\v': - fputs("\\v", stream); + strbuf_addstr(sb, "\\v"); break; } } - fputc('"', stream); + strbuf_addch(sb, '"'); } @@ -27,8 +27,6 @@ struct strbuf; * excluding the final null regardless of the buffer size. */ -extern void sq_quote_print(FILE *stream, const char *src); - extern void sq_quote_buf(struct strbuf *, const char *src); extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); @@ -68,8 +66,8 @@ extern char *quote_path_relative(const char *in, const char *prefix, struct strbuf *out); /* quoting as a string literal for other languages */ -extern void perl_quote_print(FILE *stream, const char *src); -extern void python_quote_print(FILE *stream, const char *src); -extern void tcl_quote_print(FILE *stream, const char *src); +extern void perl_quote_buf(struct strbuf *sb, const char *src); +extern void python_quote_buf(struct strbuf *sb, const char *src); +extern void tcl_quote_buf(struct strbuf *sb, const char *src); #endif diff --git a/read-cache.c b/read-cache.c index c3d5e3543f..885943a6df 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1114,7 +1114,8 @@ static void show_file(const char * fmt, const char * name, int in_porcelain, printf(fmt, name); } -int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, +int refresh_index(struct index_state *istate, unsigned int flags, + const struct pathspec *pathspec, char *seen, const char *header_msg) { int i; @@ -1149,7 +1150,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p continue; if (pathspec && - !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) + !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen)) filtered = 1; if (ce_stage(ce)) { @@ -1229,14 +1230,14 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall struct ondisk_cache_entry { struct cache_time ctime; struct cache_time mtime; - unsigned int dev; - unsigned int ino; - unsigned int mode; - unsigned int uid; - unsigned int gid; - unsigned int size; + uint32_t dev; + uint32_t ino; + uint32_t mode; + uint32_t uid; + uint32_t gid; + uint32_t size; unsigned char sha1[20]; - unsigned short flags; + uint16_t flags; char name[FLEX_ARRAY]; /* more */ }; @@ -1248,15 +1249,15 @@ struct ondisk_cache_entry { struct ondisk_cache_entry_extended { struct cache_time ctime; struct cache_time mtime; - unsigned int dev; - unsigned int ino; - unsigned int mode; - unsigned int uid; - unsigned int gid; - unsigned int size; + uint32_t dev; + uint32_t ino; + uint32_t mode; + uint32_t uid; + uint32_t gid; + uint32_t size; unsigned char sha1[20]; - unsigned short flags; - unsigned short flags2; + uint16_t flags; + uint16_t flags2; char name[FLEX_ARRAY]; /* more */ }; @@ -3196,14 +3196,6 @@ int update_ref(const char *action, const char *refname, return 0; } -struct ref *find_ref_by_name(const struct ref *list, const char *name) -{ - for ( ; list; list = list->next) - if (!strcmp(list->name, name)) - return (struct ref *)list; - return NULL; -} - /* * generate a format suitable for scanf from a ref_rev_parse_rules * rule, that is replace the "%.*s" spec with a "%s" spec diff --git a/remote-curl.c b/remote-curl.c index 5b3ce9eed2..b5ebe01800 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -6,6 +6,7 @@ #include "exec_cmd.h" #include "run-command.h" #include "pkt-line.h" +#include "string-list.h" #include "sideband.h" #include "argv-array.h" @@ -16,11 +17,13 @@ struct options { int verbosity; unsigned long depth; unsigned progress : 1, + check_self_contained_and_connected : 1, followtags : 1, dry_run : 1, thin : 1; }; static struct options options; +static struct string_list cas_options = STRING_LIST_INIT_DUP; static int set_option(const char *name, const char *value) { @@ -67,6 +70,22 @@ static int set_option(const char *name, const char *value) return -1; return 0; } + else if (!strcmp(name, "check-connectivity")) { + if (!strcmp(value, "true")) + options.check_self_contained_and_connected = 1; + else if (!strcmp(value, "false")) + options.check_self_contained_and_connected = 0; + else + return -1; + return 0; + } + else if (!strcmp(name, "cas")) { + struct strbuf val = STRBUF_INIT; + strbuf_addf(&val, "--" CAS_OPT_NAME "=%s", value); + string_list_append(&cas_options, val.buf); + strbuf_release(&val); + return 0; + } else { return 1 /* unsupported */; } @@ -654,7 +673,7 @@ static int fetch_git(struct discovery *heads, struct strbuf preamble = STRBUF_INIT; char *depth_arg = NULL; int argc = 0, i, err; - const char *argv[15]; + const char *argv[16]; argv[argc++] = "fetch-pack"; argv[argc++] = "--stateless-rpc"; @@ -668,6 +687,8 @@ static int fetch_git(struct discovery *heads, argv[argc++] = "-v"; argv[argc++] = "-v"; } + if (options.check_self_contained_and_connected) + argv[argc++] = "--check-self-contained-and-connected"; if (!options.progress) argv[argc++] = "--no-progress"; if (options.depth) { @@ -790,6 +811,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) struct rpc_state rpc; int i, err; struct argv_array args; + struct string_list_item *cas_option; argv_array_init(&args); argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status", @@ -804,6 +826,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) else if (options.verbosity > 1) argv_array_push(&args, "--verbose"); argv_array_push(&args, options.progress ? "--progress" : "--no-progress"); + for_each_string_list_item(cas_option, &cas_options) + argv_array_push(&args, cas_option->string); argv_array_push(&args, url); for (i = 0; i < nr_spec; i++) argv_array_push(&args, specs[i]); @@ -939,6 +963,7 @@ int main(int argc, const char **argv) printf("fetch\n"); printf("option\n"); printf("push\n"); + printf("check-connectivity\n"); printf("\n"); fflush(stdout); } else { @@ -148,6 +148,7 @@ static struct remote *make_remote(const char *name, int len) } ret = xcalloc(1, sizeof(struct remote)); + ret->prune = -1; /* unspecified */ ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); remotes[remotes_nr++] = ret; if (len) @@ -404,6 +405,8 @@ static int handle_config(const char *key, const char *value, void *cb) remote->skip_default_update = git_config_bool(key, value); else if (!strcmp(subkey, ".skipfetchall")) remote->skip_default_update = git_config_bool(key, value); + else if (!strcmp(subkey, ".prune")) + remote->prune = git_config_bool(key, value); else if (!strcmp(subkey, ".url")) { const char *v; if (git_config_string(&v, key, value)) @@ -1302,6 +1305,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds free(sent_tips.tip); } +struct ref *find_ref_by_name(const struct ref *list, const char *name) +{ + for ( ; list; list = list->next) + if (!strcmp(list->name, name)) + return (struct ref *)list; + return NULL; +} + static void prepare_ref_index(struct string_list *ref_index, struct ref *ref) { for ( ; ref; ref = ref->next) @@ -1411,12 +1422,13 @@ int match_push_refs(struct ref *src, struct ref **dst, } void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, - int force_update) + int force_update) { struct ref *ref; for (ref = remote_refs; ref; ref = ref->next) { int force_ref_update = ref->force || force_update; + int reject_reason = 0; if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); @@ -1431,6 +1443,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, } /* + * Bypass the usual "must fast-forward" check but + * replace it with a weaker "the old value must be + * this value we observed". If the remote ref has + * moved and is now different from what we expect, + * reject any push. + * + * It also is an error if the user told us to check + * with the remote-tracking branch to find the value + * to expect, but we did not have such a tracking + * branch. + */ + if (ref->expect_old_sha1) { + if (ref->expect_old_no_trackback || + hashcmp(ref->old_sha1, ref->old_sha1_expect)) + reject_reason = REF_STATUS_REJECT_STALE; + } + + /* + * The usual "must fast-forward" rules. + * * Decide whether an individual refspec A:B can be * pushed. The push will succeed if any of the * following are true: @@ -1448,24 +1480,26 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, * passing the --force argument */ - if (!ref->deletion && !is_null_sha1(ref->old_sha1)) { - int why = 0; /* why would this push require --force? */ - + else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) { if (!prefixcmp(ref->name, "refs/tags/")) - why = REF_STATUS_REJECT_ALREADY_EXISTS; + reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_sha1_file(ref->old_sha1)) - why = REF_STATUS_REJECT_FETCH_FIRST; + reject_reason = REF_STATUS_REJECT_FETCH_FIRST; else if (!lookup_commit_reference_gently(ref->old_sha1, 1) || !lookup_commit_reference_gently(ref->new_sha1, 1)) - why = REF_STATUS_REJECT_NEEDS_FORCE; + reject_reason = REF_STATUS_REJECT_NEEDS_FORCE; else if (!ref_newer(ref->new_sha1, ref->old_sha1)) - why = REF_STATUS_REJECT_NONFASTFORWARD; - - if (!force_ref_update) - ref->status = why; - else if (why) - ref->forced_update = 1; + reject_reason = REF_STATUS_REJECT_NONFASTFORWARD; } + + /* + * "--force" will defeat any rejection implemented + * by the rules above. + */ + if (!force_ref_update) + ref->status = reject_reason; + else if (reject_reason) + ref->forced_update = 1; } } @@ -1936,3 +1970,121 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet string_list_clear(&ref_names, 0); return stale_refs; } + +/* + * Compare-and-swap + */ +void clear_cas_option(struct push_cas_option *cas) +{ + int i; + + for (i = 0; i < cas->nr; i++) + free(cas->entry[i].refname); + free(cas->entry); + memset(cas, 0, sizeof(*cas)); +} + +static struct push_cas *add_cas_entry(struct push_cas_option *cas, + const char *refname, + size_t refnamelen) +{ + struct push_cas *entry; + ALLOC_GROW(cas->entry, cas->nr + 1, cas->alloc); + entry = &cas->entry[cas->nr++]; + memset(entry, 0, sizeof(*entry)); + entry->refname = xmemdupz(refname, refnamelen); + return entry; +} + +int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset) +{ + const char *colon; + struct push_cas *entry; + + if (unset) { + /* "--no-<option>" */ + clear_cas_option(cas); + return 0; + } + + if (!arg) { + /* just "--<option>" */ + cas->use_tracking_for_rest = 1; + return 0; + } + + /* "--<option>=refname" or "--<option>=refname:value" */ + colon = strchrnul(arg, ':'); + entry = add_cas_entry(cas, arg, colon - arg); + if (!*colon) + entry->use_tracking = 1; + else if (get_sha1(colon + 1, entry->expect)) + return error("cannot parse expected object name '%s'", colon + 1); + return 0; +} + +int parseopt_push_cas_option(const struct option *opt, const char *arg, int unset) +{ + return parse_push_cas_option(opt->value, arg, unset); +} + +int is_empty_cas(const struct push_cas_option *cas) +{ + return !cas->use_tracking_for_rest && !cas->nr; +} + +/* + * Look at remote.fetch refspec and see if we have a remote + * tracking branch for the refname there. Fill its current + * value in sha1[]. + * If we cannot do so, return negative to signal an error. + */ +static int remote_tracking(struct remote *remote, const char *refname, + unsigned char sha1[20]) +{ + char *dst; + + dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname); + if (!dst) + return -1; /* no tracking ref for refname at remote */ + if (read_ref(dst, sha1)) + return -1; /* we know what the tracking ref is but we cannot read it */ + return 0; +} + +static void apply_cas(struct push_cas_option *cas, + struct remote *remote, + struct ref *ref) +{ + int i; + + /* Find an explicit --<option>=<name>[:<value>] entry */ + for (i = 0; i < cas->nr; i++) { + struct push_cas *entry = &cas->entry[i]; + if (!refname_match(entry->refname, ref->name, ref_rev_parse_rules)) + continue; + ref->expect_old_sha1 = 1; + if (!entry->use_tracking) + hashcpy(ref->old_sha1_expect, cas->entry[i].expect); + else if (remote_tracking(remote, ref->name, ref->old_sha1_expect)) + ref->expect_old_no_trackback = 1; + return; + } + + /* Are we using "--<option>" to cover all? */ + if (!cas->use_tracking_for_rest) + return; + + ref->expect_old_sha1 = 1; + if (remote_tracking(remote, ref->name, ref->old_sha1_expect)) + ref->expect_old_no_trackback = 1; +} + +void apply_push_cas(struct push_cas_option *cas, + struct remote *remote, + struct ref *remote_refs) +{ + struct ref *ref; + for (ref = remote_refs; ref; ref = ref->next) + apply_cas(cas, remote, ref); +} @@ -1,6 +1,8 @@ #ifndef REMOTE_H #define REMOTE_H +#include "parse-options.h" + enum { REMOTE_CONFIG, REMOTE_REMOTES, @@ -40,6 +42,7 @@ struct remote { int fetch_tags; int skip_default_update; int mirror; + int prune; const char *receivepack; const char *uploadpack; @@ -71,6 +74,56 @@ struct refspec { extern const struct refspec *tag_refspec; +struct ref { + struct ref *next; + unsigned char old_sha1[20]; + unsigned char new_sha1[20]; + unsigned char old_sha1_expect[20]; /* used by expect-old */ + char *symref; + unsigned int + force:1, + forced_update:1, + expect_old_sha1:1, + expect_old_no_trackback:1, + deletion:1, + matched:1; + + /* + * Order is important here, as we write to FETCH_HEAD + * in numeric order. And the default NOT_FOR_MERGE + * should be 0, so that xcalloc'd structures get it + * by default. + */ + enum { + FETCH_HEAD_MERGE = -1, + FETCH_HEAD_NOT_FOR_MERGE = 0, + FETCH_HEAD_IGNORE = 1 + } fetch_head_status; + + enum { + REF_STATUS_NONE = 0, + REF_STATUS_OK, + REF_STATUS_REJECT_NONFASTFORWARD, + REF_STATUS_REJECT_ALREADY_EXISTS, + REF_STATUS_REJECT_NODELETE, + REF_STATUS_REJECT_FETCH_FIRST, + REF_STATUS_REJECT_NEEDS_FORCE, + REF_STATUS_REJECT_STALE, + REF_STATUS_UPTODATE, + REF_STATUS_REMOTE_REJECT, + REF_STATUS_EXPECTING_REPORT + } status; + char *remote_status; + struct ref *peer_ref; /* when renaming */ + char name[FLEX_ARRAY]; /* more */ +}; + +#define REF_NORMAL (1u << 0) +#define REF_HEADS (1u << 1) +#define REF_TAGS (1u << 2) + +extern struct ref *find_ref_by_name(const struct ref *list, const char *name); + struct ref *alloc_ref(const char *name); struct ref *copy_ref(const struct ref *ref); struct ref *copy_ref_list(const struct ref *ref); @@ -84,6 +137,14 @@ int check_ref_type(const struct ref *ref, int flags); */ void free_refs(struct ref *ref); +struct extra_have_objects { + int nr, alloc; + unsigned char (*array)[20]; +}; +extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, + struct ref **list, unsigned int flags, + struct extra_have_objects *); + int resolve_remote_symref(struct ref *ref, struct ref *list); int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1); @@ -172,4 +233,27 @@ struct ref *guess_remote_head(const struct ref *head, /* Return refs which no longer exist on remote */ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map); +/* + * Compare-and-swap + */ +#define CAS_OPT_NAME "force-with-lease" + +struct push_cas_option { + unsigned use_tracking_for_rest:1; + struct push_cas { + unsigned char expect[20]; + unsigned use_tracking:1; + char *refname; + } *entry; + int nr; + int alloc; +}; + +extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset); +extern int parse_push_cas_option(struct push_cas_option *, const char *arg, int unset); +extern void clear_cas_option(struct push_cas_option *); + +extern int is_empty_cas(const struct push_cas_option *); +void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *); + #endif @@ -6,6 +6,7 @@ #include "resolve-undo.h" #include "ll-merge.h" #include "attr.h" +#include "pathspec.h" #define RESOLVED 0 #define PUNTED 1 @@ -656,7 +657,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr) return 0; } -int rerere_forget(const char **pathspec) +int rerere_forget(struct pathspec *pathspec) { int i, fd; struct string_list conflict = STRING_LIST_INIT_DUP; @@ -671,8 +672,8 @@ int rerere_forget(const char **pathspec) find_conflict(&conflict); for (i = 0; i < conflict.nr; i++) { struct string_list_item *it = &conflict.items[i]; - if (!match_pathspec(pathspec, it->string, strlen(it->string), - 0, NULL)) + if (!match_pathspec_depth(pathspec, it->string, strlen(it->string), + 0, NULL)) continue; rerere_forget_one_path(it->string, &merge_rr); } @@ -3,6 +3,8 @@ #include "string-list.h" +struct pathspec; + #define RERERE_AUTOUPDATE 01 #define RERERE_NOAUTOUPDATE 02 @@ -16,7 +18,7 @@ extern void *RERERE_RESOLVED; extern int setup_rerere(struct string_list *, int); extern int rerere(int); extern const char *rerere_path(const char *hex, const char *file); -extern int rerere_forget(const char **); +extern int rerere_forget(struct pathspec *); extern int rerere_remaining(struct string_list *); extern void rerere_clear(struct string_list *); extern void rerere_gc(struct string_list *); diff --git a/resolve-undo.c b/resolve-undo.c index 77101f51c1..c09b00664e 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -173,7 +173,7 @@ void unmerge_marked_index(struct index_state *istate) } } -void unmerge_index(struct index_state *istate, const char **pathspec) +void unmerge_index(struct index_state *istate, const struct pathspec *pathspec) { int i; @@ -182,7 +182,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec) for (i = 0; i < istate->cache_nr; i++) { const struct cache_entry *ce = istate->cache[i]; - if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) + if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) continue; i = unmerge_index_entry_at(istate, i); } diff --git a/resolve-undo.h b/resolve-undo.h index 7a30206aad..46306455ed 100644 --- a/resolve-undo.h +++ b/resolve-undo.h @@ -11,7 +11,7 @@ extern void resolve_undo_write(struct strbuf *, struct string_list *); extern struct string_list *resolve_undo_read(const char *, unsigned long); extern void resolve_undo_clear_index(struct index_state *); extern int unmerge_index_entry_at(struct index_state *, int); -extern void unmerge_index(struct index_state *, const char **); +extern void unmerge_index(struct index_state *, const struct pathspec *); extern void unmerge_marked_index(struct index_state *); #endif diff --git a/revision.c b/revision.c index 84ccc0529b..6230a80a77 100644 --- a/revision.c +++ b/revision.c @@ -15,6 +15,7 @@ #include "string-list.h" #include "line-log.h" #include "mailmap.h" +#include "commit-slab.h" volatile show_early_output_fn_t show_early_output; @@ -1372,7 +1373,7 @@ static void prepare_show_merge(struct rev_info *revs) i++; } free_pathspec(&revs->prune_data); - init_pathspec(&revs->prune_data, prune); + parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, 0, "", prune); revs->limited = 1; } @@ -2120,8 +2121,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s */ ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc); prune_data.path[prune_data.nr++] = NULL; - init_pathspec(&revs->prune_data, - get_pathspec(revs->prefix, prune_data.path)); + parse_pathspec(&revs->prune_data, 0, 0, + revs->prefix, prune_data.path); } if (revs->def == NULL) @@ -2154,12 +2155,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s revs->limited = 1; if (revs->prune_data.nr) { - diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning); + copy_pathspec(&revs->pruning.pathspec, &revs->prune_data); /* Can't prune commits with rename following: the paths change.. */ if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->prune = 1; if (!revs->full_diff) - diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt); + copy_pathspec(&revs->diffopt.pathspec, + &revs->prune_data); } if (revs->combine_merges) revs->ignore_merges = 0; @@ -2763,7 +2765,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt) return retval; } -static inline int want_ancestry(struct rev_info *revs) +static inline int want_ancestry(const struct rev_info *revs) { return (revs->rewrite_parents || revs->children.name); } @@ -2820,6 +2822,14 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) if (action == commit_show && !revs->show_all && revs->prune && revs->dense && want_ancestry(revs)) { + /* + * --full-diff on simplified parents is no good: it + * will show spurious changes from the commits that + * were elided. So we save the parents on the side + * when --full-diff is in effect. + */ + if (revs->full_diff) + save_parents(revs, commit); if (rewrite_parents(revs, commit, rewrite_one) < 0) return commit_error; } @@ -2839,6 +2849,7 @@ static struct commit *get_revision_1(struct rev_info *revs) free(entry); if (revs->reflog_info) { + save_parents(revs, commit); fake_reflog_parent(revs->reflog_info, commit); commit->object.flags &= ~(ADDED | SEEN | SHOWN); } @@ -3038,6 +3049,8 @@ struct commit *get_revision(struct rev_info *revs) c = get_revision_internal(revs); if (c && revs->graph) graph_update(revs->graph, c); + if (!c) + free_saved_parents(revs); return c; } @@ -3069,3 +3082,54 @@ void put_revision_mark(const struct rev_info *revs, const struct commit *commit) fputs(mark, stdout); putchar(' '); } + +define_commit_slab(saved_parents, struct commit_list *); + +#define EMPTY_PARENT_LIST ((struct commit_list *)-1) + +void save_parents(struct rev_info *revs, struct commit *commit) +{ + struct commit_list **pp; + + if (!revs->saved_parents_slab) { + revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents)); + init_saved_parents(revs->saved_parents_slab); + } + + pp = saved_parents_at(revs->saved_parents_slab, commit); + + /* + * When walking with reflogs, we may visit the same commit + * several times: once for each appearance in the reflog. + * + * In this case, save_parents() will be called multiple times. + * We want to keep only the first set of parents. We need to + * store a sentinel value for an empty (i.e., NULL) parent + * list to distinguish it from a not-yet-saved list, however. + */ + if (*pp) + return; + if (commit->parents) + *pp = copy_commit_list(commit->parents); + else + *pp = EMPTY_PARENT_LIST; +} + +struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit) +{ + struct commit_list *parents; + + if (!revs->saved_parents_slab) + return commit->parents; + + parents = *saved_parents_at(revs->saved_parents_slab, commit); + if (parents == EMPTY_PARENT_LIST) + return NULL; + return parents; +} + +void free_saved_parents(struct rev_info *revs) +{ + if (revs->saved_parents_slab) + clear_saved_parents(revs->saved_parents_slab); +} diff --git a/revision.h b/revision.h index 95859ba119..e7f1d211bf 100644 --- a/revision.h +++ b/revision.h @@ -25,6 +25,7 @@ struct rev_info; struct log_info; struct string_list; +struct saved_parents; struct rev_cmdline_info { unsigned int nr; @@ -187,6 +188,9 @@ struct rev_info { /* line level range that we are chasing */ struct decoration line_log_data; + + /* copies of the parent lists, for --full-diff display */ + struct saved_parents *saved_parents_slab; }; #define REV_TREE_SAME 0 @@ -273,4 +277,20 @@ typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct extern int rewrite_parents(struct rev_info *revs, struct commit *commit, rewrite_parent_fn_t rewrite_parent); + +/* + * Save a copy of the parent list, and return the saved copy. This is + * used by the log machinery to retrieve the original parents when + * commit->parents has been modified by history simpification. + * + * You may only call save_parents() once per commit (this is checked + * for non-root commits). + * + * get_saved_parents() will transparently return commit->parents if + * history simplification is off. + */ +extern void save_parents(struct rev_info *revs, struct commit *commit); +extern struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit); +extern void free_saved_parents(struct rev_info *revs); + #endif diff --git a/send-pack.c b/send-pack.c index 7d172ef37f..b228d65613 100644 --- a/send-pack.c +++ b/send-pack.c @@ -5,6 +5,7 @@ #include "sideband.h" #include "run-command.h" #include "remote.h" +#include "connect.h" #include "send-pack.h" #include "quote.h" #include "transport.h" @@ -226,6 +227,7 @@ int send_pack(struct send_pack_args *args, case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_REJECT_FETCH_FIRST: case REF_STATUS_REJECT_NEEDS_FORCE: + case REF_STATUS_REJECT_STALE: case REF_STATUS_UPTODATE: continue; default: @@ -5,7 +5,19 @@ static int inside_git_dir = -1; static int inside_work_tree = -1; -static char *prefix_path_gently(const char *prefix, int len, const char *path) +/* + * Normalize "path", prepending the "prefix" for relative paths. If + * remaining_prefix is not NULL, return the actual prefix still + * remains in the path. For example, prefix = sub1/sub2/ and path is + * + * foo -> sub1/sub2/foo (full prefix) + * ../foo -> sub1/foo (remaining prefix is sub1/) + * ../../bar -> bar (no remaining prefix) + * ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix) + * `pwd`/../bar -> sub1/bar (no remaining prefix) + */ +char *prefix_path_gently(const char *prefix, int len, + int *remaining_prefix, const char *path) { const char *orig = path; char *sanitized; @@ -13,13 +25,17 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path) const char *temp = real_path(path); sanitized = xmalloc(len + strlen(temp) + 1); strcpy(sanitized, temp); + if (remaining_prefix) + *remaining_prefix = 0; } else { sanitized = xmalloc(len + strlen(path) + 1); if (len) memcpy(sanitized, prefix, len); strcpy(sanitized + len, path); + if (remaining_prefix) + *remaining_prefix = len; } - if (normalize_path_copy(sanitized, sanitized)) + if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) goto error_out; if (is_absolute_path(orig)) { size_t root_len, len, total; @@ -44,7 +60,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path) char *prefix_path(const char *prefix, int len, const char *path) { - char *r = prefix_path_gently(prefix, len, path); + char *r = prefix_path_gently(prefix, len, NULL, path); if (!r) die("'%s' is outside repository", path); return r; @@ -53,7 +69,7 @@ char *prefix_path(const char *prefix, int len, const char *path) int path_inside_repo(const char *prefix, const char *path) { int len = prefix ? strlen(prefix) : 0; - char *r = prefix_path_gently(prefix, len, path); + char *r = prefix_path_gently(prefix, len, NULL, path); if (r) { free(r); return 1; @@ -154,155 +170,6 @@ void verify_non_filename(const char *prefix, const char *arg) "'git <command> [<revision>...] -- [<file>...]'", arg); } -/* - * Magic pathspec - * - * NEEDSWORK: These need to be moved to dir.h or even to a new - * pathspec.h when we restructure get_pathspec() users to use the - * "struct pathspec" interface. - * - * Possible future magic semantics include stuff like: - * - * { PATHSPEC_NOGLOB, '!', "noglob" }, - * { PATHSPEC_ICASE, '\0', "icase" }, - * { PATHSPEC_RECURSIVE, '*', "recursive" }, - * { PATHSPEC_REGEXP, '\0', "regexp" }, - * - */ -#define PATHSPEC_FROMTOP (1<<0) - -static struct pathspec_magic { - unsigned bit; - char mnemonic; /* this cannot be ':'! */ - const char *name; -} pathspec_magic[] = { - { PATHSPEC_FROMTOP, '/', "top" }, -}; - -/* - * Take an element of a pathspec and check for magic signatures. - * Append the result to the prefix. - * - * For now, we only parse the syntax and throw out anything other than - * "top" magic. - * - * NEEDSWORK: This needs to be rewritten when we start migrating - * get_pathspec() users to use the "struct pathspec" interface. For - * example, a pathspec element may be marked as case-insensitive, but - * the prefix part must always match literally, and a single stupid - * string cannot express such a case. - */ -static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt) -{ - unsigned magic = 0; - const char *copyfrom = elt; - int i; - - if (elt[0] != ':') { - ; /* nothing to do */ - } else if (elt[1] == '(') { - /* longhand */ - const char *nextat; - for (copyfrom = elt + 2; - *copyfrom && *copyfrom != ')'; - copyfrom = nextat) { - size_t len = strcspn(copyfrom, ",)"); - if (copyfrom[len] == ',') - nextat = copyfrom + len + 1; - else - /* handle ')' and '\0' */ - nextat = copyfrom + len; - if (!len) - continue; - for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) - if (strlen(pathspec_magic[i].name) == len && - !strncmp(pathspec_magic[i].name, copyfrom, len)) { - magic |= pathspec_magic[i].bit; - break; - } - if (ARRAY_SIZE(pathspec_magic) <= i) - die("Invalid pathspec magic '%.*s' in '%s'", - (int) len, copyfrom, elt); - } - if (*copyfrom != ')') - die("Missing ')' at the end of pathspec magic in '%s'", elt); - copyfrom++; - } else { - /* shorthand */ - for (copyfrom = elt + 1; - *copyfrom && *copyfrom != ':'; - copyfrom++) { - char ch = *copyfrom; - - if (!is_pathspec_magic(ch)) - break; - for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) - if (pathspec_magic[i].mnemonic == ch) { - magic |= pathspec_magic[i].bit; - break; - } - if (ARRAY_SIZE(pathspec_magic) <= i) - die("Unimplemented pathspec magic '%c' in '%s'", - ch, elt); - } - if (*copyfrom == ':') - copyfrom++; - } - - if (magic & PATHSPEC_FROMTOP) - return xstrdup(copyfrom); - else - return prefix_path(prefix, prefixlen, copyfrom); -} - -/* - * N.B. get_pathspec() is deprecated in favor of the "struct pathspec" - * based interface - see pathspec_magic above. - * - * Arguments: - * - prefix - a path relative to the root of the working tree - * - pathspec - a list of paths underneath the prefix path - * - * Iterates over pathspec, prepending each path with prefix, - * and return the resulting list. - * - * If pathspec is empty, return a singleton list containing prefix. - * - * If pathspec and prefix are both empty, return an empty list. - * - * This is typically used by built-in commands such as add.c, in order - * to normalize argv arguments provided to the built-in into a list of - * paths to process, all relative to the root of the working tree. - */ -const char **get_pathspec(const char *prefix, const char **pathspec) -{ - const char *entry = *pathspec; - const char **src, **dst; - int prefixlen; - - if (!prefix && !entry) - return NULL; - - if (!entry) { - static const char *spec[2]; - spec[0] = prefix; - spec[1] = NULL; - return spec; - } - - /* Otherwise we have to re-write the entries.. */ - src = pathspec; - dst = pathspec; - prefixlen = prefix ? strlen(prefix) : 0; - while (*src) { - *(dst++) = prefix_pathspec(prefix, prefixlen, *src); - src++; - } - *dst = NULL; - if (!*pathspec) - return NULL; - return pathspec; -} /* * Test if it looks like we're at a git directory. diff --git a/sha1_file.c b/sha1_file.c index 8e27db1bd2..8c2d1ed52d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -614,7 +614,7 @@ static void scan_windows(struct packed_git *p, } } -static int unuse_one_window(struct packed_git *current, int keep_fd) +static int unuse_one_window(struct packed_git *current) { struct packed_git *p, *lru_p = NULL; struct pack_window *lru_w = NULL, *lru_l = NULL; @@ -628,15 +628,8 @@ static int unuse_one_window(struct packed_git *current, int keep_fd) pack_mapped -= lru_w->len; if (lru_l) lru_l->next = lru_w->next; - else { + else lru_p->windows = lru_w->next; - if (!lru_p->windows && lru_p->pack_fd != -1 - && lru_p->pack_fd != keep_fd) { - close(lru_p->pack_fd); - pack_open_fds--; - lru_p->pack_fd = -1; - } - } free(lru_w); pack_open_windows--; return 1; @@ -644,10 +637,10 @@ static int unuse_one_window(struct packed_git *current, int keep_fd) return 0; } -void release_pack_memory(size_t need, int fd) +void release_pack_memory(size_t need) { size_t cur = pack_mapped; - while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd)) + while (need >= (cur - pack_mapped) && unuse_one_window(NULL)) ; /* nothing */ } @@ -658,7 +651,7 @@ void *xmmap(void *start, size_t length, if (ret == MAP_FAILED) { if (!length) return NULL; - release_pack_memory(length, fd); + release_pack_memory(length); ret = mmap(start, length, prot, flags, fd, offset); if (ret == MAP_FAILED) die_errno("Out of memory? mmap failed"); @@ -682,6 +675,83 @@ void close_pack_windows(struct packed_git *p) } } +/* + * The LRU pack is the one with the oldest MRU window, preferring packs + * with no used windows, or the oldest mtime if it has no windows allocated. + */ +static void find_lru_pack(struct packed_git *p, struct packed_git **lru_p, struct pack_window **mru_w, int *accept_windows_inuse) +{ + struct pack_window *w, *this_mru_w; + int has_windows_inuse = 0; + + /* + * Reject this pack if it has windows and the previously selected + * one does not. If this pack does not have windows, reject + * it if the pack file is newer than the previously selected one. + */ + if (*lru_p && !*mru_w && (p->windows || p->mtime > (*lru_p)->mtime)) + return; + + for (w = this_mru_w = p->windows; w; w = w->next) { + /* + * Reject this pack if any of its windows are in use, + * but the previously selected pack did not have any + * inuse windows. Otherwise, record that this pack + * has windows in use. + */ + if (w->inuse_cnt) { + if (*accept_windows_inuse) + has_windows_inuse = 1; + else + return; + } + + if (w->last_used > this_mru_w->last_used) + this_mru_w = w; + + /* + * Reject this pack if it has windows that have been + * used more recently than the previously selected pack. + * If the previously selected pack had windows inuse and + * we have not encountered a window in this pack that is + * inuse, skip this check since we prefer a pack with no + * inuse windows to one that has inuse windows. + */ + if (*mru_w && *accept_windows_inuse == has_windows_inuse && + this_mru_w->last_used > (*mru_w)->last_used) + return; + } + + /* + * Select this pack. + */ + *mru_w = this_mru_w; + *lru_p = p; + *accept_windows_inuse = has_windows_inuse; +} + +static int close_one_pack(void) +{ + struct packed_git *p, *lru_p = NULL; + struct pack_window *mru_w = NULL; + int accept_windows_inuse = 1; + + for (p = packed_git; p; p = p->next) { + if (p->pack_fd == -1) + continue; + find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse); + } + + if (lru_p) { + close(lru_p->pack_fd); + pack_open_fds--; + lru_p->pack_fd = -1; + return 1; + } + + return 0; +} + void unuse_pack(struct pack_window **w_cursor) { struct pack_window *w = *w_cursor; @@ -777,7 +847,7 @@ static int open_packed_git_1(struct packed_git *p) pack_max_fds = 1; } - while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1)) + while (pack_max_fds <= pack_open_fds && close_one_pack()) ; /* nothing */ p->pack_fd = git_open_noatime(p->pack_name); @@ -893,7 +963,7 @@ unsigned char *use_pack(struct packed_git *p, win->len = (size_t)len; pack_mapped += win->len; while (packed_git_limit < pack_mapped - && unuse_one_window(p, p->pack_fd)) + && unuse_one_window(p)) ; /* nothing */ win->base = xmmap(NULL, win->len, PROT_READ, MAP_PRIVATE, @@ -939,7 +1009,7 @@ static struct packed_git *alloc_packed_git(int extra) static void try_to_free_pack_memory(size_t size) { - release_pack_memory(size, -1); + release_pack_memory(size); } struct packed_git *add_packed_git(const char *path, int path_len, int local) diff --git a/submodule.c b/submodule.c index 3f0a3f9419..1905d75b2b 100644 --- a/submodule.c +++ b/submodule.c @@ -10,6 +10,7 @@ #include "string-list.h" #include "sha1-array.h" #include "argv-array.h" +#include "blob.h" static struct string_list config_name_for_path; static struct string_list config_fetch_recurse_submodules_for_name; @@ -30,6 +31,118 @@ static struct sha1_array ref_tips_after_fetch; */ static int gitmodules_is_unmerged; +/* + * This flag is set if the .gitmodules file had unstaged modifications on + * startup. This must be checked before allowing modifications to the + * .gitmodules file with the intention to stage them later, because when + * continuing we would stage the modifications the user didn't stage herself + * too. That might change in a future version when we learn to stage the + * changes we do ourselves without staging any previous modifications. + */ +static int gitmodules_is_modified; + + +int is_staging_gitmodules_ok(void) +{ + return !gitmodules_is_modified; +} + +/* + * Try to update the "path" entry in the "submodule.<name>" section of the + * .gitmodules file. Return 0 only if a .gitmodules file was found, a section + * with the correct path=<oldpath> setting was found and we could update it. + */ +int update_path_in_gitmodules(const char *oldpath, const char *newpath) +{ + struct strbuf entry = STRBUF_INIT; + struct string_list_item *path_option; + + if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */ + return -1; + + if (gitmodules_is_unmerged) + die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first")); + + path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath); + if (!path_option) { + warning(_("Could not find section in .gitmodules where path=%s"), oldpath); + return -1; + } + strbuf_addstr(&entry, "submodule."); + strbuf_addstr(&entry, path_option->util); + strbuf_addstr(&entry, ".path"); + if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) { + /* Maybe the user already did that, don't error out here */ + warning(_("Could not update .gitmodules entry %s"), entry.buf); + strbuf_release(&entry); + return -1; + } + strbuf_release(&entry); + return 0; +} + +/* + * Try to remove the "submodule.<name>" section from .gitmodules where the given + * path is configured. Return 0 only if a .gitmodules file was found, a section + * with the correct path=<path> setting was found and we could remove it. + */ +int remove_path_from_gitmodules(const char *path) +{ + struct strbuf sect = STRBUF_INIT; + struct string_list_item *path_option; + + if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */ + return -1; + + if (gitmodules_is_unmerged) + die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first")); + + path_option = unsorted_string_list_lookup(&config_name_for_path, path); + if (!path_option) { + warning(_("Could not find section in .gitmodules where path=%s"), path); + return -1; + } + strbuf_addstr(§, "submodule."); + strbuf_addstr(§, path_option->util); + if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) { + /* Maybe the user already did that, don't error out here */ + warning(_("Could not remove .gitmodules entry for %s"), path); + strbuf_release(§); + return -1; + } + strbuf_release(§); + return 0; +} + +void stage_updated_gitmodules(void) +{ + struct strbuf buf = STRBUF_INIT; + struct stat st; + int pos; + struct cache_entry *ce; + int namelen = strlen(".gitmodules"); + + pos = cache_name_pos(".gitmodules", namelen); + if (pos < 0) { + warning(_("could not find .gitmodules in index")); + return; + } + ce = active_cache[pos]; + ce->ce_flags = namelen; + if (strbuf_read_file(&buf, ".gitmodules", 0) < 0) + die(_("reading updated .gitmodules failed")); + if (lstat(".gitmodules", &st) < 0) + die_errno(_("unable to stat updated .gitmodules")); + fill_stat_cache_info(ce, &st); + ce->ce_mode = ce_mode_from_stat(ce, st.st_mode); + if (remove_cache_entry_at(pos) < 0) + die(_("unable to remove .gitmodules from index")); + if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1)) + die(_("adding updated .gitmodules failed")); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) + die(_("staging updated .gitmodules failed")); +} + static int add_submodule_odb(const char *path) { struct strbuf objects_directory = STRBUF_INIT; @@ -116,6 +229,11 @@ void gitmodules_config(void) !memcmp(ce->name, ".gitmodules", 11)) gitmodules_is_unmerged = 1; } + } else if (pos < active_nr) { + struct stat st; + if (lstat(".gitmodules", &st) == 0 && + ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED) + gitmodules_is_modified = 1; } if (!gitmodules_is_unmerged) @@ -134,6 +252,9 @@ int parse_submodule_config_option(const char *var, const char *value) return 0; if (!strcmp(key, "path")) { + if (!value) + return config_error_nonbool(var); + config = unsorted_string_list_lookup(&config_name_for_path, value); if (config) free(config->util); @@ -151,6 +272,9 @@ int parse_submodule_config_option(const char *var, const char *value) } else if (!strcmp(key, "ignore")) { char *name_cstr; + if (!value) + return config_error_nonbool(var); + if (strcmp(value, "untracked") && strcmp(value, "dirty") && strcmp(value, "all") && strcmp(value, "none")) { warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var); @@ -1004,3 +1128,34 @@ int merge_submodule(unsigned char result[20], const char *path, free(merges.objects); return 0; } + +/* Update gitfile and core.worktree setting to connect work tree and git dir */ +void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir) +{ + struct strbuf file_name = STRBUF_INIT; + struct strbuf rel_path = STRBUF_INIT; + const char *real_work_tree = xstrdup(real_path(work_tree)); + FILE *fp; + + /* Update gitfile */ + strbuf_addf(&file_name, "%s/.git", work_tree); + fp = fopen(file_name.buf, "w"); + if (!fp) + die(_("Could not create git link %s"), file_name.buf); + fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree, + &rel_path)); + fclose(fp); + + /* Update core.worktree setting */ + strbuf_reset(&file_name); + strbuf_addf(&file_name, "%s/config", git_dir); + if (git_config_set_in_file(file_name.buf, "core.worktree", + relative_path(real_work_tree, git_dir, + &rel_path))) + die(_("Could not set core.worktree in %s"), + file_name.buf); + + strbuf_release(&file_name); + strbuf_release(&rel_path); + free((void *)real_work_tree); +} diff --git a/submodule.h b/submodule.h index c7ffc7c399..7beec4822b 100644 --- a/submodule.h +++ b/submodule.h @@ -11,6 +11,10 @@ enum { RECURSE_SUBMODULES_ON = 2 }; +int is_staging_gitmodules_ok(void); +int update_path_in_gitmodules(const char *oldpath, const char *newpath); +int remove_path_from_gitmodules(const char *path); +void stage_updated_gitmodules(void); void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, const char *path); int submodule_config(const char *var, const char *value, void *cb); @@ -36,5 +40,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name, struct string_list *needs_pushing); int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name); +void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir); #endif diff --git a/t/.gitattributes b/t/.gitattributes index 1b97c5465b..2d44088f56 100644 --- a/t/.gitattributes +++ b/t/.gitattributes @@ -1 +1,2 @@ t[0-9][0-9][0-9][0-9]/* -whitespace +t0110/url-* binary diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh index d4e7f4736f..99caa42f5c 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -185,6 +185,26 @@ test_expect_success 'blame -L Y,X (undocumented)' ' check_count -L6,3 B 1 B1 1 B2 1 D 1 ' +test_expect_success 'blame -L -X' ' + test_must_fail $PROG -L-1 file +' + +test_expect_success 'blame -L 0' ' + test_must_fail $PROG -L0 file +' + +test_expect_success 'blame -L ,0' ' + test_must_fail $PROG -L,0 file +' + +test_expect_success 'blame -L ,+0' ' + test_must_fail $PROG -L,+0 file +' + +test_expect_success 'blame -L X,+0' ' + test_must_fail $PROG -L1,+0 file +' + test_expect_success 'blame -L X,+1' ' check_count -L3,+1 B2 1 ' @@ -193,6 +213,14 @@ test_expect_success 'blame -L X,+N' ' check_count -L3,+4 B 1 B1 1 B2 1 D 1 ' +test_expect_success 'blame -L ,-0' ' + test_must_fail $PROG -L,-0 file +' + +test_expect_success 'blame -L X,-0' ' + test_must_fail $PROG -L1,-0 file +' + test_expect_success 'blame -L X,-1' ' check_count -L3,-1 B2 1 ' @@ -225,14 +253,105 @@ test_expect_success 'blame -L /RE/,-N' ' check_count -L/99/,-3 B 1 B2 1 D 1 ' +# 'file' ends with an incomplete line, so 'wc' reports one fewer lines than +# git-blame sees, hence the last line is actually $(wc...)+1. +test_expect_success 'blame -L X (X == nlines)' ' + n=$(expr $(wc -l <file) + 1) && + check_count -L$n C 1 +' + +test_expect_success 'blame -L X (X == nlines + 1)' ' + n=$(expr $(wc -l <file) + 2) && + test_must_fail $PROG -L$n file +' + test_expect_success 'blame -L X (X > nlines)' ' test_must_fail $PROG -L12345 file ' +test_expect_success 'blame -L ,Y (Y == nlines)' ' + n=$(expr $(wc -l <file) + 1) && + check_count -L,$n A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L ,Y (Y == nlines + 1)' ' + n=$(expr $(wc -l <file) + 2) && + test_must_fail $PROG -L,$n file +' + test_expect_success 'blame -L ,Y (Y > nlines)' ' test_must_fail $PROG -L,12345 file ' +test_expect_success 'blame -L multiple (disjoint)' ' + check_count -L2,3 -L6,7 A 1 B1 1 B2 1 "A U Thor" 1 +' + +test_expect_success 'blame -L multiple (disjoint: unordered)' ' + check_count -L6,7 -L2,3 A 1 B1 1 B2 1 "A U Thor" 1 +' + +test_expect_success 'blame -L multiple (adjacent)' ' + check_count -L2,3 -L4,5 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (adjacent: unordered)' ' + check_count -L4,5 -L2,3 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (overlapping)' ' + check_count -L2,4 -L3,5 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (overlapping: unordered)' ' + check_count -L3,5 -L2,4 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (superset/subset)' ' + check_count -L2,8 -L3,5 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L multiple (superset/subset: unordered)' ' + check_count -L3,5 -L2,8 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/ (relative)' ' + check_count -L3,3 -L/fox/ B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/ (relative: no preceding range)' ' + check_count -L/dog/ A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/ (relative: adjacent)' ' + check_count -L1,1 -L/dog/,+1 A 1 E 1 +' + +test_expect_success 'blame -L /RE/ (relative: not found)' ' + test_must_fail $PROG -L4,4 -L/dog/ file +' + +test_expect_success 'blame -L /RE/ (relative: end-of-file)' ' + test_must_fail $PROG -L, -L/$/ file +' + +test_expect_success 'blame -L ^/RE/ (absolute)' ' + check_count -L3,3 -L^/dog/,+2 A 1 B2 1 +' + +test_expect_success 'blame -L ^/RE/ (absolute: no preceding range)' ' + check_count -L^/dog/,+2 A 1 B2 1 +' + +test_expect_success 'blame -L ^/RE/ (absolute: not found)' ' + test_must_fail $PROG -L4,4 -L^/tambourine/ file +' + +test_expect_success 'blame -L ^/RE/ (absolute: end-of-file)' ' + n=$(expr $(wc -l <file) + 1) && + check_count -L$n -L^/$/,+2 A 1 C 1 E 1 +' + test_expect_success 'setup -L :regex' ' tr Q "\\t" >hello.c <<-\EOF && int main(int argc, const char *argv[]) @@ -275,12 +394,139 @@ test_expect_success 'blame -L :nomatch' ' test_must_fail $PROG -L:nomatch hello.c ' -test_expect_success 'blame -L bogus' ' - test_must_fail $PROG -L file && - test_must_fail $PROG -L1,+ file && - test_must_fail $PROG -L1,- file && - test_must_fail $PROG -LX file && - test_must_fail $PROG -L1,X file && - test_must_fail $PROG -L1,+N file && +test_expect_success 'blame -L :RE (relative)' ' + check_count -f hello.c -L3,3 -L:ma.. F 1 H 4 +' + +test_expect_success 'blame -L :RE (relative: no preceding range)' ' + check_count -f hello.c -L:ma.. F 4 G 1 +' + +test_expect_success 'blame -L :RE (relative: not found)' ' + test_must_fail $PROG -L3,3 -L:tambourine hello.c +' + +test_expect_success 'blame -L :RE (relative: end-of-file)' ' + test_must_fail $PROG -L, -L:main hello.c +' + +test_expect_success 'blame -L ^:RE (absolute)' ' + check_count -f hello.c -L3,3 -L^:ma.. F 4 G 1 +' + +test_expect_success 'blame -L ^:RE (absolute: no preceding range)' ' + check_count -f hello.c -L^:ma.. F 4 G 1 +' + +test_expect_success 'blame -L ^:RE (absolute: not found)' ' + test_must_fail $PROG -L4,4 -L^:tambourine hello.c +' + +test_expect_success 'blame -L ^:RE (absolute: end-of-file)' ' + n=$(printf "%d" $(wc -l <hello.c)) && + check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1 +' + +test_expect_success 'setup incremental' ' + ( + GIT_AUTHOR_NAME=I && + export GIT_AUTHOR_NAME && + GIT_AUTHOR_EMAIL=I@test.git && + export GIT_AUTHOR_EMAIL && + >incremental && + git add incremental && + git commit -m "step 0" && + printf "partial" >>incremental && + git commit -a -m "step 0.5" && + echo >>incremental && + git commit -a -m "step 1" + ) +' + +test_expect_success 'blame empty' ' + check_count -h HEAD^^ -f incremental +' + +test_expect_success 'blame -L 0 empty' ' + test_must_fail $PROG -L0 incremental HEAD^^ +' + +test_expect_success 'blame -L 1 empty' ' + test_must_fail $PROG -L1 incremental HEAD^^ +' + +test_expect_success 'blame -L 2 empty' ' + test_must_fail $PROG -L2 incremental HEAD^^ +' + +test_expect_success 'blame half' ' + check_count -h HEAD^ -f incremental I 1 +' + +test_expect_success 'blame -L 0 half' ' + test_must_fail $PROG -L0 incremental HEAD^ +' + +test_expect_success 'blame -L 1 half' ' + check_count -h HEAD^ -f incremental -L1 I 1 +' + +test_expect_success 'blame -L 2 half' ' + test_must_fail $PROG -L2 incremental HEAD^ +' + +test_expect_success 'blame -L 3 half' ' + test_must_fail $PROG -L3 incremental HEAD^ +' + +test_expect_success 'blame full' ' + check_count -f incremental I 1 +' + +test_expect_success 'blame -L 0 full' ' + test_must_fail $PROG -L0 incremental +' + +test_expect_success 'blame -L 1 full' ' + check_count -f incremental -L1 I 1 +' + +test_expect_success 'blame -L 2 full' ' + test_must_fail $PROG -L2 incremental +' + +test_expect_success 'blame -L 3 full' ' + test_must_fail $PROG -L3 incremental +' + +test_expect_success 'blame -L' ' + test_must_fail $PROG -L file +' + +test_expect_success 'blame -L X,+' ' + test_must_fail $PROG -L1,+ file +' + +test_expect_success 'blame -L X,-' ' + test_must_fail $PROG -L1,- file +' + +test_expect_success 'blame -L X (non-numeric X)' ' + test_must_fail $PROG -LX file +' + +test_expect_success 'blame -L X,Y (non-numeric Y)' ' + test_must_fail $PROG -L1,Y file +' + +test_expect_success 'blame -L X,+N (non-numeric N)' ' + test_must_fail $PROG -L1,+N file +' + +test_expect_success 'blame -L X,-N (non-numeric N)' ' test_must_fail $PROG -L1,-N file ' + +test_expect_success 'blame -L ,^/RE/' ' + test_must_fail $PROG -L1,^/99/ file +' diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 895b9258b0..dab405d574 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -141,10 +141,11 @@ stop_httpd() { -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop } -test_http_push_nonff() { +test_http_push_nonff () { REMOTE_REPO=$1 LOCAL_REPO=$2 BRANCH=$3 + EXPECT_CAS_RESULT=${4-failure} test_expect_success 'non-fast-forward push fails' ' cd "$REMOTE_REPO" && @@ -167,6 +168,22 @@ test_http_push_nonff() { test_expect_success 'non-fast-forward push shows help message' ' test_i18ngrep "Updates were rejected because" output ' + + test_expect_failure 'force with lease aka cas' ' + HEAD=$( cd "$REMOTE_REPO" && git rev-parse --verify HEAD ) && + test_when_finished '\'' + (cd "$REMOTE_REPO" && git update-ref HEAD "$HEAD") + '\'' && + ( + cd "$LOCAL_REPO" && + git push -v --force-with-lease=$BRANCH:$HEAD origin + ) && + git rev-parse --verify "$BRANCH" >expect && + ( + cd "$REMOTE_REPO" && git rev-parse --verify HEAD + ) >actual && + test_cmp expect actual + ' } setup_askpass_helper() { diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index dd17e3a09d..397c480401 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -22,6 +22,9 @@ ErrorLog error.log <IfModule !mod_version.c> LoadModule version_module modules/mod_version.so </IfModule> +<IfModule !mod_headers.c> + LoadModule headers_module modules/mod_headers.so +</IfModule> <IfVersion < 2.4> LockFile accept.lock @@ -87,6 +90,11 @@ Alias /auth/dumb/ www/auth/dumb/ SetEnv GIT_HTTP_EXPORT_ALL SetEnv GIT_NAMESPACE ns </LocationMatch> +<LocationMatch /smart_cookies/> + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL + Header set Set-Cookie name=value +</LocationMatch> ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1 ScriptAlias /broken_smart/ broken-smart-http.sh/ <Directory ${GIT_EXEC_PATH}> diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index c29342d6bc..96f40fedfb 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -432,7 +432,7 @@ test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' ' test_expect_success_multi SYMLINKS 'beyond a symlink' '' ' test_check_ignore "a/symlink/foo" 128 && - test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link" + test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link" ' test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' @@ -440,7 +440,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' cd a && test_check_ignore "symlink/foo" 128 ) && - test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link" + test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link" ' ############################################################################ @@ -449,7 +449,7 @@ test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' test_expect_success_multi 'submodule' '' ' test_check_ignore "a/submodule/one" 128 && - test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" + test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" ' test_expect_success_multi 'submodule from subdirectory' '' ' @@ -457,7 +457,7 @@ test_expect_success_multi 'submodule from subdirectory' '' ' cd a && test_check_ignore "submodule/one" 128 ) && - test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" + test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''" ' ############################################################################ diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index e50f0f742f..b92e6cb046 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -190,4 +190,18 @@ test_expect_success 'required filter clean failure' ' test_must_fail git add test.fc ' +test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE + +test_expect_success EXPENSIVE 'filter large file' ' + git config filter.largefile.smudge cat && + git config filter.largefile.clean cat && + for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB && + echo "2GB filter=largefile" >.gitattributes && + git add 2GB 2>err && + ! test -s err && + rm -f 2GB && + git checkout -- 2GB 2>err && + ! test -s err +' + test_done diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh index 986b2a8f26..5ed69a6f56 100755 --- a/t/t0070-fundamental.sh +++ b/t/t0070-fundamental.sh @@ -25,6 +25,10 @@ test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints file grep "cannotwrite/test" err ' +test_expect_success 'git_mkstemps_mode does not fail if fd 0 is not open' ' + git commit --allow-empty -m message <&- +' + test_expect_success 'check for a bug in the regex routines' ' # if this test fails, re-build git with NO_REGEX=1 test-regex diff --git a/t/t0110-urlmatch-normalization.sh b/t/t0110-urlmatch-normalization.sh new file mode 100755 index 0000000000..8d6096d4d1 --- /dev/null +++ b/t/t0110-urlmatch-normalization.sh @@ -0,0 +1,177 @@ +#!/bin/sh + +test_description='urlmatch URL normalization' +. ./test-lib.sh + +# The base name of the test url files +tu="$TEST_DIRECTORY/t0110/url" + +# Note that only file: URLs should be allowed without a host + +test_expect_success 'url scheme' ' + ! test-urlmatch-normalization "" && + ! test-urlmatch-normalization "_" && + ! test-urlmatch-normalization "scheme" && + ! test-urlmatch-normalization "scheme:" && + ! test-urlmatch-normalization "scheme:/" && + ! test-urlmatch-normalization "scheme://" && + ! test-urlmatch-normalization "file" && + ! test-urlmatch-normalization "file:" && + ! test-urlmatch-normalization "file:/" && + test-urlmatch-normalization "file://" && + ! test-urlmatch-normalization "://acme.co" && + ! test-urlmatch-normalization "x_test://acme.co" && + ! test-urlmatch-normalization "-test://acme.co" && + ! test-urlmatch-normalization "0test://acme.co" && + ! test-urlmatch-normalization "+test://acme.co" && + ! test-urlmatch-normalization ".test://acme.co" && + ! test-urlmatch-normalization "schem%6e://" && + test-urlmatch-normalization "x-Test+v1.0://acme.co" && + test "$(test-urlmatch-normalization -p "AbCdeF://x.Y")" = "abcdef://x.y/" +' + +test_expect_success 'url authority' ' + ! test-urlmatch-normalization "scheme://user:pass@" && + ! test-urlmatch-normalization "scheme://?" && + ! test-urlmatch-normalization "scheme://#" && + ! test-urlmatch-normalization "scheme:///" && + ! test-urlmatch-normalization "scheme://:" && + ! test-urlmatch-normalization "scheme://:555" && + test-urlmatch-normalization "file://user:pass@" && + test-urlmatch-normalization "file://?" && + test-urlmatch-normalization "file://#" && + test-urlmatch-normalization "file:///" && + test-urlmatch-normalization "file://:" && + ! test-urlmatch-normalization "file://:555" && + test-urlmatch-normalization "scheme://user:pass@host" && + test-urlmatch-normalization "scheme://@host" && + test-urlmatch-normalization "scheme://%00@host" && + ! test-urlmatch-normalization "scheme://%%@host" && + ! test-urlmatch-normalization "scheme://host_" && + test-urlmatch-normalization "scheme://user:pass@host/" && + test-urlmatch-normalization "scheme://@host/" && + test-urlmatch-normalization "scheme://host/" && + test-urlmatch-normalization "scheme://host?x" && + test-urlmatch-normalization "scheme://host#x" && + test-urlmatch-normalization "scheme://host/@" && + test-urlmatch-normalization "scheme://host?@x" && + test-urlmatch-normalization "scheme://host#@x" && + test-urlmatch-normalization "scheme://[::1]" && + test-urlmatch-normalization "scheme://[::1]/" && + ! test-urlmatch-normalization "scheme://hos%41/" && + test-urlmatch-normalization "scheme://[invalid....:/" && + test-urlmatch-normalization "scheme://invalid....:]/" && + ! test-urlmatch-normalization "scheme://invalid....:[/" && + ! test-urlmatch-normalization "scheme://invalid....:[" +' + +test_expect_success 'url port checks' ' + test-urlmatch-normalization "xyz://q@some.host:" && + test-urlmatch-normalization "xyz://q@some.host:456/" && + ! test-urlmatch-normalization "xyz://q@some.host:0" && + ! test-urlmatch-normalization "xyz://q@some.host:0000000" && + test-urlmatch-normalization "xyz://q@some.host:0000001?" && + test-urlmatch-normalization "xyz://q@some.host:065535#" && + test-urlmatch-normalization "xyz://q@some.host:65535" && + ! test-urlmatch-normalization "xyz://q@some.host:65536" && + ! test-urlmatch-normalization "xyz://q@some.host:99999" && + ! test-urlmatch-normalization "xyz://q@some.host:100000" && + ! test-urlmatch-normalization "xyz://q@some.host:100001" && + test-urlmatch-normalization "http://q@some.host:80" && + test-urlmatch-normalization "https://q@some.host:443" && + test-urlmatch-normalization "http://q@some.host:80/" && + test-urlmatch-normalization "https://q@some.host:443?" && + ! test-urlmatch-normalization "http://q@:8008" && + ! test-urlmatch-normalization "http://:8080" && + ! test-urlmatch-normalization "http://:" && + test-urlmatch-normalization "xyz://q@some.host:456/" && + test-urlmatch-normalization "xyz://[::1]:456/" && + test-urlmatch-normalization "xyz://[::1]:/" && + ! test-urlmatch-normalization "xyz://[::1]:000/" && + ! test-urlmatch-normalization "xyz://[::1]:0%300/" && + ! test-urlmatch-normalization "xyz://[::1]:0x80/" && + ! test-urlmatch-normalization "xyz://[::1]:4294967297/" && + ! test-urlmatch-normalization "xyz://[::1]:030f/" +' + +test_expect_success 'url port normalization' ' + test "$(test-urlmatch-normalization -p "http://x:800")" = "http://x:800/" && + test "$(test-urlmatch-normalization -p "http://x:0800")" = "http://x:800/" && + test "$(test-urlmatch-normalization -p "http://x:00000800")" = "http://x:800/" && + test "$(test-urlmatch-normalization -p "http://x:065535")" = "http://x:65535/" && + test "$(test-urlmatch-normalization -p "http://x:1")" = "http://x:1/" && + test "$(test-urlmatch-normalization -p "http://x:80")" = "http://x/" && + test "$(test-urlmatch-normalization -p "http://x:080")" = "http://x/" && + test "$(test-urlmatch-normalization -p "http://x:000000080")" = "http://x/" && + test "$(test-urlmatch-normalization -p "https://x:443")" = "https://x/" && + test "$(test-urlmatch-normalization -p "https://x:0443")" = "https://x/" && + test "$(test-urlmatch-normalization -p "https://x:000000443")" = "https://x/" +' + +test_expect_success 'url general escapes' ' + ! test-urlmatch-normalization "http://x.y?%fg" && + test "$(test-urlmatch-normalization -p "X://W/%7e%41^%3a")" = "x://w/~A%5E%3A" && + test "$(test-urlmatch-normalization -p "X://W/:/?#[]@")" = "x://w/:/?#[]@" && + test "$(test-urlmatch-normalization -p "X://W/$&()*+,;=")" = "x://w/$&()*+,;=" && + test "$(test-urlmatch-normalization -p "X://W/'\''")" = "x://w/'\''" && + test "$(test-urlmatch-normalization -p "X://W?'\!'")" = "x://w/?'\!'" +' + +test_expect_success 'url high-bit escapes' ' + test "$(test-urlmatch-normalization -p "$(cat "$tu-1")")" = "x://q/%01%02%03%04%05%06%07%08%0E%0F%10%11%12" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-2")")" = "x://q/%13%14%15%16%17%18%19%1B%1C%1D%1E%1F%7F" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-3")")" = "x://q/%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-4")")" = "x://q/%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-5")")" = "x://q/%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-6")")" = "x://q/%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-7")")" = "x://q/%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-8")")" = "x://q/%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-9")")" = "x://q/%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-10")")" = "x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-11")")" = "x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD" +' + +test_expect_success 'url username/password escapes' ' + test "$(test-urlmatch-normalization -p "x://%41%62(^):%70+d@foo")" = "x://Ab(%5E):p+d@foo/" +' + +test_expect_success 'url normalized lengths' ' + test "$(test-urlmatch-normalization -l "Http://%4d%65:%4d^%70@The.Host")" = 25 && + test "$(test-urlmatch-normalization -l "http://%41:%42@x.y/%61/")" = 17 && + test "$(test-urlmatch-normalization -l "http://@x.y/^")" = 15 +' + +test_expect_success 'url . and .. segments' ' + test "$(test-urlmatch-normalization -p "x://y/.")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/./")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/a/.")" = "x://y/a" && + test "$(test-urlmatch-normalization -p "x://y/a/./")" = "x://y/a/" && + test "$(test-urlmatch-normalization -p "x://y/.?")" = "x://y/?" && + test "$(test-urlmatch-normalization -p "x://y/./?")" = "x://y/?" && + test "$(test-urlmatch-normalization -p "x://y/a/.?")" = "x://y/a?" && + test "$(test-urlmatch-normalization -p "x://y/a/./?")" = "x://y/a/?" && + test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c")" = "x://y/c" && + test "$(test-urlmatch-normalization -p "x://y/a/./b/../.././c/")" = "x://y/c/" && + test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c/././.././.")" = "x://y/" && + ! test-urlmatch-normalization "x://y/a/./b/.././../c/././.././.." && + test "$(test-urlmatch-normalization -p "x://y/a/./?/././..")" = "x://y/a/?/././.." && + test "$(test-urlmatch-normalization -p "x://y/%2e/")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/%2E/")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/a/%2e./")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/b/.%2E/")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/c/%2e%2E/")" = "x://y/" +' + +# http://@foo specifies an empty user name but does not specify a password +# http://foo specifies neither a user name nor a password +# So they should not be equivalent +test_expect_success 'url equivalents' ' + test-urlmatch-normalization "httP://x" "Http://X/" && + test-urlmatch-normalization "Http://%4d%65:%4d^%70@The.Host" "hTTP://Me:%4D^p@the.HOST:80/" && + ! test-urlmatch-normalization "https://@x.y/^" "httpS://x.y:443/^" && + test-urlmatch-normalization "https://@x.y/^" "httpS://@x.y:0443/^" && + test-urlmatch-normalization "https://@x.y/^/../abc" "httpS://@x.y:0443/abc" && + test-urlmatch-normalization "https://@x.y/^/.." "httpS://@x.y:0443/" +' + +test_done diff --git a/t/t0110/README b/t/t0110/README new file mode 100644 index 0000000000..ad4a50ecd8 --- /dev/null +++ b/t/t0110/README @@ -0,0 +1,9 @@ +The url data files in this directory contain URLs with characters +in the range 0x01-0x1f and 0x7f-0xff to test the proper normalization +of unprintable characters. + +A select few characters in the 0x01-0x1f range are skipped to help +avoid problems running the test itself. + +The urls are in test files in this directory rather than being +embedded in the test script for portability. diff --git a/t/t0110/url-1 b/t/t0110/url-1 new file mode 100644 index 0000000000..519019c5ce --- /dev/null +++ b/t/t0110/url-1 @@ -0,0 +1 @@ +x://q/ diff --git a/t/t0110/url-10 b/t/t0110/url-10 new file mode 100644 index 0000000000..b9965de6a5 --- /dev/null +++ b/t/t0110/url-10 @@ -0,0 +1 @@ +x://q/ðñòóôõö÷øùúûüýþÿ diff --git a/t/t0110/url-11 b/t/t0110/url-11 new file mode 100644 index 0000000000..f0a50f1009 --- /dev/null +++ b/t/t0110/url-11 @@ -0,0 +1 @@ +x://q/Â€ß¿à €ï¿½ð€€ð¯¿½ diff --git a/t/t0110/url-2 b/t/t0110/url-2 new file mode 100644 index 0000000000..43334b05b2 --- /dev/null +++ b/t/t0110/url-2 @@ -0,0 +1 @@ +x://q/ diff --git a/t/t0110/url-3 b/t/t0110/url-3 new file mode 100644 index 0000000000..7378c7bec2 --- /dev/null +++ b/t/t0110/url-3 @@ -0,0 +1 @@ +x://q/€‚ƒ„…†‡ˆ‰Š‹ŒŽ diff --git a/t/t0110/url-4 b/t/t0110/url-4 new file mode 100644 index 0000000000..220b198c97 --- /dev/null +++ b/t/t0110/url-4 @@ -0,0 +1 @@ +x://q/‘’“”•–—˜™š›œžŸ diff --git a/t/t0110/url-5 b/t/t0110/url-5 new file mode 100644 index 0000000000..1ccd927779 --- /dev/null +++ b/t/t0110/url-5 @@ -0,0 +1 @@ +x://q/ ¡¢£¤¥¦§¨©ª«¬®¯ diff --git a/t/t0110/url-6 b/t/t0110/url-6 new file mode 100644 index 0000000000..e8283aac6d --- /dev/null +++ b/t/t0110/url-6 @@ -0,0 +1 @@ +x://q/°±²³´µ¶·¸¹º»¼½¾¿ diff --git a/t/t0110/url-7 b/t/t0110/url-7 new file mode 100644 index 0000000000..fa7c10b615 --- /dev/null +++ b/t/t0110/url-7 @@ -0,0 +1 @@ +x://q/ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ diff --git a/t/t0110/url-8 b/t/t0110/url-8 new file mode 100644 index 0000000000..79a0ba836f --- /dev/null +++ b/t/t0110/url-8 @@ -0,0 +1 @@ +x://q/ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß diff --git a/t/t0110/url-9 b/t/t0110/url-9 new file mode 100644 index 0000000000..8b44bec48b --- /dev/null +++ b/t/t0110/url-9 @@ -0,0 +1 @@ +x://q/àáâãäåæçèéêëìíîï diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 4e911fb43d..a420742494 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -78,6 +78,13 @@ $content" echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && test_cmp expect actual ' + + test_expect_success '--batch-check with %(rest)' ' + echo "$type this is some extra content" >expect && + echo "$sha1 this is some extra content" | + git cat-file --batch-check="%(objecttype) %(rest)" >actual && + test_cmp expect actual + ' } hello_content="Hello World" @@ -91,6 +98,14 @@ test_expect_success "setup" ' run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content" +test_expect_success '--batch-check without %(rest) considers whole line' ' + echo "$hello_sha1 blob $hello_size" >expect && + git update-index --add --cacheinfo 100644 $hello_sha1 "white space" && + test_when_finished "git update-index --remove \"white space\"" && + echo ":white space" | git cat-file --batch-check >actual && + test_cmp expect actual +' + tree_sha1=$(git write-tree) tree_size=33 tree_pretty_content="100644 blob $hello_sha1 hello" diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index c4a7d84f46..967359344d 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -652,16 +652,23 @@ test_expect_success numbers ' test_cmp expect actual ' +test_expect_success '--int is at least 64 bits' ' + git config giga.watts 121g && + echo 129922760704 >expect && + git config --int --get giga.watts >actual && + test_cmp expect actual +' + test_expect_success 'invalid unit' ' git config aninvalid.unit "1auto" && echo 1auto >expect && git config aninvalid.unit >actual && test_cmp expect actual && - cat > expect <<-\EOF - fatal: bad config value for '\''aninvalid.unit'\'' in .git/config + cat >expect <<-\EOF + fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit EOF test_must_fail git config --int --get aninvalid.unit 2>actual && - test_cmp actual expect + test_i18ncmp expect actual ' cat > expect << EOF @@ -1087,6 +1094,31 @@ test_expect_success 'barf on incomplete string' ' grep " line 3 " error ' +test_expect_success 'urlmatch' ' + cat >.git/config <<-\EOF && + [http] + sslVerify + [http "https://weak.example.com"] + sslVerify = false + cookieFile = /tmp/cookie.txt + EOF + + echo true >expect && + git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual && + test_cmp expect actual && + + echo false >expect && + git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual && + test_cmp expect actual && + + { + echo http.cookiefile /tmp/cookie.txt && + echo http.sslverify false + } >expect && + git config --get-urlmatch HTTP https://weak.example.com >actual && + test_cmp expect actual +' + # good section hygiene test_expect_failure 'unsetting the last key in a section removes header' ' cat >.git/config <<-\EOF && diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh index 9a105fe21f..6f47c0dd0e 100755 --- a/t/t1411-reflog-show.sh +++ b/t/t1411-reflog-show.sh @@ -144,4 +144,26 @@ test_expect_success 'empty reflog file' ' test_cmp expect actual ' +# This guards against the alternative of showing the diffs vs. the +# reflog ancestor. The reflog used is designed to list the commits +# more than once, so as to exercise the corresponding logic. +test_expect_success 'git log -g -p shows diffs vs. parents' ' + test_commit two && + git branch flipflop && + git update-ref refs/heads/flipflop -m flip1 HEAD^ && + git update-ref refs/heads/flipflop -m flop1 HEAD && + git update-ref refs/heads/flipflop -m flip2 HEAD^ && + git log -g -p flipflop >reflog && + grep -v ^Reflog reflog >actual && + git log -1 -p HEAD^ >log.one && + git log -1 -p HEAD >log.two && + ( + cat log.one; echo + cat log.two; echo + cat log.one; echo + cat log.two + ) >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh index f611d799b6..6d3b828a95 100755 --- a/t/t3010-ls-files-killed-modified.sh +++ b/t/t3010-ls-files-killed-modified.sh @@ -11,6 +11,7 @@ This test prepares the following in the cache: path1 - a symlink path2/file2 - a file in a directory path3/file3 - a file in a directory + pathx/ju - a file in a directory submod1/ - a submodule submod2/ - another submodule @@ -23,6 +24,7 @@ and the following on the filesystem: path4 - a file path5 - a symlink path6/file6 - a file in a directory + pathx/ju/nk - a file in a directory to be killed submod1/ - a submodule (modified from the cache) submod2/ - a submodule (matches the cache) @@ -44,14 +46,15 @@ modified without reporting path9 and path10. submod1 is also modified. test_expect_success 'git update-index --add to add various paths.' ' date >path0 && test_ln_s_add xyzzy path1 && - mkdir path2 path3 && + mkdir path2 path3 pathx && date >path2/file2 && date >path3/file3 && + >pathx/ju && : >path7 && date >path8 && : >path9 && date >path10 && - git update-index --add -- path0 path?/file? path7 path8 path9 path10 && + git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 && for i in 1 2 do git init submod$i && @@ -77,7 +80,7 @@ test_expect_success 'git ls-files -k to show killed files.' ' date >path3 && date >path5 fi && - mkdir path0 path1 path6 && + mkdir -p path0 path1 path6 pathx/ju && date >path0/file0 && date >path1/file1 && date >path6/file6 && @@ -85,16 +88,23 @@ test_expect_success 'git ls-files -k to show killed files.' ' : >path8 && : >path9 && touch path10 && - git ls-files -k >.output -' - -test_expect_success 'validate git ls-files -k output.' ' - cat >.expected <<-\EOF && + >pathx/ju/nk && + cat >.expected <<-\EOF path0/file0 path1/file1 path2 path3 + pathx/ju/nk EOF +' + +test_expect_success 'git ls-files -k output (w/o icase)' ' + git ls-files -k >.output + test_cmp .expected .output +' + +test_expect_success 'git ls-files -k output (w/ icase)' ' + git -c core.ignorecase=true ls-files -k >.output test_cmp .expected .output ' @@ -110,6 +120,7 @@ test_expect_success 'validate git ls-files -m output.' ' path3/file3 path7 path8 + pathx/ju submod1 EOF test_cmp .expected .output diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 49ccb38f88..50e22b1cad 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -29,8 +29,6 @@ Initial setup: . "$TEST_DIRECTORY"/lib-rebase.sh -set_fake_editor - # WARNING: Modifications to the initial repository can change the SHA ID used # in the expect2 file for the 'stop on conflicting pick' test. @@ -72,6 +70,7 @@ export SHELL test_expect_success 'rebase -i with the exec command' ' git checkout master && ( + set_fake_editor && FAKE_LINES="1 exec_>touch-one 2 exec_>touch-two exec_false exec_>touch-three 3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" && @@ -93,6 +92,7 @@ test_expect_success 'rebase -i with the exec command' ' test_expect_success 'rebase -i with the exec command runs from tree root' ' git checkout master && mkdir subdir && (cd subdir && + set_fake_editor && FAKE_LINES="1 exec_>touch-subdir" \ git rebase -i HEAD^ ) && @@ -103,6 +103,7 @@ test_expect_success 'rebase -i with the exec command runs from tree root' ' test_expect_success 'rebase -i with the exec command checks tree cleanness' ' git checkout master && ( + set_fake_editor && FAKE_LINES="exec_echo_foo_>file1 1" && export FAKE_LINES && test_must_fail git rebase -i HEAD^ @@ -116,6 +117,7 @@ test_expect_success 'rebase -i with exec of inexistent command' ' git checkout master && test_when_finished "git rebase --abort" && ( + set_fake_editor && FAKE_LINES="exec_this-command-does-not-exist 1" && export FAKE_LINES && test_must_fail git rebase -i HEAD^ >actual 2>&1 @@ -125,6 +127,7 @@ test_expect_success 'rebase -i with exec of inexistent command' ' test_expect_success 'no changes are a nop' ' git checkout branch2 && + set_fake_editor && git rebase -i F && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" && test $(git rev-parse I) = $(git rev-parse HEAD) @@ -134,6 +137,7 @@ test_expect_success 'test the [branch] option' ' git checkout -b dead-end && git rm file6 && git commit -m "stop here" && + set_fake_editor && git rebase -i F branch2 && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" && test $(git rev-parse I) = $(git rev-parse branch2) && @@ -142,6 +146,7 @@ test_expect_success 'test the [branch] option' ' test_expect_success 'test --onto <branch>' ' git checkout -b test-onto branch2 && + set_fake_editor && git rebase -i --onto branch1 F && test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" && test $(git rev-parse HEAD^) = $(git rev-parse branch1) && @@ -151,6 +156,7 @@ test_expect_success 'test --onto <branch>' ' test_expect_success 'rebase on top of a non-conflicting commit' ' git checkout branch1 && git tag original-branch1 && + set_fake_editor && git rebase -i branch2 && test file6 = $(git diff --name-only original-branch1) && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" && @@ -163,6 +169,7 @@ test_expect_success 'reflog for the branch shows state before rebase' ' ' 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) @@ -188,6 +195,7 @@ EOF test_expect_success 'stop on conflicting pick' ' git tag new-branch1 && + set_fake_editor && test_must_fail git rebase -i master && test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" && test_cmp expect .git/rebase-merge/patch && @@ -208,6 +216,7 @@ test_expect_success 'abort' ' test_expect_success 'abort with error when new base cannot be checked out' ' git rm --cached file1 && git commit -m "remove file in base" && + set_fake_editor && test_must_fail git rebase -i master > output 2>&1 && grep "The following untracked working tree files would be overwritten by checkout:" \ output && @@ -222,6 +231,7 @@ test_expect_success 'retain authorship' ' test_tick && GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && git tag twerp && + set_fake_editor && git rebase -i --onto master HEAD^ && git show HEAD | grep "^Author: Twerp Snog" ' @@ -232,6 +242,7 @@ test_expect_success 'squash' ' test_tick && GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 && echo "******************************" && + set_fake_editor && FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \ git rebase -i --onto master HEAD~2 && test B = $(cat file7) && @@ -244,6 +255,7 @@ test_expect_success 'retain authorship when squashing' ' test_expect_success '-p handles "no changes" gracefully' ' HEAD=$(git rev-parse HEAD) && + set_fake_editor && git rebase -i -p HEAD^ && git update-index --refresh && git diff-files --quiet && @@ -253,6 +265,7 @@ test_expect_success '-p handles "no changes" gracefully' ' test_expect_failure 'exchange two commits with -p' ' git checkout H && + set_fake_editor && FAKE_LINES="2 1" git rebase -i -p HEAD~2 && test H = $(git cat-file commit HEAD^ | sed -ne \$p) && test G = $(git cat-file commit HEAD | sed -ne \$p) @@ -287,6 +300,7 @@ test_expect_success 'preserve merges with -p' ' git commit -m M file1 && git checkout -b to-be-rebased && test_tick && + set_fake_editor && git rebase -i -p --onto branch1 master && git update-index --refresh && git diff-files --quiet && @@ -301,6 +315,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' + set_fake_editor && FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && @@ -314,6 +329,7 @@ test_expect_success 'edit ancestor with -p' ' test_expect_success '--continue tries to commit' ' test_tick && + set_fake_editor && test_must_fail git rebase -i --onto new-branch1 HEAD^ && echo resolved > file1 && git add file1 && @@ -325,6 +341,7 @@ test_expect_success '--continue tries to commit' ' test_expect_success 'verbose flag is heeded, even after --continue' ' git reset --hard master@{1} && test_tick && + set_fake_editor && test_must_fail git rebase -v -i --onto new-branch1 HEAD^ && echo resolved > file1 && git add file1 && @@ -334,6 +351,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' ' test_expect_success 'multi-squash only fires up editor once' ' base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -344,6 +362,7 @@ test_expect_success 'multi-squash only fires up editor once' ' test_expect_success 'multi-fixup does not fire up editor' ' git checkout -b multi-fixup E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \ git rebase -i $base && test $base = $(git rev-parse HEAD^) && @@ -355,6 +374,7 @@ test_expect_success 'multi-fixup does not fire up editor' ' test_expect_success 'commit message used after conflict' ' git checkout -b conflict-fixup conflict-branch && base=$(git rev-parse HEAD~4) && + set_fake_editor && ( FAKE_LINES="1 fixup 3 fixup 4" && export FAKE_LINES && @@ -373,6 +393,7 @@ test_expect_success 'commit message used after conflict' ' test_expect_success 'commit message retained after conflict' ' git checkout -b conflict-squash conflict-branch && base=$(git rev-parse HEAD~4) && + set_fake_editor && ( FAKE_LINES="1 fixup 3 squash 4" && export FAKE_LINES && @@ -399,6 +420,7 @@ EOF test_expect_success 'squash and fixup generate correct log messages' ' git checkout -b squash-fixup E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -411,6 +433,7 @@ test_expect_success 'squash and fixup generate correct log messages' ' test_expect_success 'squash ignores comments' ' git checkout -b skip-comments E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -423,6 +446,7 @@ test_expect_success 'squash ignores comments' ' test_expect_success 'squash ignores blank lines' ' git checkout -b skip-blank-lines E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -435,6 +459,7 @@ test_expect_success 'squash ignores blank lines' ' test_expect_success 'squash works as expected' ' git checkout -b squash-works no-conflict-branch && one=$(git rev-parse HEAD~3) && + set_fake_editor && FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \ git rebase -i HEAD~3 && test $one = $(git rev-parse HEAD~2) @@ -443,6 +468,7 @@ test_expect_success 'squash works as expected' ' test_expect_success 'interrupted squash works as expected' ' git checkout -b interrupted-squash conflict-branch && one=$(git rev-parse HEAD~3) && + set_fake_editor && ( FAKE_LINES="1 squash 3 2" && export FAKE_LINES && @@ -460,6 +486,7 @@ test_expect_success 'interrupted squash works as expected' ' test_expect_success 'interrupted squash works as expected (case 2)' ' git checkout -b interrupted-squash2 conflict-branch && one=$(git rev-parse HEAD~3) && + set_fake_editor && ( FAKE_LINES="3 squash 1 2" && export FAKE_LINES && @@ -484,6 +511,7 @@ test_expect_success '--continue tries to commit, even for "edit"' ' git commit -m "unrelated change" && parent=$(git rev-parse HEAD^) && test_tick && + set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo edited > file7 && git add file7 && @@ -496,6 +524,7 @@ test_expect_success '--continue tries to commit, even for "edit"' ' test_expect_success 'aborted --continue does not squash commits after "edit"' ' old=$(git rev-parse HEAD) && test_tick && + set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo "edited again" > file7 && git add file7 && @@ -510,6 +539,7 @@ test_expect_success 'aborted --continue does not squash commits after "edit"' ' test_expect_success 'auto-amend only edited commits after "edit"' ' test_tick && + set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo "edited again" > file7 && git add file7 && @@ -528,6 +558,7 @@ test_expect_success 'auto-amend only edited commits after "edit"' ' test_expect_success 'clean error after failed "exec"' ' test_tick && test_when_finished "git rebase --abort || :" && + set_fake_editor && ( FAKE_LINES="1 exec_false" && export FAKE_LINES && @@ -543,6 +574,7 @@ test_expect_success 'rebase a detached HEAD' ' grandparent=$(git rev-parse HEAD~2) && git checkout $(git rev-parse HEAD) && test_tick && + set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 && test $grandparent = $(git rev-parse HEAD~2) ' @@ -559,6 +591,7 @@ test_expect_success 'rebase a commit violating pre-commit' ' test_must_fail git commit -m doesnt-verify file1 && git commit -m doesnt-verify --no-verify file1 && test_tick && + set_fake_editor && FAKE_LINES=2 git rebase -i HEAD~2 ' @@ -580,6 +613,7 @@ test_expect_success 'rebase with a file named HEAD in worktree' ' git commit -m "Add body" ) && + set_fake_editor && FAKE_LINES="1 squash 2" git rebase -i to-be-rebased && test "$(git show -s --pretty=format:%an)" = "Squashed Away" @@ -591,6 +625,7 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' ' GIT_EDITOR=: git commit --amend \ --author="Somebody else <somebody@else.com>" && test $(git rev-parse branch3) != $(git rev-parse branch4) && + set_fake_editor && git rebase -i branch3 && test $(git rev-parse branch3) = $(git rev-parse branch4) @@ -615,10 +650,12 @@ test_expect_success 'submodule rebase setup' ' git commit -a -m "submodule second" ) && test_tick && + set_fake_editor && git commit -a -m "Three changes submodule" ' test_expect_success 'submodule rebase -i' ' + set_fake_editor && FAKE_LINES="1 squash 2 3" git rebase -i A ' @@ -636,6 +673,7 @@ test_expect_success 'submodule conflict setup' ' ' test_expect_success 'rebase -i continue with only submodule staged' ' + set_fake_editor && test_must_fail git rebase -i submodule-base && git add sub && git rebase --continue && @@ -645,6 +683,7 @@ test_expect_success 'rebase -i continue with only submodule staged' ' test_expect_success 'rebase -i continue with unstaged submodule' ' git checkout submodule-topic && git reset --hard && + set_fake_editor && test_must_fail git rebase -i submodule-base && git reset && git rebase --continue && @@ -657,6 +696,7 @@ test_expect_success 'avoid unnecessary reset' ' test-chmtime =123456789 file3 && git update-index --refresh && HEAD=$(git rev-parse HEAD) && + set_fake_editor && git rebase -i HEAD~4 && test $HEAD = $(git rev-parse HEAD) && MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') && @@ -665,6 +705,7 @@ test_expect_success 'avoid unnecessary reset' ' test_expect_success 'reword' ' git checkout -b reword-branch master && + set_fake_editor && FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A && git show HEAD | grep "E changed" && test $(git rev-parse master) != $(git rev-parse HEAD) && @@ -684,6 +725,7 @@ test_expect_success 'rebase -i can copy notes' ' test_commit n2 && test_commit n3 && git notes add -m"a note" n3 && + set_fake_editor && git rebase -i --onto n1 n2 && test "a note" = "$(git notes show HEAD)" ' @@ -697,6 +739,7 @@ EOF test_expect_success 'rebase -i can copy notes over a fixup' ' git reset --hard n3 && git notes add -m"an earlier note" n2 && + set_fake_editor && GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 && git notes show > output && test_cmp expect output @@ -706,6 +749,7 @@ test_expect_success 'rebase while detaching HEAD' ' git symbolic-ref HEAD && grandparent=$(git rev-parse HEAD~2) && test_tick && + set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 && test $grandparent = $(git rev-parse HEAD~2) && test_must_fail git symbolic-ref HEAD @@ -715,6 +759,7 @@ test_tick # Ensure that the rebased commits get a different timestamp. test_expect_success 'always cherry-pick with --no-ff' ' git checkout no-ff-branch && git tag original-no-ff-branch && + set_fake_editor && git rebase -i --no-ff A && touch empty && for p in 0 1 2 @@ -747,6 +792,7 @@ test_expect_success 'set up commits with funny messages' ' test_expect_success 'rebase-i history with funny messages' ' git rev-list A..funny >expect && test_tick && + set_fake_editor && FAKE_LINES="1 2 3 4" git rebase -i A && git rev-list A.. >actual && test_cmp expect actual @@ -763,6 +809,7 @@ test_expect_success 'prepare for rebase -i --exec' ' test_expect_success 'running "git rebase -i --exec git show HEAD"' ' + set_fake_editor && git rebase -i --exec "git show HEAD" HEAD~2 >actual && ( FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && @@ -776,6 +823,7 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' ' test_expect_success 'running "git rebase --exec git show HEAD -i"' ' git reset --hard execute && + set_fake_editor && git rebase --exec "git show HEAD" -i HEAD~2 >actual && ( FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && @@ -789,6 +837,7 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' ' test_expect_success 'running "git rebase -ix git show HEAD"' ' git reset --hard execute && + set_fake_editor && git rebase -ix "git show HEAD" HEAD~2 >actual && ( FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && @@ -802,6 +851,7 @@ test_expect_success 'running "git rebase -ix git show HEAD"' ' test_expect_success 'rebase -ix with several <CMD>' ' git reset --hard execute && + set_fake_editor && git rebase -ix "git show HEAD; pwd" HEAD~2 >actual && ( FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" && @@ -815,6 +865,7 @@ test_expect_success 'rebase -ix with several <CMD>' ' test_expect_success 'rebase -ix with several instances of --exec' ' git reset --hard execute && + set_fake_editor && git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual && ( FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2 @@ -836,6 +887,7 @@ test_expect_success 'rebase -ix with --autosquash' ' echo bis >bis.txt && git add bis.txt && git commit -m "fixup! two_exec" && + set_fake_editor && ( git checkout -b autosquash_actual && git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual @@ -854,6 +906,7 @@ test_expect_success 'rebase -ix with --autosquash' ' test_expect_success 'rebase --exec without -i shows error message' ' git reset --hard execute && + set_fake_editor && test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual && echo "The --exec option must be used with the --interactive option" >expected && test_i18ncmp expected actual @@ -862,6 +915,7 @@ test_expect_success 'rebase --exec without -i shows error message' ' test_expect_success 'rebase -i --exec without <CMD>' ' git reset --hard execute && + set_fake_editor && test_must_fail git rebase -i --exec 2>tmp && sed -e "1d" tmp >actual && test_must_fail git rebase -h >expected && @@ -871,6 +925,7 @@ test_expect_success 'rebase -i --exec without <CMD>' ' test_expect_success 'rebase -i --root re-order and drop commits' ' git checkout E && + set_fake_editor && FAKE_LINES="3 1 2 5" git rebase -i --root && test E = $(git cat-file commit HEAD | sed -ne \$p) && test B = $(git cat-file commit HEAD^ | sed -ne \$p) && @@ -884,6 +939,7 @@ test_expect_success 'rebase -i --root retain root commit author and message' ' echo B >file7 && git add file7 && GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && + set_fake_editor && FAKE_LINES="2" git rebase -i --root && git cat-file commit HEAD | grep -q "^author Twerp Snog" && git cat-file commit HEAD | grep -q "^different author$" @@ -892,6 +948,7 @@ test_expect_success 'rebase -i --root retain root commit author and message' ' test_expect_success 'rebase -i --root temporary sentinel commit' ' git checkout B && ( + set_fake_editor && FAKE_LINES="2" && export FAKE_LINES && test_must_fail git rebase -i --root @@ -902,6 +959,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' ' test_expect_success 'rebase -i --root fixup root commit' ' git checkout B && + set_fake_editor && FAKE_LINES="1 fixup 2" git rebase -i --root && test A = $(git cat-file commit HEAD | sed -ne \$p) && test B = $(git show HEAD:file1) && @@ -911,6 +969,7 @@ test_expect_success 'rebase -i --root fixup root commit' ' test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' ' git reset --hard && git checkout conflict-branch && + set_fake_editor && test_must_fail git rebase --onto HEAD~2 HEAD~ && test_must_fail git rebase --edit-todo && git rebase --abort @@ -919,6 +978,7 @@ test_expect_success 'rebase --edit-todo does not works on non-interactive rebase test_expect_success 'rebase --edit-todo can be used to modify todo' ' git reset --hard && git checkout no-conflict-branch^0 && + set_fake_editor && FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 && FAKE_LINES="2 1" git rebase --edit-todo && git rebase --continue @@ -929,6 +989,7 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' ' test_expect_success 'rebase -i produces readable reflog' ' git reset --hard && git branch -f branch-reflog-test H && + set_fake_editor && git rebase -i --onto I F branch-reflog-test && cat >expect <<-\EOF && rebase -i (start): checkout I @@ -976,4 +1037,41 @@ test_expect_success 'rebase -i with --strategy and -X' ' test $(cat file1) = Z ' +test_expect_success 'rebase -i error on commits with \ in message' ' + current_head=$(git rev-parse HEAD) + test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" && + test_commit TO-REMOVE will-conflict old-content && + test_commit "\temp" will-conflict new-content dummy && + ( + EDITOR=true && + export EDITOR && + test_must_fail git rebase -i HEAD^ --onto HEAD^^ 2>error + ) && + test_expect_code 1 grep " emp" error +' + +test_expect_success 'short SHA-1 setup' ' + test_when_finished "git checkout master" && + git checkout --orphan collide && + git rm -rf . && + ( + unset test_tick && + test_commit collide1 collide && + test_commit --notick collide2 collide && + test_commit --notick collide3 collide + ) +' + +test_expect_success 'short SHA-1 collide' ' + test_when_finished "reset_rebase && git checkout master" && + git checkout collide && + ( + unset test_tick && + test_tick && + set_fake_editor && + FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \ + FAKE_LINES="reword 1 2" git rebase -i HEAD~2 + ) +' + test_done diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index 2e0c36415f..8c251c57a6 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -28,6 +28,8 @@ export GIT_AUTHOR_EMAIL # \--A3 <-- topic2 # \ # B2 <-- origin/topic +# +# Clone 4 (same as Clone 3) test_expect_success 'setup for merge-preserving rebase' \ 'echo First > A && @@ -64,6 +66,16 @@ test_expect_success 'setup for merge-preserving rebase' \ git merge --no-ff topic2 ) && + git clone ./. clone4 && + ( + cd clone4 && + git checkout -b topic2 origin/topic && + echo Sixth > A && + git commit -a -m "Modify A3" && + git checkout -b topic origin/topic && + git merge --no-ff topic2 + ) && + git checkout topic && echo Fourth >> B && git commit -a -m "Modify B2" @@ -96,4 +108,15 @@ test_expect_success 'rebase -p preserves no-ff merges' ' ) ' +test_expect_success 'rebase -p ignores merge.log config' ' + ( + cd clone4 && + git fetch && + git -c merge.log=1 rebase -p origin/topic && + echo >expected && + git log --format="%b" -1 >current && + test_cmp expected current + ) +' + test_done diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 5c87b55645..639cb70941 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -263,6 +263,7 @@ test_expect_success 'rm removes subdirectories recursively' ' ' cat >expect <<EOF +M .gitmodules D submod EOF @@ -270,6 +271,15 @@ cat >expect.modified <<EOF M submod EOF +cat >expect.cached <<EOF +D submod +EOF + +cat >expect.both_deleted<<EOF +D .gitmodules +D submod +EOF + test_expect_success 'rm removes empty submodules from work tree' ' mkdir submod && git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod && @@ -281,16 +291,20 @@ test_expect_success 'rm removes empty submodules from work tree' ' git rm submod && test ! -e submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' -test_expect_success 'rm removes removed submodule from index' ' +test_expect_success 'rm removes removed submodule from index and .gitmodules' ' git reset --hard && git submodule update && rm -rf submod && git rm submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm removes work tree of unmodified submodules' ' @@ -299,7 +313,9 @@ test_expect_success 'rm removes work tree of unmodified submodules' ' git rm submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm removes a submodule with a trailing /' ' @@ -333,6 +349,72 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' ' + git reset --hard && + git submodule update && + git rm --cached submod && + test -d submod && + test -f submod/.git && + git status -s -uno >actual && + test_cmp expect.cached actual && + git config -f .gitmodules submodule.sub.url && + git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' ' + git reset --hard && + git submodule update && + git rm -n submod && + test -f submod/.git && + git diff-index --exit-code HEAD +' + +test_expect_success 'rm does not complain when no .gitmodules file is found' ' + git reset --hard && + git submodule update && + git rm .gitmodules && + git rm submod >actual 2>actual.err && + ! test -s actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect.both_deleted actual +' + +test_expect_success 'rm will error out on a modified .gitmodules file unless staged' ' + git reset --hard && + git submodule update && + git config -f .gitmodules foo.bar true && + test_must_fail git rm submod >actual 2>actual.err && + test -s actual.err && + test -d submod && + test -f submod/.git && + git diff-files --quiet -- submod && + git add .gitmodules && + git rm submod >actual 2>actual.err && + ! test -s actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect actual +' + +test_expect_success 'rm issues a warning when section is not found in .gitmodules' ' + git reset --hard && + git submodule update && + git config -f .gitmodules --remove-section submodule.sub && + git add .gitmodules && + echo "warning: Could not find section in .gitmodules where path=submod" >expect.err && + git rm submod >actual 2>actual.err && + test_i18ncmp expect.err actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && test_cmp expect actual ' @@ -427,7 +509,9 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' ' @@ -446,7 +530,9 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f git rm -f submod && test ! -d submod && git status -s -uno --ignore-submodules=none > actual && - test_cmp expect actual + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path ' test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' ' diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 97172b46b2..cd0454356a 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -73,7 +73,7 @@ test_expect_success 'plumbing not affected' ' test_expect_success 'non-integer config parsing' ' git config diff.context no && test_must_fail git diff 2>output && - test_i18ngrep "bad config value" output + test_i18ngrep "bad numeric config value" output ' test_expect_success 'negative integer config parsing' ' diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index baa4685dcc..ce3eace065 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -202,7 +202,8 @@ test_expect_success 'setup mailmap blob tests' ' Blob Guy <author@example.com> Blob Guy <bugs@company.xx> EOF - git add just-bugs both && + printf "Tricky Guy <author@example.com>" >no-newline && + git add just-bugs both no-newline && git commit -m "my mailmaps" && echo "Repo Guy <author@example.com>" >.mailmap && echo "Internal Guy <author@example.com>" >internal.map @@ -286,6 +287,19 @@ test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' ' ) ' +test_expect_success 'mailmap.blob can handle blobs without trailing newline' ' + cat >expect <<-\EOF && + Tricky Guy (1): + initial + + nick1 (1): + second + + EOF + git -c mailmap.blob=map:no-newline shortlog HEAD >actual && + test_cmp expect actual +' + test_expect_success 'cleanup after mailmap.blob tests' ' rm -f .mailmap ' diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 7665d6785c..7369d3c517 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -48,7 +48,7 @@ canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main canned_test "-L 4,12:a.c -L :main:a.c simple" multiple -canned_test "-L 4,18:a.c -L :main:a.c simple" multiple-overlapping +canned_test "-L 4,18:a.c -L ^:main:a.c simple" multiple-overlapping canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset @@ -64,17 +64,34 @@ test_bad_opts "-L 1,1000:b.c" "has only.*lines" test_bad_opts "-L :b.c" "argument.*not of the form" test_bad_opts "-L :foo:b.c" "no match" -# There is a separate bug when an empty -L range is the first -L encountered, -# thus to demonstrate this particular bug, the empty -L range must follow a -# non-empty -L range. -test_expect_success '-L {empty-range} (any -L)' ' +test_expect_success '-L X (X == nlines)' ' + n=$(wc -l <b.c) && + git log -L $n:b.c +' + +test_expect_success '-L X (X == nlines + 1)' ' n=$(expr $(wc -l <b.c) + 1) && - git log -L1,1:b.c -L$n:b.c + test_must_fail git log -L $n:b.c +' + +test_expect_success '-L X (X == nlines + 2)' ' + n=$(expr $(wc -l <b.c) + 2) && + test_must_fail git log -L $n:b.c ' -test_expect_success '-L {empty-range} (first -L)' ' +test_expect_success '-L ,Y (Y == nlines)' ' + n=$(printf "%d" $(wc -l <b.c)) && + git log -L ,$n:b.c +' + +test_expect_success '-L ,Y (Y == nlines + 1)' ' n=$(expr $(wc -l <b.c) + 1) && - git log -L$n:b.c + test_must_fail git log -L ,$n:b.c +' + +test_expect_success '-L ,Y (Y == nlines + 2)' ' + n=$(expr $(wc -l <b.c) + 2) && + test_must_fail git log -L ,$n:b.c ' test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index fd2598e601..a80584ea0e 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -505,4 +505,20 @@ test_expect_success 'test --all, --depth, and explicit tag' ' ) >out-adt 2>error-adt ' +test_expect_success 'shallow fetch with tags does not break the repository' ' + mkdir repo1 && + ( + cd repo1 && + git init && + test_commit 1 && + test_commit 2 && + test_commit 3 && + mkdir repo2 && + cd repo2 && + git init && + git fetch --depth=2 ../.git master:branch && + git fsck + ) +' + test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index fde689166a..1f0f8e6827 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -497,6 +497,88 @@ test_expect_success "should be able to fetch with duplicate refspecs" ' ) ' +# configured prune tests + +set_config_tristate () { + # var=$1 val=$2 + case "$2" in + unset) test_unconfig "$1" ;; + *) git config "$1" "$2" ;; + esac +} + +test_configured_prune () { + fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4 + + test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" ' + # make sure a newbranch is there in . and also in one + git branch -f newbranch && + ( + cd one && + test_unconfig fetch.prune && + test_unconfig remote.origin.prune && + git fetch && + git rev-parse --verify refs/remotes/origin/newbranch + ) + + # now remove it + git branch -d newbranch && + + # then test + ( + cd one && + set_config_tristate fetch.prune $fetch_prune && + set_config_tristate remote.origin.prune $remote_origin_prune && + + git fetch $cmdline && + case "$expected" in + pruned) + test_must_fail git rev-parse --verify refs/remotes/origin/newbranch + ;; + kept) + git rev-parse --verify refs/remotes/origin/newbranch + ;; + esac + ) + ' +} + +test_configured_prune unset unset "" kept +test_configured_prune unset unset "--no-prune" kept +test_configured_prune unset unset "--prune" pruned + +test_configured_prune false unset "" kept +test_configured_prune false unset "--no-prune" kept +test_configured_prune false unset "--prune" pruned + +test_configured_prune true unset "" pruned +test_configured_prune true unset "--prune" pruned +test_configured_prune true unset "--no-prune" kept + +test_configured_prune unset false "" kept +test_configured_prune unset false "--no-prune" kept +test_configured_prune unset false "--prune" pruned + +test_configured_prune false false "" kept +test_configured_prune false false "--no-prune" kept +test_configured_prune false false "--prune" pruned + +test_configured_prune true false "" kept +test_configured_prune true false "--prune" pruned +test_configured_prune true false "--no-prune" kept + +test_configured_prune unset true "" pruned +test_configured_prune unset true "--no-prune" kept +test_configured_prune unset true "--prune" pruned + +test_configured_prune false true "" pruned +test_configured_prune false true "--no-prune" kept +test_configured_prune false true "--prune" pruned + +test_configured_prune true true "" pruned +test_configured_prune true true "--prune" pruned +test_configured_prune true true "--no-prune" kept + test_expect_success 'all boundary commits are excluded' ' test_commit base && test_commit oneside && diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4691d51b8c..99c32d7539 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1172,4 +1172,21 @@ test_expect_success 'push --follow-tag only pushes relevant tags' ' test_cmp expect actual ' +test_expect_success 'push --no-thin must produce non-thin pack' ' + cat >>path1 <<\EOF && +keep base version of path1 big enough, compared to the new changes +later, in order to pass size heuristics in +builtin/pack-objects.c:try_delta() +EOF + git commit -am initial && + git init no-thin && + git --git-dir=no-thin/.git config receive.unpacklimit 0 && + git push no-thin/.git refs/heads/master:refs/heads/foo && + echo modified >> path1 && + git commit -am modified && + git repack -adf && + rcvpck="git receive-pack --reject-thin-pack-for-testing" && + git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo +' + test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index ed4d9c8318..227d293350 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -148,6 +148,95 @@ test_expect_success 'branch.to-rebase.rebase should override pull.rebase' ' test new = $(git show HEAD:file2) ' +# add a feature branch, keep-merge, that is merged into master, so the +# test can try preserving the merge commit (or not) with various +# --rebase flags/pull.rebase settings. +test_expect_success 'preserve merge setup' ' + git reset --hard before-rebase && + git checkout -b keep-merge second^ && + test_commit file3 && + git checkout to-rebase && + git merge keep-merge && + git tag before-preserve-rebase +' + +test_expect_success 'pull.rebase=false create a new merge commit' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase false && + git pull . copy && + test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) && + test $(git rev-parse HEAD^2) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success 'pull.rebase=true flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase true && + git pull . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase 1 && + git pull . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase preserve && + git pull . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge) +' + +test_expect_success 'pull.rebase=invalid fails' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase invalid && + ! git pull . copy +' + +test_expect_success '--rebase=false create a new merge commit' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase true && + git pull --rebase=false . copy && + test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) && + test $(git rev-parse HEAD^2) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success '--rebase=true rebases and flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase preserve && + git pull --rebase=true . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success '--rebase=preserve rebases and merges keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase true && + git pull --rebase=preserve . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge) +' + +test_expect_success '--rebase=invalid fails' ' + git reset --hard before-preserve-rebase && + ! git pull --rebase=invalid . copy +' + +test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase preserve && + git pull --rebase . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + test_expect_success '--rebase with rebased upstream' ' git remote add -f me . && diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh new file mode 100755 index 0000000000..ba20d83333 --- /dev/null +++ b/t/t5533-push-cas.sh @@ -0,0 +1,189 @@ +#!/bin/sh + +test_description='compare & swap push force/delete safety' + +. ./test-lib.sh + +setup_srcdst_basic () { + rm -fr src dst && + git clone --no-local . src && + git clone --no-local src dst && + ( + cd src && git checkout HEAD^0 + ) +} + +test_expect_success setup ' + : create template repository + test_commit A && + test_commit B && + test_commit C +' + +test_expect_success 'push to update (protected)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + test_must_fail git push --force-with-lease=master:master origin master + ) && + git ls-remote . refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (protected, forced)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + git push --force --force-with-lease=master:master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (protected, tracking)' ' + setup_srcdst_basic && + ( + cd src && + git checkout master && + test_commit D && + git checkout HEAD^0 + ) && + git ls-remote src refs/heads/master >expect && + ( + cd dst && + test_commit E && + git ls-remote . refs/remotes/origin/master >expect && + test_must_fail git push --force-with-lease=master origin master && + git ls-remote . refs/remotes/origin/master >actual && + test_cmp expect actual + ) && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (protected, tracking, forced)' ' + setup_srcdst_basic && + ( + cd src && + git checkout master && + test_commit D && + git checkout HEAD^0 + ) && + ( + cd dst && + test_commit E && + git ls-remote . refs/remotes/origin/master >expect && + git push --force --force-with-lease=master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (allowed)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + git push --force-with-lease=master:master^ origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (allowed, tracking)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + git push --force-with-lease=master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (allowed even though no-ff)' ' + setup_srcdst_basic && + ( + cd dst && + git reset --hard HEAD^ && + test_commit D && + git push --force-with-lease=master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to delete (protected)' ' + setup_srcdst_basic && + git ls-remote src refs/heads/master >expect && + ( + cd dst && + test_must_fail git push --force-with-lease=master:master^ origin :master + ) && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to delete (protected, forced)' ' + setup_srcdst_basic && + ( + cd dst && + git push --force --force-with-lease=master:master^ origin :master + ) && + >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to delete (allowed)' ' + setup_srcdst_basic && + ( + cd dst && + git push --force-with-lease=master origin :master + ) && + >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'cover everything with default force-with-lease (protected)' ' + setup_srcdst_basic && + ( + cd src && + git branch naster master^ + ) + git ls-remote src refs/heads/\* >expect && + ( + cd dst && + test_must_fail git push --force-with-lease origin master master:naster + ) && + git ls-remote src refs/heads/\* >actual && + test_cmp expect actual +' + +test_expect_success 'cover everything with default force-with-lease (allowed)' ' + setup_srcdst_basic && + ( + cd src && + git branch naster master^ + ) + ( + cd dst && + git fetch && + git push --force-with-lease origin master master:naster + ) && + git ls-remote dst refs/heads/master | + sed -e "s/master/naster/" >expect && + git ls-remote src refs/heads/naster >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index beb00be4b1..470ac54295 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -153,7 +153,7 @@ test_expect_success 'used receive-pack service' ' ' test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \ - "$ROOT_PATH"/test_repo_clone master + "$ROOT_PATH"/test_repo_clone master success test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' ' # create a dissimilarly-named remote ref so that git is unable to match the diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh index 55a866af80..8196af19f6 100755 --- a/t/t5551-http-fetch.sh +++ b/t/t5551-http-fetch.sh @@ -187,6 +187,22 @@ test_expect_success 'dumb clone via http-backend respects namespace' ' test_cmp expect actual ' +cat >cookies.txt <<EOF +127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue +EOF +cat >expect_cookies.txt <<EOF + +127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue +127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value +EOF +test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' ' + git config http.cookiefile cookies.txt && + git config http.savecookies true && + git ls-remote $HTTPD_URL/smart_cookies/repo.git master && + tail -3 cookies.txt > cookies_tail.txt + test_cmp expect_cookies.txt cookies_tail.txt +' + test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE test_expect_success EXPENSIVE 'create 50,000 tags in the repo' ' diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh new file mode 100755 index 0000000000..878faf2b63 --- /dev/null +++ b/t/t5802-connect-helper.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +test_description='ext::cmd remote "connect" helper' +. ./test-lib.sh + +test_expect_success setup ' + test_tick && + git commit --allow-empty -m initial && + test_tick && + git commit --allow-empty -m second && + test_tick && + git commit --allow-empty -m third && + test_tick && + git tag -a -m "tip three" three && + + test_tick && + git commit --allow-empty -m fourth +' + +test_expect_success clone ' + cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") && + git clone "ext::sh -c %S% ." dst && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git config remote.origin.url "ext::sh -c $cmd" && + git for-each-ref refs/heads/ refs/tags/ + ) >actual && + test_cmp expect actual +' + +test_expect_success 'update following tag' ' + test_tick && + git commit --allow-empty -m fifth && + test_tick && + git tag -a -m "tip five" five && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git pull && + git for-each-ref refs/heads/ refs/tags/ >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'update backfilled tag' ' + test_tick && + git commit --allow-empty -m sixth && + test_tick && + git tag -a -m "tip two" two three^1 && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git pull && + git for-each-ref refs/heads/ refs/tags/ >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'update backfilled tag without primary transfer' ' + test_tick && + git tag -a -m "tip one " one two^1 && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git pull && + git for-each-ref refs/heads/ refs/tags/ >../actual + ) && + test_cmp expect actual +' + +test_done diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index 57ce2395d6..fde5e712eb 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -127,4 +127,10 @@ test_expect_success 'full history simplification without parent' ' } ' +test_expect_success '--full-diff is not affected by --parents' ' + git log -p --pretty="%H" --full-diff -- file >expected && + git log -p --pretty="%H" --full-diff --parents -- file >actual && + test_cmp expected actual +' + test_done diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh index 39ef61994f..ea00d71e77 100755 --- a/t/t6130-pathspec-noglob.sh +++ b/t/t6130-pathspec-noglob.sh @@ -32,6 +32,16 @@ test_expect_success 'star pathspec globs' ' test_cmp expect actual ' +test_expect_success 'star pathspec globs' ' + cat >expect <<-\EOF && + bracket + star + vanilla + EOF + git log --format=%s -- ":(glob)f*" >actual && + test_cmp expect actual +' + test_expect_success 'bracket pathspec globs and matches literal brackets' ' cat >expect <<-\EOF && bracket @@ -41,28 +51,105 @@ test_expect_success 'bracket pathspec globs and matches literal brackets' ' test_cmp expect actual ' +test_expect_success 'bracket pathspec globs and matches literal brackets' ' + cat >expect <<-\EOF && + bracket + vanilla + EOF + git log --format=%s -- ":(glob)f[o][o]" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob option matches literally (vanilla)' ' echo vanilla >expect && git --literal-pathspecs log --format=%s -- foo >actual && test_cmp expect actual ' +test_expect_success 'no-glob option matches literally (vanilla)' ' + echo vanilla >expect && + git log --format=%s -- ":(literal)foo" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob option matches literally (star)' ' echo star >expect && git --literal-pathspecs log --format=%s -- "f*" >actual && test_cmp expect actual ' +test_expect_success 'no-glob option matches literally (star)' ' + echo star >expect && + git log --format=%s -- ":(literal)f*" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob option matches literally (bracket)' ' echo bracket >expect && git --literal-pathspecs log --format=%s -- "f[o][o]" >actual && test_cmp expect actual ' +test_expect_success 'no-glob option matches literally (bracket)' ' + echo bracket >expect && + git log --format=%s -- ":(literal)f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option disables :(literal)' ' + : >expect && + git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual && + test_cmp expect actual +' + test_expect_success 'no-glob environment variable works' ' echo star >expect && GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual && test_cmp expect actual ' +test_expect_success 'setup xxx/bar' ' + mkdir xxx && + test_commit xxx xxx/bar +' + +test_expect_success '**/ works with :(glob)' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git log --format=%s -- ":(glob)**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ does not work with --noglob-pathspecs' ' + : >expect && + git --noglob-pathspecs log --format=%s -- "**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ works with :(glob) and --noglob-pathspecs' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git --noglob-pathspecs log --format=%s -- ":(glob)**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ works with --glob-pathspecs' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git --glob-pathspecs log --format=%s -- "**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' ' + : >expect && + git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh new file mode 100755 index 0000000000..8d4a7fcb91 --- /dev/null +++ b/t/t6131-pathspec-icase.sh @@ -0,0 +1,103 @@ +#!/bin/sh + +test_description='test case insensitive pathspec limiting' +. ./test-lib.sh + +if test_have_prereq CASE_INSENSITIVE_FS +then + skip_all='skipping case sensitive tests - case insensitive file system' + test_done +fi + +test_expect_success 'create commits with glob characters' ' + test_commit bar bar && + test_commit bAr bAr && + test_commit BAR BAR && + mkdir foo && + test_commit foo/bar foo/bar && + test_commit foo/bAr foo/bAr && + test_commit foo/BAR foo/BAR && + mkdir fOo && + test_commit fOo/bar fOo/bar && + test_commit fOo/bAr fOo/bAr && + test_commit fOo/BAR fOo/BAR && + mkdir FOO && + test_commit FOO/bar FOO/bar && + test_commit FOO/bAr FOO/bAr && + test_commit FOO/BAR FOO/BAR +' + +test_expect_success 'tree_entry_interesting matches bar' ' + echo bar >expect && + git log --format=%s -- "bar" >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar' ' + cat <<-EOF >expect && + BAR + bAr + bar + EOF + git log --format=%s -- ":(icase)bar" >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar with prefix' ' + cat <<-EOF >expect && + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git log --format=%s -- ":(icase)bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefix' ' + cat <<-EOF >expect && + FOO/BAR + FOO/bAr + FOO/bar + fOo/BAR + fOo/bAr + fOo/bar + foo/BAR + foo/bAr + foo/bar + EOF + ( cd fOo && git log --format=%s -- ":(icase)../foo/bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar' ' + cat <<-EOF >expect && + BAR + bAr + bar + EOF + git ls-files ":(icase)bar" >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' ' + cat <<-EOF >expect && + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git ls-files --full-name ":(icase)bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' ' + cat <<-EOF >expect && + bar + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git ls-files --full-name ":(icase)bar" ../bar ) >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 101816e718..d432f42bcb 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -259,4 +259,132 @@ test_expect_success SYMLINKS 'check moved symlink' ' rm -f moved symlink +test_expect_success 'setup submodule' ' + git commit -m initial && + git reset --hard && + git submodule add ./. sub && + echo content >file && + git add file && + git commit -m "added sub and file" +' + +test_expect_success 'git mv cannot move a submodule in a file' ' + test_must_fail git mv sub file +' + +test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' ' + entry="$(git ls-files --stage sub | cut -f 1)" && + git rm .gitmodules && + ( + cd sub && + rm -f .git && + cp -a ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + mkdir mod && + git mv sub mod/sub && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'git mv moves a submodule with gitfile' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + entry="$(git ls-files --stage sub | cut -f 1)" && + ( + cd mod && + git mv ../sub/ . + ) && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + echo mod/sub >expected && + git config -f .gitmodules submodule.sub.path >actual && + test_cmp expected actual && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv does not complain when no .gitmodules file is found' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git rm .gitmodules && + entry="$(git ls-files --stage sub | cut -f 1)" && + git mv sub mod/sub 2>actual.err && + ! test -s actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv will error out on a modified .gitmodules file unless staged' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git config -f .gitmodules foo.bar true && + entry="$(git ls-files --stage sub | cut -f 1)" && + test_must_fail git mv sub mod/sub 2>actual.err && + test -s actual.err && + test -e sub && + git diff-files --quiet -- sub && + git add .gitmodules && + git mv sub mod/sub 2>actual.err && + ! test -s actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv issues a warning when section is not found in .gitmodules' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git config -f .gitmodules --remove-section submodule.sub && + git add .gitmodules && + entry="$(git ls-files --stage sub | cut -f 1)" && + echo "warning: Could not find section in .gitmodules where path=sub" >expect.err && + git mv sub mod/sub 2>actual.err && + test_i18ncmp expect.err actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git mv -n sub mod/sub 2>actual.err && + test -f sub/.git && + git diff-index --exit-code HEAD && + git update-index --refresh && + git diff-files --quiet -- sub .gitmodules +' + test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 5ee97b003a..4192fe0ec6 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -18,6 +18,16 @@ test_expect_success 'setup - initial commit' ' git branch initial ' +test_expect_success 'configuration parsing' ' + test_when_finished "rm -f .gitmodules" && + cat >.gitmodules <<-\EOF && + [submodule "s"] + path + ignore + EOF + test_must_fail git status +' + test_expect_success 'setup - repository in init subdirectory' ' mkdir init && ( @@ -773,13 +783,11 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano test_cmp expect .git ) && echo "repo" >expect && - git config -f .gitmodules submodule.repo.path >actual && - test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.repo.path && git config -f .gitmodules submodule.repo_new.path >actual && test_cmp expect actual&& echo "$submodurl/repo" >expect && - git config -f .gitmodules submodule.repo.url >actual && - test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.repo.url && echo "$submodurl/bare.git" >expect && git config -f .gitmodules submodule.repo_new.url >actual && test_cmp expect actual && @@ -799,12 +807,8 @@ test_expect_success 'submodule add with an existing name fails unless forced' ' git rm repo && test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo && test ! -d repo && - echo "repo" >expect && - git config -f .gitmodules submodule.repo_new.path >actual && - test_cmp expect actual&& - echo "$submodurl/bare.git" >expect && - git config -f .gitmodules submodule.repo_new.url >actual && - test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.repo_new.path && + test_must_fail git config -f .gitmodules submodule.repo_new.url && echo "$submodurl/bare.git" >expect && git config submodule.repo_new.url >actual && test_cmp expect actual && diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index d526b1d96a..05d9db090d 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -253,7 +253,7 @@ test_expect_success 'deleted vs modified submodule' ' git checkout -b test6 branch1 && git submodule update -N && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && git commit -m "Submodule deleted from branch" && git checkout -b test6.a test6 && test_must_fail git merge master && @@ -322,7 +322,7 @@ test_expect_success 'file vs modified submodule' ' git checkout -b test7 branch1 && git submodule update -N && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && echo not a submodule >submod && git add submod && git commit -m "Submodule path becomes file" && @@ -453,7 +453,7 @@ test_expect_success 'submodule in subdirectory' ' test_expect_success 'directory vs modified submodule' ' git checkout -b test11 branch1 && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && mkdir submod && echo not a submodule >submod/file16 && git add submod/file16 && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index ac6f3b6af2..31a770d9bc 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -1031,6 +1031,32 @@ test_expect_success \ git diff-tree -M -r M3^ M3 >actual && compare_diff_raw expect actual' +cat >input <<INPUT_END +commit refs/heads/M4 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +rename root +COMMIT + +from refs/heads/M2^0 +R "" sub + +INPUT_END + +cat >expect <<EOF +:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2/oldf sub/file2/oldf +:100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100 file4 sub/file4 +:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you sub/i/am/new/to/you +:100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100 newdir/exec.sh sub/newdir/exec.sh +:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting sub/newdir/interesting +EOF +test_expect_success \ + 'M: rename root to subdirectory' \ + 'git fast-import <input && + git diff-tree -M -r M4^ M4 >actual && + cat actual && + compare_diff_raw expect actual' + ### ### series N ### @@ -1228,6 +1254,29 @@ test_expect_success \ compare_diff_raw expect actual' test_expect_success \ + 'N: copy root by path' \ + 'cat >expect <<-\EOF && + :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf oldroot/file2/newf + :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf oldroot/file2/oldf + :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100 file4 oldroot/file4 + :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100 newdir/exec.sh oldroot/newdir/exec.sh + :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting oldroot/newdir/interesting + EOF + cat >input <<-INPUT_END && + commit refs/heads/N-copy-root-path + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + copy root directory by (empty) path + COMMIT + + from refs/heads/branch^0 + C "" oldroot + INPUT_END + git fast-import <input && + git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual && + compare_diff_raw expect actual' + +test_expect_success \ 'N: delete directory by copying' \ 'cat >expect <<-\EOF && OBJID @@ -2934,4 +2983,20 @@ test_expect_success 'S: ls with garbage after sha1 must fail' ' test_i18ngrep "space after tree-ish" err ' +### +### series T (ls) +### +# Setup is carried over from series S. + +test_expect_success 'T: ls root tree' ' + sed -e "s/Z\$//" >expect <<-EOF && + 040000 tree $(git rev-parse S^{tree}) Z + EOF + sha1=$(git rev-parse --verify S) && + git fast-import --import-marks=marks <<-EOF >actual && + ls $sha1 "" + EOF + test_cmp expect actual +' + test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 272a071e85..2d4beb5e50 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -69,7 +69,7 @@ run_completion () local -a COMPREPLY _words local _cword _words=( $1 ) - test "${1: -1}" = ' ' && _words+=('') + test "${1: -1}" = ' ' && _words[${#_words[@]}+1]='' (( _cword = ${#_words[@]} - 1 )) __git_wrap__git_main && print_comp } diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 3c3e4e8c38..59f875e830 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -61,6 +61,29 @@ test_expect_success 'prompt - unborn branch' ' test_cmp expected "$actual" ' +repo_with_newline='repo +with +newline' + +if mkdir "$repo_with_newline" 2>/dev/null +then + test_set_prereq FUNNYNAMES +else + say 'Your filesystem does not allow newlines in filenames.' +fi + +test_expect_success FUNNYNAMES 'prompt - with newline in path' ' + printf " (master)" >expected && + git init "$repo_with_newline" && + test_when_finished "rm -rf \"$repo_with_newline\"" && + mkdir "$repo_with_newline"/subdir && + ( + cd "$repo_with_newline/subdir" && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + test_expect_success 'prompt - detached head' ' printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected && test_config core.abbrev 13 && diff --git a/templates/hooks--pre-push.sample b/templates/hooks--pre-push.sample index 15ab6d8e7e..1f3bcebfd7 100755 --- a/templates/hooks--pre-push.sample +++ b/templates/hooks--pre-push.sample @@ -30,6 +30,7 @@ do if [ "$local_sha" = $z40 ] then # Handle delete + : else if [ "$remote_sha" = $z40 ] then diff --git a/test-urlmatch-normalization.c b/test-urlmatch-normalization.c new file mode 100644 index 0000000000..090bf219a7 --- /dev/null +++ b/test-urlmatch-normalization.c @@ -0,0 +1,50 @@ +#include "git-compat-util.h" +#include "urlmatch.h" + +int main(int argc, char **argv) +{ + const char usage[] = "test-urlmatch-normalization [-p | -l] <url1> | <url1> <url2>"; + char *url1, *url2; + int opt_p = 0, opt_l = 0; + + /* + * For one url, succeed if url_normalize succeeds on it, fail otherwise. + * For two urls, succeed only if url_normalize succeeds on both and + * the results compare equal with strcmp. If -p is given (one url only) + * and url_normalize succeeds, print the result followed by "\n". If + * -l is given (one url only) and url_normalize succeeds, print the + * returned length in decimal followed by "\n". + */ + + if (argc > 1 && !strcmp(argv[1], "-p")) { + opt_p = 1; + argc--; + argv++; + } else if (argc > 1 && !strcmp(argv[1], "-l")) { + opt_l = 1; + argc--; + argv++; + } + + if (argc < 2 || argc > 3) + die("%s", usage); + + if (argc == 2) { + struct url_info info; + url1 = url_normalize(argv[1], &info); + if (!url1) + return 1; + if (opt_p) + printf("%s\n", url1); + if (opt_l) + printf("%u\n", (unsigned)info.url_len); + return 0; + } + + if (opt_p || opt_l) + die("%s", usage); + + url1 = url_normalize(argv[1], NULL); + url2 = url_normalize(argv[2], NULL); + return (url1 && url2 && !strcmp(url1, url2)) ? 0 : 1; +} diff --git a/transport-helper.c b/transport-helper.c index 63cabc37e3..4c2a39e8ff 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -27,6 +27,7 @@ struct helper_data { push : 1, connect : 1, signed_tags : 1, + check_connectivity : 1, no_disconnect_req : 1; char *export_marks; char *import_marks; @@ -186,6 +187,8 @@ static struct child_process *get_helper(struct transport *transport) data->bidi_import = 1; else if (!strcmp(capname, "export")) data->export = 1; + else if (!strcmp(capname, "check-connectivity")) + data->check_connectivity = 1; else if (!data->refspecs && !prefixcmp(capname, "refspec ")) { ALLOC_GROW(refspecs, refspec_nr + 1, @@ -349,6 +352,9 @@ static int fetch_with_fetch(struct transport *transport, struct strbuf buf = STRBUF_INIT; standard_options(transport); + if (data->check_connectivity && + data->transport_options.check_self_contained_and_connected) + set_helper_option(transport, "check-connectivity", "true"); for (i = 0; i < nr_heads; i++) { const struct ref *posn = to_fetch[i]; @@ -372,6 +378,10 @@ static int fetch_with_fetch(struct transport *transport, else transport->pack_lockfile = xstrdup(name); } + else if (data->check_connectivity && + data->transport_options.check_self_contained_and_connected && + !strcmp(buf.buf, "connectivity-ok")) + data->transport_options.self_contained_and_connected = 1; else if (!buf.len) break; else @@ -683,6 +693,11 @@ static int push_update_ref_status(struct strbuf *buf, free(msg); msg = NULL; } + else if (!strcmp(msg, "stale info")) { + status = REF_STATUS_REJECT_STALE; + free(msg); + msg = NULL; + } } if (*ref) @@ -737,13 +752,15 @@ static void push_update_refs_status(struct helper_data *data, } static int push_refs_with_push(struct transport *transport, - struct ref *remote_refs, int flags) + struct ref *remote_refs, int flags) { int force_all = flags & TRANSPORT_PUSH_FORCE; int mirror = flags & TRANSPORT_PUSH_MIRROR; struct helper_data *data = transport->data; struct strbuf buf = STRBUF_INIT; struct ref *ref; + struct string_list cas_options = STRING_LIST_INIT_DUP; + struct string_list_item *cas_option; get_helper(transport); if (!data->push) @@ -756,6 +773,7 @@ static int push_refs_with_push(struct transport *transport, /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_STALE: case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_UPTODATE: continue; @@ -778,11 +796,29 @@ static int push_refs_with_push(struct transport *transport, strbuf_addch(&buf, ':'); strbuf_addstr(&buf, ref->name); strbuf_addch(&buf, '\n'); + + /* + * The "--force-with-lease" options without explicit + * values to expect have already been expanded into + * the ref->old_sha1_expect[] field; we can ignore + * transport->smart_options->cas altogether and instead + * can enumerate them from the refs. + */ + if (ref->expect_old_sha1) { + struct strbuf cas = STRBUF_INIT; + strbuf_addf(&cas, "%s:%s", + ref->name, sha1_to_hex(ref->old_sha1_expect)); + string_list_append(&cas_options, strbuf_detach(&cas, NULL)); + } } - if (buf.len == 0) + if (buf.len == 0) { + string_list_clear(&cas_options, 0); return 0; + } standard_options(transport); + for_each_string_list_item(cas_option, &cas_options) + set_helper_option(transport, "cas", cas_option->string); if (flags & TRANSPORT_PUSH_DRY_RUN) { if (set_helper_option(transport, "dry-run", "true") != 0) diff --git a/transport.c b/transport.c index e15db9808c..7202b7777d 100644 --- a/transport.c +++ b/transport.c @@ -3,6 +3,8 @@ #include "run-command.h" #include "pkt-line.h" #include "fetch-pack.h" +#include "remote.h" +#include "connect.h" #include "send-pack.h" #include "walker.h" #include "bundle.h" @@ -707,6 +709,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i print_ref_status('!', "[rejected]", ref, ref->peer_ref, "needs force", porcelain); break; + case REF_STATUS_REJECT_STALE: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "stale info", porcelain); + break; case REF_STATUS_REMOTE_REJECT: print_ref_status('!', "[remote rejected]", ref, ref->deletion ? NULL : ref->peer_ref, @@ -875,6 +881,8 @@ void transport_take_over(struct transport *transport, transport->push_refs = git_transport_push; transport->disconnect = disconnect_git; transport->smart_options = &(data->options); + + transport->cannot_reuse = 1; } static int is_local(const char *url) @@ -1076,6 +1084,7 @@ static int run_pre_push_hook(struct transport *transport, for (r = remote_refs; r; r = r->next) { if (!r->peer_ref) continue; if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue; + if (r->status == REF_STATUS_REJECT_STALE) continue; if (r->status == REF_STATUS_UPTODATE) continue; strbuf_reset(&buf); @@ -1140,6 +1149,12 @@ int transport_push(struct transport *transport, return -1; } + if (transport->smart_options && + transport->smart_options->cas && + !is_empty_cas(transport->smart_options->cas)) + apply_push_cas(transport->smart_options->cas, + transport->remote, remote_refs); + set_ref_status_for_push(remote_refs, flags & TRANSPORT_PUSH_MIRROR, flags & TRANSPORT_PUSH_FORCE); diff --git a/transport.h b/transport.h index ea70ea7e4a..8f96bed775 100644 --- a/transport.h +++ b/transport.h @@ -2,6 +2,7 @@ #define TRANSPORT_H #include "cache.h" +#include "run-command.h" #include "remote.h" struct git_transport_options { @@ -13,6 +14,7 @@ struct git_transport_options { int depth; const char *uploadpack; const char *receivepack; + struct push_cas_option *cas; }; struct transport { @@ -27,6 +29,12 @@ struct transport { */ unsigned got_remote_refs : 1; + /* + * Transports that call take-over destroys the data specific to + * the transport type while doing so, and cannot be reused. + */ + unsigned cannot_reuse : 1; + /** * Returns 0 if successful, positive if the option is not * recognized or is inapplicable, and negative if the option @@ -126,6 +134,9 @@ struct transport *transport_get(struct remote *, const char *); /* Transfer the data as a thin pack if not null */ #define TRANS_OPT_THIN "thin" +/* Check the current value of the remote ref */ +#define TRANS_OPT_CAS "cas" + /* Keep the pack that was transferred if not null */ #define TRANS_OPT_KEEP "keep" diff --git a/tree-diff.c b/tree-diff.c index ba01563a02..ccf9d7c8fd 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -138,7 +138,6 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, /* Enable recursion indefinitely */ opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE); - opt->pathspec.max_depth = -1; strbuf_init(&base, PATH_MAX); strbuf_add(&base, base_str, baselen); @@ -196,9 +195,27 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co struct diff_options diff_opts; struct diff_queue_struct *q = &diff_queued_diff; struct diff_filepair *choice; - const char *paths[1]; int i; + /* + * follow-rename code is very specific, we need exactly one + * path. Magic that matches more than one path is not + * supported. + */ + GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); +#if 0 + /* + * We should reject wildcards as well. Unfortunately we + * haven't got a reliable way to detect that 'foo\*bar' in + * fact has no wildcards. nowildcard_len is merely a hint for + * optimization. Let it slip for now until wildmatch is taught + * about dry-run mode and returns wildcard info. + */ + if (opt->pathspec.has_wildcard) + die("BUG:%s:%d: wildcards are not supported", + __FILE__, __LINE__); +#endif + /* Remove the file creation entry from the diff queue, and remember it */ choice = q->queue[0]; q->nr = 0; @@ -207,15 +224,13 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co DIFF_OPT_SET(&diff_opts, RECURSIVE); DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_opts.single_follow = opt->pathspec.raw[0]; + diff_opts.single_follow = opt->pathspec.items[0].match; diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; - paths[0] = NULL; - diff_tree_setup_paths(paths, &diff_opts); diff_setup_done(&diff_opts); diff_tree(t1, t2, base, &diff_opts); diffcore_std(&diff_opts); - diff_tree_release_paths(&diff_opts); + free_pathspec(&diff_opts.pathspec); /* Go through the new set of filepairing, and see if we find a more interesting one */ opt->found_follow = 0; @@ -228,15 +243,18 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co * the future! */ if ((p->status == 'R' || p->status == 'C') && - !strcmp(p->two->path, opt->pathspec.raw[0])) { + !strcmp(p->two->path, opt->pathspec.items[0].match)) { + const char *path[2]; + /* Switch the file-pairs around */ q->queue[i] = choice; choice = p; /* Update the path we use from now on.. */ - diff_tree_release_paths(opt); - opt->pathspec.raw[0] = xstrdup(p->one->path); - diff_tree_setup_paths(opt->pathspec.raw, opt); + path[0] = p->one->path; + path[1] = NULL; + free_pathspec(&opt->pathspec); + parse_pathspec(&opt->pathspec, PATHSPEC_ALL_MAGIC, 0, "", path); /* * The caller expects us to return a set of vanilla @@ -310,13 +328,3 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_ free(tree); return retval; } - -void diff_tree_release_paths(struct diff_options *opt) -{ - free_pathspec(&opt->pathspec); -} - -void diff_tree_setup_paths(const char **p, struct diff_options *opt) -{ - init_pathspec(&opt->pathspec, p); -} diff --git a/tree-walk.c b/tree-walk.c index c626135234..5ece8c3477 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -3,6 +3,7 @@ #include "unpack-trees.h" #include "dir.h" #include "tree.h" +#include "pathspec.h" static const char *get_mode(const char *str, unsigned int *modep) { @@ -487,13 +488,25 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch return retval; } -static int match_entry(const struct name_entry *entry, int pathlen, +static int match_entry(const struct pathspec_item *item, + const struct name_entry *entry, int pathlen, const char *match, int matchlen, enum interesting *never_interesting) { int m = -1; /* signals that we haven't called strncmp() */ - if (*never_interesting != entry_not_interesting) { + if (item->magic & PATHSPEC_ICASE) + /* + * "Never interesting" trick requires exact + * matching. We could do something clever with inexact + * matching, but it's trickier (and not to forget that + * strcasecmp is locale-dependent, at least in + * glibc). Just disable it for now. It can't be worse + * than the wildcard's codepath of '[Tt][Hi][Is][Ss]' + * pattern. + */ + *never_interesting = entry_not_interesting; + else if (*never_interesting != entry_not_interesting) { /* * We have not seen any match that sorts later * than the current path. @@ -539,7 +552,7 @@ static int match_entry(const struct name_entry *entry, int pathlen, * we cheated and did not do strncmp(), so we do * that here. */ - m = strncmp(match, entry->path, pathlen); + m = ps_strncmp(item, match, entry->path, pathlen); /* * If common part matched earlier then it is a hit, @@ -547,15 +560,39 @@ static int match_entry(const struct name_entry *entry, int pathlen, * leading directory and is shorter than match. */ if (!m) + /* + * match_entry does not check if the prefix part is + * matched case-sensitively. If the entry is a + * directory and part of prefix, it'll be rematched + * eventually by basecmp with special treatment for + * the prefix. + */ return 1; return 0; } -static int match_dir_prefix(const char *base, +/* :(icase)-aware string compare */ +static int basecmp(const struct pathspec_item *item, + const char *base, const char *match, int len) +{ + if (item->magic & PATHSPEC_ICASE) { + int ret, n = len > item->prefix ? item->prefix : len; + ret = strncmp(base, match, n); + if (ret) + return ret; + base += n; + match += n; + len -= n; + } + return ps_strncmp(item, base, match, len); +} + +static int match_dir_prefix(const struct pathspec_item *item, + const char *base, const char *match, int matchlen) { - if (strncmp(base, match, matchlen)) + if (basecmp(item, base, match, matchlen)) return 0; /* @@ -592,7 +629,7 @@ static int match_wildcard_base(const struct pathspec_item *item, */ if (baselen >= matchlen) { *matched = matchlen; - return !strncmp(base, match, matchlen); + return !basecmp(item, base, match, matchlen); } dirlen = matchlen; @@ -605,7 +642,7 @@ static int match_wildcard_base(const struct pathspec_item *item, * base ends with '/' so we are sure it really matches * directory */ - if (strncmp(base, match, baselen)) + if (basecmp(item, base, match, baselen)) return 0; *matched = baselen; } else @@ -634,8 +671,17 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, enum interesting never_interesting = ps->has_wildcard ? entry_not_interesting : all_entries_not_interesting; + GUARD_PATHSPEC(ps, + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ICASE); + if (!ps->nr) { - if (!ps->recursive || ps->max_depth == -1) + if (!ps->recursive || + !(ps->magic & PATHSPEC_MAXDEPTH) || + ps->max_depth == -1) return all_entries_interesting; return within_depth(base->buf + base_offset, baselen, !!S_ISDIR(entry->mode), @@ -653,10 +699,12 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, if (baselen >= matchlen) { /* If it doesn't match, move along... */ - if (!match_dir_prefix(base_str, match, matchlen)) + if (!match_dir_prefix(item, base_str, match, matchlen)) goto match_wildcards; - if (!ps->recursive || ps->max_depth == -1) + if (!ps->recursive || + !(ps->magic & PATHSPEC_MAXDEPTH) || + ps->max_depth == -1) return all_entries_interesting; return within_depth(base_str + matchlen + 1, @@ -667,15 +715,14 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, } /* Either there must be no base, or the base must match. */ - if (baselen == 0 || !strncmp(base_str, match, baselen)) { - if (match_entry(entry, pathlen, + if (baselen == 0 || !basecmp(item, base_str, match, baselen)) { + if (match_entry(item, entry, pathlen, match + baselen, matchlen - baselen, &never_interesting)) return entry_interesting; if (item->nowildcard_len < item->len) { - if (!git_fnmatch(match + baselen, entry->path, - item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + if (!git_fnmatch(item, match + baselen, entry->path, item->nowildcard_len - baselen)) return entry_interesting; @@ -716,8 +763,7 @@ match_wildcards: strbuf_add(base, entry->path, pathlen); - if (!git_fnmatch(match, base->buf + base_offset, - item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + if (!git_fnmatch(item, match, base->buf + base_offset, item->nowildcard_len)) { strbuf_setlen(base, base_offset + baselen); return entry_interesting; @@ -47,7 +47,7 @@ static int read_one_entry_quick(const unsigned char *sha1, const char *base, int } static int read_tree_1(struct tree *tree, struct strbuf *base, - int stage, struct pathspec *pathspec, + int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context) { struct tree_desc desc; @@ -116,7 +116,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base, int read_tree_recursive(struct tree *tree, const char *base, int baselen, - int stage, struct pathspec *pathspec, + int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context) { struct strbuf sb = STRBUF_INIT; @@ -25,7 +25,7 @@ typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const ch extern int read_tree_recursive(struct tree *tree, const char *base, int baselen, - int stage, struct pathspec *pathspec, + int stage, const struct pathspec *pathspec, read_tree_fn_t fn, void *context); extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec); diff --git a/unpack-trees.c b/unpack-trees.c index bf01717015..1a61e6f363 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1154,8 +1154,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->src_index = NULL; ret = check_updates(o) ? (-2) : 0; - if (o->dst_index) + if (o->dst_index) { + discard_index(o->dst_index); *o->dst_index = o->result; + } done: clear_exclude_list(&el); diff --git a/upload-pack.c b/upload-pack.c index 127e59a603..b03492e664 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -10,6 +10,7 @@ #include "revision.h" #include "list-objects.h" #include "run-command.h" +#include "connect.h" #include "sigchain.h" #include "version.h" #include "string-list.h" diff --git a/urlmatch.c b/urlmatch.c new file mode 100644 index 0000000000..1db76c89bc --- /dev/null +++ b/urlmatch.c @@ -0,0 +1,535 @@ +#include "cache.h" +#include "urlmatch.h" + +#define URL_ALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +#define URL_DIGIT "0123456789" +#define URL_ALPHADIGIT URL_ALPHA URL_DIGIT +#define URL_SCHEME_CHARS URL_ALPHADIGIT "+.-" +#define URL_HOST_CHARS URL_ALPHADIGIT ".-[:]" /* IPv6 literals need [:] */ +#define URL_UNSAFE_CHARS " <>\"%{}|\\^`" /* plus 0x00-0x1F,0x7F-0xFF */ +#define URL_GEN_RESERVED ":/?#[]@" +#define URL_SUB_RESERVED "!$&'()*+,;=" +#define URL_RESERVED URL_GEN_RESERVED URL_SUB_RESERVED /* only allowed delims */ + +static int append_normalized_escapes(struct strbuf *buf, + const char *from, + size_t from_len, + const char *esc_extra, + const char *esc_ok) +{ + /* + * Append to strbuf 'buf' characters from string 'from' with length + * 'from_len' while unescaping characters that do not need to be escaped + * and escaping characters that do. The set of characters to escape + * (the complement of which is unescaped) starts out as the RFC 3986 + * unsafe characters (0x00-0x1F,0x7F-0xFF," <>\"#%{}|\\^`"). If + * 'esc_extra' is not NULL, those additional characters will also always + * be escaped. If 'esc_ok' is not NULL, those characters will be left + * escaped if found that way, but will not be unescaped otherwise (used + * for delimiters). If a %-escape sequence is encountered that is not + * followed by 2 hexadecimal digits, the sequence is invalid and + * false (0) will be returned. Otherwise true (1) will be returned for + * success. + * + * Note that all %-escape sequences will be normalized to UPPERCASE + * as indicated in RFC 3986. Unless included in esc_extra or esc_ok + * alphanumerics and "-._~" will always be unescaped as per RFC 3986. + */ + + while (from_len) { + int ch = *from++; + int was_esc = 0; + + from_len--; + if (ch == '%') { + if (from_len < 2 || + !isxdigit((unsigned char)from[0]) || + !isxdigit((unsigned char)from[1])) + return 0; + ch = hexval_table[(unsigned char)*from++] << 4; + ch |= hexval_table[(unsigned char)*from++]; + from_len -= 2; + was_esc = 1; + } + if ((unsigned char)ch <= 0x1F || (unsigned char)ch >= 0x7F || + strchr(URL_UNSAFE_CHARS, ch) || + (esc_extra && strchr(esc_extra, ch)) || + (was_esc && strchr(esc_ok, ch))) + strbuf_addf(buf, "%%%02X", (unsigned char)ch); + else + strbuf_addch(buf, ch); + } + + return 1; +} + +char *url_normalize(const char *url, struct url_info *out_info) +{ + /* + * Normalize NUL-terminated url using the following rules: + * + * 1. Case-insensitive parts of url will be converted to lower case + * 2. %-encoded characters that do not need to be will be unencoded + * 3. Characters that are not %-encoded and must be will be encoded + * 4. All %-encodings will be converted to upper case hexadecimal + * 5. Leading 0s are removed from port numbers + * 6. If the default port for the scheme is given it will be removed + * 7. A path part (including empty) not starting with '/' has one added + * 8. Any dot segments (. or ..) in the path are resolved and removed + * 9. IPv6 host literals are allowed (but not normalized or validated) + * + * The rules are based on information in RFC 3986. + * + * Please note this function requires a full URL including a scheme + * and host part (except for file: URLs which may have an empty host). + * + * The return value is a newly allocated string that must be freed + * or NULL if the url is not valid. + * + * If out_info is non-NULL, the url and err fields therein will always + * be set. If a non-NULL value is returned, it will be stored in + * out_info->url as well, out_info->err will be set to NULL and the + * other fields of *out_info will also be filled in. If a NULL value + * is returned, NULL will be stored in out_info->url and out_info->err + * will be set to a brief, translated, error message, but no other + * fields will be filled in. + * + * This is NOT a URL validation function. Full URL validation is NOT + * performed. Some invalid host names are passed through this function + * undetected. However, most all other problems that make a URL invalid + * will be detected (including a missing host for non file: URLs). + */ + + size_t url_len = strlen(url); + struct strbuf norm; + size_t spanned; + size_t scheme_len, user_off=0, user_len=0, passwd_off=0, passwd_len=0; + size_t host_off=0, host_len=0, port_len=0, path_off, path_len, result_len; + const char *slash_ptr, *at_ptr, *colon_ptr, *path_start; + char *result; + + /* + * Copy lowercased scheme and :// suffix, %-escapes are not allowed + * First character of scheme must be URL_ALPHA + */ + spanned = strspn(url, URL_SCHEME_CHARS); + if (!spanned || !isalpha(url[0]) || spanned + 3 > url_len || + url[spanned] != ':' || url[spanned+1] != '/' || url[spanned+2] != '/') { + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid URL scheme name or missing '://' suffix"); + } + return NULL; /* Bad scheme and/or missing "://" part */ + } + strbuf_init(&norm, url_len); + scheme_len = spanned; + spanned += 3; + url_len -= spanned; + while (spanned--) + strbuf_addch(&norm, tolower(*url++)); + + + /* + * Copy any username:password if present normalizing %-escapes + */ + at_ptr = strchr(url, '@'); + slash_ptr = url + strcspn(url, "/?#"); + if (at_ptr && at_ptr < slash_ptr) { + user_off = norm.len; + if (at_ptr > url) { + if (!append_normalized_escapes(&norm, url, at_ptr - url, + "", URL_RESERVED)) { + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid %XX escape sequence"); + } + strbuf_release(&norm); + return NULL; + } + colon_ptr = strchr(norm.buf + scheme_len + 3, ':'); + if (colon_ptr) { + passwd_off = (colon_ptr + 1) - norm.buf; + passwd_len = norm.len - passwd_off; + user_len = (passwd_off - 1) - (scheme_len + 3); + } else { + user_len = norm.len - (scheme_len + 3); + } + } + strbuf_addch(&norm, '@'); + url_len -= (++at_ptr - url); + url = at_ptr; + } + + + /* + * Copy the host part excluding any port part, no %-escapes allowed + */ + if (!url_len || strchr(":/?#", *url)) { + /* Missing host invalid for all URL schemes except file */ + if (strncmp(norm.buf, "file:", 5)) { + if (out_info) { + out_info->url = NULL; + out_info->err = _("missing host and scheme is not 'file:'"); + } + strbuf_release(&norm); + return NULL; + } + } else { + host_off = norm.len; + } + colon_ptr = slash_ptr - 1; + while (colon_ptr > url && *colon_ptr != ':' && *colon_ptr != ']') + colon_ptr--; + if (*colon_ptr != ':') { + colon_ptr = slash_ptr; + } else if (!host_off && colon_ptr < slash_ptr && colon_ptr + 1 != slash_ptr) { + /* file: URLs may not have a port number */ + if (out_info) { + out_info->url = NULL; + out_info->err = _("a 'file:' URL may not have a port number"); + } + strbuf_release(&norm); + return NULL; + } + spanned = strspn(url, URL_HOST_CHARS); + if (spanned < colon_ptr - url) { + /* Host name has invalid characters */ + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid characters in host name"); + } + strbuf_release(&norm); + return NULL; + } + while (url < colon_ptr) { + strbuf_addch(&norm, tolower(*url++)); + url_len--; + } + + + /* + * Check the port part and copy if not the default (after removing any + * leading 0s); no %-escapes allowed + */ + if (colon_ptr < slash_ptr) { + /* skip the ':' and leading 0s but not the last one if all 0s */ + url++; + url += strspn(url, "0"); + if (url == slash_ptr && url[-1] == '0') + url--; + if (url == slash_ptr) { + /* Skip ":" port with no number, it's same as default */ + } else if (slash_ptr - url == 2 && + !strncmp(norm.buf, "http:", 5) && + !strncmp(url, "80", 2)) { + /* Skip http :80 as it's the default */ + } else if (slash_ptr - url == 3 && + !strncmp(norm.buf, "https:", 6) && + !strncmp(url, "443", 3)) { + /* Skip https :443 as it's the default */ + } else { + /* + * Port number must be all digits with leading 0s removed + * and since all the protocols we deal with have a 16-bit + * port number it must also be in the range 1..65535 + * 0 is not allowed because that means "next available" + * on just about every system and therefore cannot be used + */ + unsigned long pnum = 0; + spanned = strspn(url, URL_DIGIT); + if (spanned < slash_ptr - url) { + /* port number has invalid characters */ + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid port number"); + } + strbuf_release(&norm); + return NULL; + } + if (slash_ptr - url <= 5) + pnum = strtoul(url, NULL, 10); + if (pnum == 0 || pnum > 65535) { + /* port number not in range 1..65535 */ + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid port number"); + } + strbuf_release(&norm); + return NULL; + } + strbuf_addch(&norm, ':'); + strbuf_add(&norm, url, slash_ptr - url); + port_len = slash_ptr - url; + } + url_len -= slash_ptr - colon_ptr; + url = slash_ptr; + } + if (host_off) + host_len = norm.len - host_off; + + + /* + * Now copy the path resolving any . and .. segments being careful not + * to corrupt the URL by unescaping any delimiters, but do add an + * initial '/' if it's missing and do normalize any %-escape sequences. + */ + path_off = norm.len; + path_start = norm.buf + path_off; + strbuf_addch(&norm, '/'); + if (*url == '/') { + url++; + url_len--; + } + for (;;) { + const char *seg_start = norm.buf + norm.len; + const char *next_slash = url + strcspn(url, "/?#"); + int skip_add_slash = 0; + /* + * RFC 3689 indicates that any . or .. segments should be + * unescaped before being checked for. + */ + if (!append_normalized_escapes(&norm, url, next_slash - url, "", + URL_RESERVED)) { + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid %XX escape sequence"); + } + strbuf_release(&norm); + return NULL; + } + if (!strcmp(seg_start, ".")) { + /* ignore a . segment; be careful not to remove initial '/' */ + if (seg_start == path_start + 1) { + strbuf_setlen(&norm, norm.len - 1); + skip_add_slash = 1; + } else { + strbuf_setlen(&norm, norm.len - 2); + } + } else if (!strcmp(seg_start, "..")) { + /* + * ignore a .. segment and remove the previous segment; + * be careful not to remove initial '/' from path + */ + const char *prev_slash = norm.buf + norm.len - 3; + if (prev_slash == path_start) { + /* invalid .. because no previous segment to remove */ + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid '..' path segment"); + } + strbuf_release(&norm); + return NULL; + } + while (*--prev_slash != '/') {} + if (prev_slash == path_start) { + strbuf_setlen(&norm, prev_slash - norm.buf + 1); + skip_add_slash = 1; + } else { + strbuf_setlen(&norm, prev_slash - norm.buf); + } + } + url_len -= next_slash - url; + url = next_slash; + /* if the next char is not '/' done with the path */ + if (*url != '/') + break; + url++; + url_len--; + if (!skip_add_slash) + strbuf_addch(&norm, '/'); + } + path_len = norm.len - path_off; + + + /* + * Now simply copy the rest, if any, only normalizing %-escapes and + * being careful not to corrupt the URL by unescaping any delimiters. + */ + if (*url) { + if (!append_normalized_escapes(&norm, url, url_len, "", URL_RESERVED)) { + if (out_info) { + out_info->url = NULL; + out_info->err = _("invalid %XX escape sequence"); + } + strbuf_release(&norm); + return NULL; + } + } + + + result = strbuf_detach(&norm, &result_len); + if (out_info) { + out_info->url = result; + out_info->err = NULL; + out_info->url_len = result_len; + out_info->scheme_len = scheme_len; + out_info->user_off = user_off; + out_info->user_len = user_len; + out_info->passwd_off = passwd_off; + out_info->passwd_len = passwd_len; + out_info->host_off = host_off; + out_info->host_len = host_len; + out_info->port_len = port_len; + out_info->path_off = path_off; + out_info->path_len = path_len; + } + return result; +} + +static size_t url_match_prefix(const char *url, + const char *url_prefix, + size_t url_prefix_len) +{ + /* + * url_prefix matches url if url_prefix is an exact match for url or it + * is a prefix of url and the match ends on a path component boundary. + * Both url and url_prefix are considered to have an implicit '/' on the + * end for matching purposes if they do not already. + * + * url must be NUL terminated. url_prefix_len is the length of + * url_prefix which need not be NUL terminated. + * + * The return value is the length of the match in characters (including + * the final '/' even if it's implicit) or 0 for no match. + * + * Passing NULL as url and/or url_prefix will always cause 0 to be + * returned without causing any faults. + */ + if (!url || !url_prefix) + return 0; + if (!url_prefix_len || (url_prefix_len == 1 && *url_prefix == '/')) + return (!*url || *url == '/') ? 1 : 0; + if (url_prefix[url_prefix_len - 1] == '/') + url_prefix_len--; + if (strncmp(url, url_prefix, url_prefix_len)) + return 0; + if ((strlen(url) == url_prefix_len) || (url[url_prefix_len] == '/')) + return url_prefix_len + 1; + return 0; +} + +int match_urls(const struct url_info *url, + const struct url_info *url_prefix, + int *exactusermatch) +{ + /* + * url_prefix matches url if the scheme, host and port of url_prefix + * are the same as those of url and the path portion of url_prefix + * is the same as the path portion of url or it is a prefix that + * matches at a '/' boundary. If url_prefix contains a user name, + * that must also exactly match the user name in url. + * + * If the user, host, port and path match in this fashion, the returned + * value is the length of the path match including any implicit + * final '/'. For example, "http://me@example.com/path" is matched by + * "http://example.com" with a path length of 1. + * + * If there is a match and exactusermatch is not NULL, then + * *exactusermatch will be set to true if both url and url_prefix + * contained a user name or false if url_prefix did not have a + * user name. If there is no match *exactusermatch is left untouched. + */ + int usermatched = 0; + int pathmatchlen; + + if (!url || !url_prefix || !url->url || !url_prefix->url) + return 0; + + /* check the scheme */ + if (url_prefix->scheme_len != url->scheme_len || + strncmp(url->url, url_prefix->url, url->scheme_len)) + return 0; /* schemes do not match */ + + /* check the user name if url_prefix has one */ + if (url_prefix->user_off) { + if (!url->user_off || url->user_len != url_prefix->user_len || + strncmp(url->url + url->user_off, + url_prefix->url + url_prefix->user_off, + url->user_len)) + return 0; /* url_prefix has a user but it's not a match */ + usermatched = 1; + } + + /* check the host and port */ + if (url_prefix->host_len != url->host_len || + strncmp(url->url + url->host_off, + url_prefix->url + url_prefix->host_off, url->host_len)) + return 0; /* host names and/or ports do not match */ + + /* check the path */ + pathmatchlen = url_match_prefix( + url->url + url->path_off, + url_prefix->url + url_prefix->path_off, + url_prefix->url_len - url_prefix->path_off); + + if (pathmatchlen && exactusermatch) + *exactusermatch = usermatched; + return pathmatchlen; +} + +int urlmatch_config_entry(const char *var, const char *value, void *cb) +{ + struct string_list_item *item; + struct urlmatch_config *collect = cb; + struct urlmatch_item *matched; + struct url_info *url = &collect->url; + const char *key, *dot; + struct strbuf synthkey = STRBUF_INIT; + size_t matched_len = 0; + int user_matched = 0; + int retval; + + key = skip_prefix(var, collect->section); + if (!key || *(key++) != '.') { + if (collect->cascade_fn) + return collect->cascade_fn(var, value, cb); + return 0; /* not interested */ + } + dot = strrchr(key, '.'); + if (dot) { + char *config_url, *norm_url; + struct url_info norm_info; + + config_url = xmemdupz(key, dot - key); + norm_url = url_normalize(config_url, &norm_info); + free(config_url); + if (!norm_url) + return 0; + matched_len = match_urls(url, &norm_info, &user_matched); + free(norm_url); + if (!matched_len) + return 0; + key = dot + 1; + } + + if (collect->key && strcmp(key, collect->key)) + return 0; + + item = string_list_insert(&collect->vars, key); + if (!item->util) { + matched = xcalloc(1, sizeof(*matched)); + item->util = matched; + } else { + matched = item->util; + /* + * Is our match shorter? Is our match the same + * length, and without user while the current + * candidate is with user? Then we cannot use it. + */ + if (matched_len < matched->matched_len || + ((matched_len == matched->matched_len) && + (!user_matched && matched->user_matched))) + return 0; + /* Otherwise, replace it with this one. */ + } + + matched->matched_len = matched_len; + matched->user_matched = user_matched; + strbuf_addstr(&synthkey, collect->section); + strbuf_addch(&synthkey, '.'); + strbuf_addstr(&synthkey, key); + retval = collect->collect_fn(synthkey.buf, value, collect->cb); + + strbuf_release(&synthkey); + return retval; +} diff --git a/urlmatch.h b/urlmatch.h new file mode 100644 index 0000000000..b461dfd3df --- /dev/null +++ b/urlmatch.h @@ -0,0 +1,54 @@ +#ifndef URL_MATCH_H +#include "string-list.h" + +struct url_info { + /* normalized url on success, must be freed, otherwise NULL */ + char *url; + /* if !url, a brief reason for the failure, otherwise NULL */ + const char *err; + + /* the rest of the fields are only set if url != NULL */ + + size_t url_len; /* total length of url (which is now normalized) */ + size_t scheme_len; /* length of scheme name (excluding final :) */ + size_t user_off; /* offset into url to start of user name (0 => none) */ + size_t user_len; /* length of user name; if user_off != 0 but + user_len == 0, an empty user name was given */ + size_t passwd_off; /* offset into url to start of passwd (0 => none) */ + size_t passwd_len; /* length of passwd; if passwd_off != 0 but + passwd_len == 0, an empty passwd was given */ + size_t host_off; /* offset into url to start of host name (0 => none) */ + size_t host_len; /* length of host name; this INCLUDES any ':portnum'; + * file urls may have host_len == 0 */ + size_t port_len; /* if a portnum is present (port_len != 0), it has + * this length (excluding the leading ':') at the + * end of the host name (always 0 for file urls) */ + size_t path_off; /* offset into url to the start of the url path; + * this will always point to a '/' character + * after the url has been normalized */ + size_t path_len; /* length of path portion excluding any trailing + * '?...' and '#...' portion; will always be >= 1 */ +}; + +extern char *url_normalize(const char *, struct url_info *); +extern int match_urls(const struct url_info *url, const struct url_info *url_prefix, int *exactusermatch); + +struct urlmatch_item { + size_t matched_len; + char user_matched; +}; + +struct urlmatch_config { + struct string_list vars; + struct url_info url; + const char *section; + const char *key; + + void *cb; + int (*collect_fn)(const char *var, const char *value, void *cb); + int (*cascade_fn)(const char *var, const char *value, void *cb); +}; + +extern int urlmatch_config_entry(const char *var, const char *value, void *cb); + +#endif /* URL_MATCH_H */ @@ -131,6 +131,14 @@ void *xcalloc(size_t nmemb, size_t size) } /* + * Limit size of IO chunks, because huge chunks only cause pain. OS X + * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in + * the absense of bugs, large chunks can result in bad latencies when + * you decide to kill the process. + */ +#define MAX_IO_SIZE (8*1024*1024) + +/* * xread() is the same a read(), but it automatically restarts read() * operations with a recoverable error (EAGAIN and EINTR). xread() * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. @@ -138,6 +146,8 @@ void *xcalloc(size_t nmemb, size_t size) ssize_t xread(int fd, void *buf, size_t len) { ssize_t nr; + if (len > MAX_IO_SIZE) + len = MAX_IO_SIZE; while (1) { nr = read(fd, buf, len); if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) @@ -154,6 +164,8 @@ ssize_t xread(int fd, void *buf, size_t len) ssize_t xwrite(int fd, const void *buf, size_t len) { ssize_t nr; + if (len > MAX_IO_SIZE) + len = MAX_IO_SIZE; while (1) { nr = write(fd, buf, len); if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) diff --git a/wt-status.c b/wt-status.c index cb24f1fa9b..ff4b32426a 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "pathspec.h" #include "wt-status.h" #include "object.h" #include "dir.h" @@ -438,7 +439,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s) } rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; - init_pathspec(&rev.prune_data, s->pathspec); + copy_pathspec(&rev.prune_data, &s->pathspec); run_diff_files(&rev, 0); } @@ -463,22 +464,20 @@ static void wt_status_collect_changes_index(struct wt_status *s) rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; - init_pathspec(&rev.prune_data, s->pathspec); + copy_pathspec(&rev.prune_data, &s->pathspec); run_diff_index(&rev, 1); } static void wt_status_collect_changes_initial(struct wt_status *s) { - struct pathspec pathspec; int i; - init_pathspec(&pathspec, s->pathspec); for (i = 0; i < active_nr; i++) { struct string_list_item *it; struct wt_status_change_data *d; const struct cache_entry *ce = active_cache[i]; - if (!ce_path_match(ce, &pathspec)) + if (!ce_path_match(ce, &s->pathspec)) continue; it = string_list_insert(&s->change, ce->name); d = it->util; @@ -493,7 +492,6 @@ static void wt_status_collect_changes_initial(struct wt_status *s) else d->index_status = DIFF_STATUS_ADDED; } - free_pathspec(&pathspec); } static void wt_status_collect_untracked(struct wt_status *s) @@ -516,12 +514,12 @@ static void wt_status_collect_untracked(struct wt_status *s) dir.flags |= DIR_SHOW_IGNORED_TOO; setup_standard_excludes(&dir); - fill_directory(&dir, s->pathspec); + fill_directory(&dir, &s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; if (cache_name_is_other(ent->name, ent->len) && - match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL)) string_list_insert(&s->untracked, ent->name); free(ent); } @@ -529,7 +527,7 @@ static void wt_status_collect_untracked(struct wt_status *s) for (i = 0; i < dir.ignored_nr; i++) { struct dir_entry *ent = dir.ignored[i]; if (cache_name_is_other(ent->name, ent->len) && - match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + match_pathspec_depth(&s->pathspec, ent->name, ent->len, 0, NULL)) string_list_insert(&s->ignored, ent->name); free(ent); } diff --git a/wt-status.h b/wt-status.h index fb7152e187..9966c13deb 100644 --- a/wt-status.h +++ b/wt-status.h @@ -44,7 +44,7 @@ struct wt_status { int is_initial; char *branch; const char *reference; - const char **pathspec; + struct pathspec pathspec; int verbose; int amend; enum commit_whence whence; |