diff options
227 files changed, 7877 insertions, 2639 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.4.txt b/Documentation/RelNotes/1.8.4.txt index f7bff4d635..02f681b710 100644 --- a/Documentation/RelNotes/1.8.4.txt +++ b/Documentation/RelNotes/1.8.4.txt @@ -267,7 +267,7 @@ Performance, Internal Implementation, etc. been susceptible to lossage of refs under right conditions, which has been tightened up. - * We read loose and packed rerferences in two steps, but after + * We read loose and packed references in two steps, but after deciding to read a loose ref but before actually opening it to read it, another process racing with us can unlink it, which would cause us to barf. The codepath has been updated to retry when such a @@ -379,7 +379,7 @@ details). * "git apply" parsed patches that add new files, generated by programs other than Git, incorrectly. This is an old breakage in - v1.7.11 and will need to be merged down to the maintanance tracks. + v1.7.11 and will need to be merged down to the maintenance tracks. * Older cURL wanted piece of memory we call it with to be stable, but we updated the auth material after handing it to a call. 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 3bdd56e8f4..c4f0ed5957 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,6 +43,11 @@ unreleased) version of Git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: +* link:v1.8.4/git.html[documentation for release 1.8.4] + +* release notes for + link:RelNotes/1.8.4.txt[1.8.4]. + * link:v1.8.3.4/git.html[documentation for release 1.8.3.4] * release notes for @@ -452,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 ------------ @@ -818,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 @@ -862,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 572dfeb6e0..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-rc4 +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 8b182d2a9f..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) { @@ -157,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; @@ -365,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; @@ -524,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 */ @@ -1306,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; @@ -1340,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'; } } @@ -1042,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); @@ -1063,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); } @@ -1246,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; @@ -1273,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 b66c632621..967359344d 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1094,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/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; |