diff options
171 files changed, 4134 insertions, 1602 deletions
diff --git a/.gitignore b/.gitignore index bb5c91e712..68fe464090 100644 --- a/.gitignore +++ b/.gitignore @@ -189,6 +189,7 @@ /test-mktemp /test-parse-options /test-path-utils +/test-regex /test-revision-walking /test-run-command /test-sha1 @@ -18,6 +18,7 @@ David Kågedal <davidk@lysator.liu.se> David S. Miller <davem@davemloft.net> Deskin Miller <deskinm@umich.edu> Dirk Süsserott <newsletter@dirk.my1.cc> +Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com> Fredrik Kuivinen <freku045@student.liu.se> H. Peter Anvin <hpa@bonde.sc.orionmulti.com> H. Peter Anvin <hpa@tazenda.sc.orionmulti.com> @@ -26,6 +27,9 @@ Horst H. von Brand <vonbrand@inf.utfsm.cl> İsmail Dönmez <ismail@pardus.org.tr> Jay Soffian <jaysoffian+git@gmail.com> Joachim Berdal Haga <cjhaga@fys.uio.no> +Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at> +Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net> +Johannes Sixt <j6t@kdbg.org> <J.Sixt@eudaptics.com> Jon Loeliger <jdl@freescale.com> Jon Seymour <jon@blackcubes.dyndns.org> Jonathan Nieder <jrnieder@uchicago.edu> @@ -43,6 +47,7 @@ Lars Doelle <lars.doelle@on-line.de> Li Hong <leehong@pku.edu.cn> Lukas Sandström <lukass@etek.chalmers.se> Martin Langhoff <martin@laptop.org> +Martin von Zweigbergk <martinvonz@gmail.com> <martin.von.zweigbergk@gmail.com> Michael Coleman <tutufan@gmail.com> Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm> Michael W. Olson <mwolson@gnu.org> @@ -51,7 +56,10 @@ Nanako Shiraishi <nanako3@bluebottle.com> Nanako Shiraishi <nanako3@lavabit.com> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> <nico@fluxnic.net> <nico@cam.org> +Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se> +Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se> Philippe Bruhat <book@cpan.org> +Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com> Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk> René Scharfe <rene.scharfe@lsrfire.ath.cx> Robert Fitzsimons <robfitz@273k.net> @@ -61,6 +69,7 @@ Sean Estabrooks <seanlkml@sympatico.ca> Shawn O. Pearce <spearce@spearce.org> Steven Grimm <koreth@midwinter.com> Theodore Ts'o <tytso@mit.edu> +Thomas Rast <trast@inf.ethz.ch> <trast@student.ethz.ch> Tony Luck <tony.luck@intel.com> Uwe Kleine-König <Uwe_Zeisberger@digi.com> Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com> diff --git a/Documentation/RelNotes/1.8.0.txt b/Documentation/RelNotes/1.8.0.txt new file mode 100644 index 0000000000..689c3874e1 --- /dev/null +++ b/Documentation/RelNotes/1.8.0.txt @@ -0,0 +1,165 @@ +Git v1.8.0 Release Notes +======================== + +Backward compatibility notes +---------------------------- + +In the next major release, we will change the behaviour of the "git +push" command. When "git push [$there]" does not say what to push, we +have used the traditional "matching" semantics (all your branches were +sent to the remote as long as there already are branches of the same +name over there). We will use the "simple" semantics, that pushes the +current branch to the branch with the same name only when the current +branch is set to integrate with that remote branch. There is a user +preference configuration variable "push.default" to change this, and +"git push" will warn about the upcoming change until you set this +variable. + +"git branch --set-upstream" is deprecated and may be removed in a +relatively distant future. "git branch [-u|--set-upstream-to]" has +been introduced with a saner order of arguments. + + +Updates since v1.7.12 +--------------------- + +UI, Workflows & Features + + * A credential helper for Win32 to allow access to the keychain of + the logged-in user has been added. + + * A credential helper to allow access to the Gnome keyring has been + added. + + * It was tempting to say "git branch --set-upstream origin/master", + but that tells Git to arrange the local branch "origin/master" to + integrate with the currently checked out branch, which is highly + unlikely what the user meant. The option is deprecated; use the + new "--set-upstream-to" (with a short-and-sweet "-u") option + instead. + + * "git cherry-pick" learned the "--allow-empty-message" option to + allow it to replay a commit without any log message. + + * "git daemon" learned the "--access-hook" option to allow an + external command to decline service based on the client address, + repository path, etc. + + * "git difftool --dir-diff" learned to use symbolic links to prepare + temporary copy of the working tree when available. + + * "git grep" learned to use a non-standard pattern type by default if + a configuration variable tells it to. + + * "git merge-base" learned "--is-ancestor A B" option to tell if A is + an ancestor of B. The result is indicated by its exit status code. + + +Foreign Interface + + * "git svn" has been updated to work with SVN 1.7. + +Performance, Internal Implementation, etc. (please report possible regressions) + + * Git ships with a fall-back regexp implementation for platforms with + buggy regexp library, but it was easy for people to keep using their + platform regexp. A new test has been added to check this. + + * The "check-docs" build target has been updated and greatly + simplified. + + * The documentation in the TeXinfo format was using indented output + for materials meant to be examples that are better typeset in + monospace. + + * Compatibility wrapper around some mkdir(2) implementations that + reject parameter with trailing slash has been introduced. + + * Many internal uses of "git merge-base" equivalent were only to see + if one commit fast-forwards to the other, which did not need the + full set of merge bases to be computed. They have been updated to + use less expensive checks. + + * The heuristics to detect and silently convert latin1 to utf8 when + we were told to use utf-8 in the log message has been transplanted + from "mailinfo" to "commit" and "commit-tree". + + * Messages given by "git <subcommand> -h" from many subcommands have + been marked for translation. + +Also contains minor documentation updates and code clean-ups. + + +Fixes since v1.7.12 +------------------- + +Unless otherwise noted, all the fixes since v1.7.12 in the +maintenance track are contained in this release (see release notes +to them for details). + + * "git show --format='%ci'" did not give timestamp correctly for + commits created without human readable name on "committer" line. + (merge e27ddb6 jc/maint-ident-missing-human-name later to maint). + + * "git cherry-pick A C B" used to replay changes in A and then B and + then C if these three commits had committer timestamps in that + order, which is not what the user who said "A C B" naturally + expects. + (merge a73e22e mz/cherry-pick-cmdline-order later to maint). + + * "git show --quiet" ought to be a synonym for "git show -s", but + wasn't. + (merge f9c75d8 jk/maint-quiet-is-synonym-to-s-in-log later to maint). + + * "git p4", when "--use-client-spec" and "--detect-branches" are used + together, misdetected branches. + (merge 21ef5df pw/p4-use-client-spec-branch-detection later to maint). + + * Output from "git branch -v" contains "(no branch)" that could be + localized, but the code to align it along with the names of + branches were counting in bytes, not in display columns. + (merge 1452bd6 nd/branch-v-alignment later to maint). + + * "git for-each-ref" did not currectly support more than one --sort + option. + (merge 3b51222 kk/maint-for-each-ref-multi-sort later to maint). + + * Pushing to smart HTTP server with recent Git fails without having + the username in the URL to force authentication, if the server is + configured to allow GET anonymously, while requiring authentication + for POST. + (merge b81401c jk/maint-http-half-auth-push later to maint). + + * When looking for $HOME/.gitconfig etc., it is OK if we cannot read + them because they do not exist, but we did not diagnose existing + files that we cannot read. + + * The synopsis said "checkout [-B branch]" to make it clear the + branch name is a parameter to the option, but the heading for the + option description was "-B::", not "-B branch::", making the + documentation misleading. There may be room in documentation pages + of other commands for similar improvements (hint, hint). + (merge 45aaf03 jc/maint-doc-checkout-b-always-takes-branch-name later to maint). + + * "git log .." errored out saying it is both rev range and a path + when there is no disambiguating "--" is on the command line. + Update the command line parser to interpret ".." as a path in such + a case. + (merge 003c84f jc/dotdot-is-parent-directory later to maint). + + * "git apply -p0" did not parse pathnames on "diff --git" line + correctly. This caused patches that had pathnames in no other + places to be mistakenly rejected (most notably, binary patch that + does not rename nor change mode). Textual patches, renames or mode + changes have preimage and postimage pathnames in different places + in a form that can be parsed unambiguously and did not suffer from + this problem. + (merge 6a2abdc jc/apply-binary-p0 later to maint). + + * The interactive prompt "git send-email" gives was error prone. It + asked "What e-mail address do you want to use?" with the address it + guessed (correctly) the user would want to use in its prompt, + tempting the user to say "y". But the response was taken as "No, + please use 'y' as the e-mail address instead", which is most + certainly not what the user meant. + (merge 51bbccf jc/send-email-reconfirm later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index a95e5a4ac9..6416cae511 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1210,8 +1210,16 @@ gitweb.snapshot:: grep.lineNumber:: If set to true, enable '-n' option by default. +grep.patternType:: + Set the default matching behavior. Using a value of 'basic', 'extended', + 'fixed', or 'perl' will enable the '--basic-regexp', '--extended-regexp', + '--fixed-strings', or '--perl-regexp' option accordingly, while the + value 'default' will return to the default matching behavior. + grep.extendedRegexp:: - If set to true, enable '--extended-regexp' option by default. + If set to true, enable '--extended-regexp' option by default. This + option is ignored when the 'grep.patternType' option is set to a value + other than 'default'. gpg.program:: Use this custom program instead of "gpg" found on $PATH when diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 47235bea04..9c1d2f1781 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -13,6 +13,8 @@ SYNOPSIS [--column[=<options>] | --no-column] [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] +'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>] +'git branch' --unset-upstream [<branchname>] 'git branch' (-m | -M) [<oldbranch>] <newbranch> 'git branch' (-d | -D) [-r] <branchname>... 'git branch' --edit-description [<branchname>] @@ -48,7 +50,7 @@ branch so that 'git pull' will appropriately merge from the remote-tracking branch. This behavior may be changed via the global `branch.autosetupmerge` configuration flag. That setting can be overridden by using the `--track` and `--no-track` options, and -changed later using `git branch --set-upstream`. +changed later using `git branch --set-upstream-to`. With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>. If <oldbranch> had a corresponding reflog, it is renamed to match @@ -173,6 +175,16 @@ start-point is either a local or remote-tracking branch. like `--track` would when creating the branch, except that where branch points to is not changed. +-u <upstream>:: +--set-upstream-to=<upstream>:: + Set up <branchname>'s tracking information so <upstream> is + considered <branchname>'s upstream branch. If no <branchname> + is specified, then it defaults to the current branch. + +--unset-upstream:: + Remove the upstream information for <branchname>. If no branch + is specified it defaults to the current branch. + --edit-description:: Open an editor and edit the text to explain what the branch is for, to be used by various other commands (e.g. `request-pull`). diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 11cc7f0588..7958a47006 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -367,6 +367,18 @@ $ git checkout hello.c <3> <2> take a file out of another commit <3> restore hello.c from the index + +If you want to check out _all_ C source files out of the index, +you can say ++ +------------ +$ git checkout -- '*.c' +------------ ++ +Note the quotes around `*.c`. The file `hello.c` will also be +checked out, even though it is no longer in the working tree, +because the file globbing is used to match entries in the index +(not in the working tree by the shell). ++ If you have an unfortunate branch that is named `hello.c`, this step would be confused as an instruction to switch to that branch. You should instead write: diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 0e170a51ca..c205d2363e 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -118,6 +118,11 @@ effect to your index in a row. previous commit are dropped. To force the inclusion of those commits use `--keep-redundant-commits`. +--allow-empty-message:: + By default, cherry-picking a commit with an empty message will fail. + This option overrides that behaviour, allowing commits with empty + messages to be cherry picked. + --keep-redundant-commits:: If a commit being cherry picked duplicates a commit already in the current history, it will become empty. By default these diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index e8f757704c..7e5098a95e 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -16,6 +16,7 @@ SYNOPSIS [--reuseaddr] [--detach] [--pid-file=<file>] [--enable=<service>] [--disable=<service>] [--allow-override=<service>] [--forbid-override=<service>] + [--access-hook=<path>] [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]] [<directory>...] @@ -171,6 +172,21 @@ the facility of inet daemon to achieve the same before spawning errors are not enabled, all errors report "access denied" to the client. The default is --no-informative-errors. +--access-hook=<path>:: + Every time a client connects, first run an external command + specified by the <path> with service name (e.g. "upload-pack"), + path to the repository, hostname (%H), canonical hostname + (%CH), ip address (%IP), and tcp port (%P) as its command line + arguments. The external command can decide to decline the + service by exiting with a non-zero status (or to allow it by + exiting with a zero status). It can also look at the $REMOTE_ADDR + and $REMOTE_PORT environment variables to learn about the + requestor when making this decision. ++ +The external command can optionally write a single line to its +standard output to be sent to the requestor as an error message when +it declines the service. + <directory>:: A directory to add to the whitelist of allowed directories. Unless --strict-paths is specified this will also include subdirectories diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt index 31fc2e3aed..73ca7025a3 100644 --- a/Documentation/git-difftool.txt +++ b/Documentation/git-difftool.txt @@ -69,6 +69,14 @@ with custom merge tool commands and has the same value as `$MERGED`. --tool-help:: Print a list of diff tools that may be used with `--tool`. +--symlinks:: +--no-symlinks:: + 'git difftool''s default behavior is create symlinks to the + working tree when run in `--dir-diff` mode. ++ + Specifying `--no-symlinks` instructs 'git difftool' to create + copies instead. `--no-symlinks` is the default on Windows. + -x <command>:: --extcmd=<command>:: Specify a custom command for viewing diffs. diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 3bec036883..cfecf848fb 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -42,8 +42,16 @@ CONFIGURATION grep.lineNumber:: If set to true, enable '-n' option by default. +grep.patternType:: + Set the default matching behavior. Using a value of 'basic', 'extended', + 'fixed', or 'perl' will enable the '--basic-regexp', '--extended-regexp', + '--fixed-strings', or '--perl-regexp' option accordingly, while the + value 'default' will return to the default matching behavior. + grep.extendedRegexp:: - If set to true, enable '--extended-regexp' option by default. + If set to true, enable '--extended-regexp' option by default. This + option is ignored when the 'grep.patternType' option is set to a value + other than 'default'. OPTIONS diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 1f906208f9..585dac40ba 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -24,10 +24,6 @@ each commit introduces are shown. OPTIONS ------- --<n>:: - Limits the number of commits to show. - Note that this is a commit limiting option, see below. - <since>..<until>:: Show only commits between the named two commits. When either <since> or <until> is omitted, it defaults to @@ -137,6 +133,8 @@ Examples This makes sense only when following a strict policy of merging all topic branches when staying on a single integration branch. +`git log -3`:: + Limits the number of commits to show to 3. Discussion ---------- diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index b295bf8330..87842e33f8 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git merge-base' [-a|--all] <commit> <commit>... 'git merge-base' [-a|--all] --octopus <commit>... +'git merge-base' --is-ancestor <commit> <commit> 'git merge-base' --independent <commit>... DESCRIPTION @@ -50,6 +51,12 @@ from linkgit:git-show-branch[1] when used with the `--merge-base` option. from any other. This mimics the behavior of 'git show-branch --independent'. +--is-ancestor:: + Check if the first <commit> is an ancestor of the second <commit>, + and exit with status 0 if true, or with status 1 if not. + Errors are signaled by a non-zero status that is not 1. + + OPTIONS ------- -a:: @@ -110,6 +117,27 @@ both '1' and '2' are merge-bases of A and B. Neither one is better than the other (both are 'best' merge bases). When the `--all` option is not given, it is unspecified which best one is output. +A common idiom to check "fast-forward-ness" between two commits A +and B is (or at least used to be) to compute the merge base between +A and B, and check if it is the same as A, in which case, A is an +ancestor of B. You will see this idiom used often in older scripts. + + A=$(git rev-parse --verify A) + if test "$A" = "$(git merge-base A B)" + then + ... A is an ancestor of B ... + fi + +In modern git, you can say this in a more direct way: + + if git merge-base --is-ancestor A B + then + ... A is an ancestor of B ... + fi + +instead. + + See also -------- linkgit:git-rev-list[1], diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index d7207bd9b9..6b563c500f 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -64,6 +64,9 @@ variable `mergetool.<tool>.trustExitCode` can be set to `true`. Otherwise, 'git mergetool' will prompt the user to indicate the success of the resolution after the custom tool has exited. +--tool-help:: + Print a list of merge tools that may be used with `--tool`. + -y:: --no-prompt:: Don't prompt before each invocation of the merge resolution diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index a308f4c79f..e8c396b5f9 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git remote' [-v | --verbose] 'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url> 'git remote rename' <old> <new> -'git remote rm' <name> +'git remote remove' <name> 'git remote set-head' <name> (-a | -d | <branch>) 'git remote set-branches' [--add] <name> <branch>... 'git remote set-url' [--push] <name> <newurl> [<oldurl>] @@ -85,6 +85,7 @@ In case <old> and <new> are the same, and <old> is a file under `$GIT_DIR/remotes` or `$GIT_DIR/branches`, the remote is converted to the configuration file format. +'remove':: 'rm':: Remove the remote named <name>. All remote-tracking branches and diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 3e72a5d68e..f6ba90c2da 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -37,11 +37,28 @@ arguments. Here are the rules: file called HEAD in your work tree, `git diff HEAD` is ambiguous, and you have to say either `git diff HEAD --` or `git diff -- HEAD` to disambiguate. - ++ When writing a script that is expected to handle random user-input, it is a good practice to make it explicit which arguments are which by placing disambiguating `--` at appropriate places. + * Many commands allow wildcards in paths, but you need to protect + them from getting globbed by the shell. These two mean different + things: ++ +-------------------------------- +$ git checkout -- *.c +$ git checkout -- \*.c +-------------------------------- ++ +The former lets your shell expand the fileglob, and you are asking +the dot-C files in your working tree to be overwritten with the version +in the index. The latter passes the `*.c` to Git, and you are asking +the paths in the index that match the pattern to be checked out to your +working tree. After running `git add hello.c; rm hello.c`, you will _not_ +see `hello.c` in your working tree with the former, but with the latter +you will. + Here are the rules regarding the "flags" that you should follow when you are scripting git: diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index def1340ac7..918c1109f2 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -8,7 +8,8 @@ ordering and formatting options, such as '--reverse'. -- --n 'number':: +-<number>:: +-n <number>:: --max-count=<number>:: Limit the number of commits to output. @@ -636,10 +637,14 @@ These options are mostly targeted for packing of git repositories. Only useful with '--objects'; print the object IDs that are not in packs. ---no-walk:: +--no-walk[=(sorted|unsorted)]:: - Only show the given revs, but do not traverse their ancestors. - This has no effect if a range is specified. + Only show the given commits, but do not traverse their ancestors. + This has no effect if a range is specified. If the argument + "unsorted" is given, the commits are show in the order they were + given on the command line. Otherwise (if "sorted" or no argument + was given), the commits are show in reverse chronological order + by commit time. --do-walk:: diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt index 69d996bc38..991fcd8f3f 100644 --- a/Documentation/revisions.txt +++ b/Documentation/revisions.txt @@ -55,6 +55,8 @@ when you run `git cherry-pick`. + Note that any of the 'refs/*' cases above may come either from the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file. +While the ref name encoding is unspecified, UTF-8 is prefered as +some output processing may assume ref names in UTF-8. '<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}':: A ref followed by the suffix '@' with a date specification diff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt index 1b7d8f140c..1a797812fb 100644 --- a/Documentation/technical/api-argv-array.txt +++ b/Documentation/technical/api-argv-array.txt @@ -46,6 +46,10 @@ Functions Format a string and push it onto the end of the array. This is a convenience wrapper combining `strbuf_addf` and `argv_array_push`. +`argv_array_pop`:: + Remove the final element from the array. If there are no + elements in the array, do nothing. + `argv_array_clear`:: Free all memory associated with the array and return it to the initial, empty state. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index b27a2ff687..d2d2d699e1 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.7.12 +DEF_VER=v1.7.12.GIT LF=' ' @@ -90,6 +90,8 @@ all:: # # Define NO_MKDTEMP if you don't have mkdtemp in the C library. # +# Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash. +# # Define NO_MKSTEMPS if you don't have mkstemps in the C library. # # Define NO_STRTOK_R if you don't have strtok_r in the C library. @@ -157,6 +159,11 @@ all:: # Define NO_PREAD if you have a problem with pread() system call (e.g. # cygwin1.dll before v1.5.22). # +# Define NO_SETITIMER if you don't have setitimer() +# +# Define NO_STRUCT_ITIMERVAL if you don't have struct itimerval +# This also implies NO_SETITIMER +# # Define NO_THREAD_SAFE_PREAD if your pread() implementation is not # thread-safe. (e.g. compat/pread.c or cygwin) # @@ -496,6 +503,7 @@ TEST_PROGRAMS_NEED_X += test-mergesort TEST_PROGRAMS_NEED_X += test-mktemp TEST_PROGRAMS_NEED_X += test-parse-options TEST_PROGRAMS_NEED_X += test-path-utils +TEST_PROGRAMS_NEED_X += test-regex TEST_PROGRAMS_NEED_X += test-revision-walking TEST_PROGRAMS_NEED_X += test-run-command TEST_PROGRAMS_NEED_X += test-scrap-cache-tree @@ -1639,6 +1647,10 @@ ifdef NO_MKDTEMP COMPAT_CFLAGS += -DNO_MKDTEMP COMPAT_OBJS += compat/mkdtemp.o endif +ifdef MKDIR_WO_TRAILING_SLASH + COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH + COMPAT_OBJS += compat/mkdir.o +endif ifdef NO_MKSTEMPS COMPAT_CFLAGS += -DNO_MKSTEMPS endif @@ -1670,6 +1682,13 @@ endif ifdef OBJECT_CREATION_USES_RENAMES COMPAT_CFLAGS += -DOBJECT_CREATION_MODE=1 endif +ifdef NO_STRUCT_ITIMERVAL + COMPAT_CFLAGS += -DNO_STRUCT_ITIMERVAL + NO_SETITIMER=YesPlease +endif +ifdef NO_SETITIMER + COMPAT_CFLAGS += -DNO_SETITIMER +endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD COMPAT_OBJS += compat/pread.o @@ -1 +1 @@ -Documentation/RelNotes/1.7.12.1.txt
\ No newline at end of file +Documentation/RelNotes/1.8.0.txt
\ No newline at end of file @@ -7,10 +7,10 @@ #include "unpack-trees.h" static char const * const archive_usage[] = { - "git archive [options] <tree-ish> [<path>...]", - "git archive --list", - "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]", - "git archive --remote <repo> [--exec <cmd>] --list", + N_("git archive [options] <tree-ish> [<path>...]"), + N_("git archive --list"), + N_("git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]"), + N_("git archive --remote <repo> [--exec <cmd>] --list"), NULL }; @@ -319,16 +319,16 @@ static int parse_archive_args(int argc, const char **argv, int worktree_attributes = 0; struct option opts[] = { OPT_GROUP(""), - OPT_STRING(0, "format", &format, "fmt", "archive format"), - OPT_STRING(0, "prefix", &base, "prefix", - "prepend prefix to each pathname in the archive"), - OPT_STRING('o', "output", &output, "file", - "write the archive to this file"), + OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")), + OPT_STRING(0, "prefix", &base, N_("prefix"), + N_("prepend prefix to each pathname in the archive")), + OPT_STRING('o', "output", &output, N_("file"), + N_("write the archive to this file")), OPT_BOOL(0, "worktree-attributes", &worktree_attributes, - "read .gitattributes in working directory"), - OPT__VERBOSE(&verbose, "report archived files on stderr"), - OPT__COMPR('0', &compression_level, "store only", 0), - OPT__COMPR('1', &compression_level, "compress faster", 1), + N_("read .gitattributes in working directory")), + OPT__VERBOSE(&verbose, N_("report archived files on stderr")), + OPT__COMPR('0', &compression_level, N_("store only"), 0), + OPT__COMPR('1', &compression_level, N_("compress faster"), 1), OPT__COMPR_HIDDEN('2', &compression_level, 2), OPT__COMPR_HIDDEN('3', &compression_level, 3), OPT__COMPR_HIDDEN('4', &compression_level, 4), @@ -336,15 +336,15 @@ static int parse_archive_args(int argc, const char **argv, OPT__COMPR_HIDDEN('6', &compression_level, 6), OPT__COMPR_HIDDEN('7', &compression_level, 7), OPT__COMPR_HIDDEN('8', &compression_level, 8), - OPT__COMPR('9', &compression_level, "compress better", 9), + OPT__COMPR('9', &compression_level, N_("compress better"), 9), OPT_GROUP(""), OPT_BOOL('l', "list", &list, - "list supported archive formats"), + N_("list supported archive formats")), OPT_GROUP(""), - OPT_STRING(0, "remote", &remote, "repo", - "retrieve the archive from remote repository <repo>"), - OPT_STRING(0, "exec", &exec, "cmd", - "path to the remote git-upload-archive command"), + OPT_STRING(0, "remote", &remote, N_("repo"), + N_("retrieve the archive from remote repository <repo>")), + OPT_STRING(0, "exec", &exec, N_("command"), + N_("path to the remote git-upload-archive command")), OPT_END() }; diff --git a/argv-array.c b/argv-array.c index 0b5f8898a1..256741d226 100644 --- a/argv-array.c +++ b/argv-array.c @@ -49,12 +49,21 @@ void argv_array_pushl(struct argv_array *array, ...) va_end(ap); } +void argv_array_pop(struct argv_array *array) +{ + if (!array->argc) + return; + free((char *)array->argv[array->argc - 1]); + array->argv[array->argc - 1] = NULL; + array->argc--; +} + void argv_array_clear(struct argv_array *array) { if (array->argv != empty_argv) { int i; for (i = 0; i < array->argc; i++) - free((char **)array->argv[i]); + free((char *)array->argv[i]); free(array->argv); } argv_array_init(array); diff --git a/argv-array.h b/argv-array.h index b93a69c36c..f4b98660f8 100644 --- a/argv-array.h +++ b/argv-array.h @@ -16,6 +16,7 @@ void argv_array_push(struct argv_array *, const char *); __attribute__((format (printf,2,3))) void argv_array_pushf(struct argv_array *, const char *fmt, ...); void argv_array_pushl(struct argv_array *, ...); +void argv_array_pop(struct argv_array *); void argv_array_clear(struct argv_array *); #endif /* ARGV_ARRAY_H */ @@ -352,8 +352,11 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) char buf[2048]; int lineno = 0; - if (!fp) + if (!fp) { + if (errno != ENOENT) + warn_on_inaccessible(path); return NULL; + } res = xcalloc(1, sizeof(*res)); while (fgets(buf, sizeof(buf), fp)) handle_attr_line(res, buf, path, ++lineno, macro_ok); diff --git a/builtin/add.c b/builtin/add.c index 89dce56a24..2816789b9d 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -16,7 +16,7 @@ #include "bulk-checkin.h" static const char * const builtin_add_usage[] = { - "git add [options] [--] <filepattern>...", + N_("git add [options] [--] <filepattern>..."), NULL }; static int patch_interactive, add_interactive, edit_interactive; @@ -315,19 +315,19 @@ static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0; static struct option builtin_add_options[] = { - OPT__DRY_RUN(&show_only, "dry run"), - OPT__VERBOSE(&verbose, "be verbose"), + OPT__DRY_RUN(&show_only, N_("dry run")), + OPT__VERBOSE(&verbose, N_("be verbose")), OPT_GROUP(""), - OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), - OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"), - OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"), - OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"), - OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), - OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"), - OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"), - OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), - OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), - OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"), + OPT_BOOLEAN('i', "interactive", &add_interactive, N_("interactive picking")), + OPT_BOOLEAN('p', "patch", &patch_interactive, N_("select hunks interactively")), + OPT_BOOLEAN('e', "edit", &edit_interactive, N_("edit current diff and apply")), + OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")), + OPT_BOOLEAN('u', "update", &take_worktree_changes, N_("update tracked files")), + OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")), + OPT_BOOLEAN('A', "all", &addremove, N_("add changes from all tracked and untracked files")), + OPT_BOOLEAN( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")), + OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")), + OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")), OPT_END(), }; diff --git a/builtin/apply.c b/builtin/apply.c index ca8695ad31..156b3ce3b7 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4314,7 +4314,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) OPT_NOOP_NOARG(0, "allow-binary-replacement"), OPT_NOOP_NOARG(0, "binary"), OPT_BOOLEAN(0, "numstat", &numstat, - N_("shows number of added and deleted lines in decimal notation")), + N_("show number of added and deleted lines in decimal notation")), OPT_BOOLEAN(0, "summary", &summary, N_("instead of applying the patch, output a summary for the input")), OPT_BOOLEAN(0, "check", &check, diff --git a/builtin/archive.c b/builtin/archive.c index 931956def9..9a1cfd3dac 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -88,12 +88,12 @@ int cmd_archive(int argc, const char **argv, const char *prefix) const char *output = NULL; const char *remote = NULL; struct option local_opts[] = { - OPT_STRING('o', "output", &output, "file", - "write the archive to this file"), - OPT_STRING(0, "remote", &remote, "repo", - "retrieve the archive from remote repository <repo>"), - OPT_STRING(0, "exec", &exec, "cmd", - "path to the remote git-upload-archive command"), + OPT_STRING('o', "output", &output, N_("file"), + N_("write the archive to this file")), + OPT_STRING(0, "remote", &remote, N_("repo"), + N_("retrieve the archive from remote repository <repo>")), + OPT_STRING(0, "exec", &exec, N_("command"), + N_("path to the remote git-upload-archive command")), OPT_END() }; diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 8d325a5179..e3884e3bb6 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -4,7 +4,7 @@ #include "bisect.h" static const char * const git_bisect_helper_usage[] = { - "git bisect--helper --next-all [--no-checkout]", + N_("git bisect--helper --next-all [--no-checkout]"), NULL }; @@ -14,9 +14,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) int no_checkout = 0; struct option options[] = { OPT_BOOLEAN(0, "next-all", &next_all, - "perform 'git bisect next'"), + N_("perform 'git bisect next'")), OPT_BOOLEAN(0, "no-checkout", &no_checkout, - "update BISECT_HEAD instead of checking out the current commit"), + N_("update BISECT_HEAD instead of checking out the current commit")), OPT_END() }; diff --git a/builtin/blame.c b/builtin/blame.c index ed5d01b36a..0e102bf2c2 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -22,12 +22,12 @@ #include "utf8.h" #include "userdiff.h" -static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file"; +static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file"); static const char *blame_opt_usage[] = { blame_usage, "", - "[rev-opts] are documented in git-rev-list(1)", + N_("[rev-opts] are documented in git-rev-list(1)"), NULL }; @@ -2313,27 +2313,27 @@ int cmd_blame(int argc, const char **argv, const char *prefix) static const char *revs_file = NULL; static const char *contents_from = NULL; static const struct option options[] = { - OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"), - OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"), - OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"), - OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"), - OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE), - OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME), - OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER), - OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN), - OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN), - OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT), - OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP), - OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME), - OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR), - OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL), - OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE), - OPT_BIT(0, "minimal", &xdl_opts, "Spend extra cycles to find better match", XDF_NEED_MINIMAL), - OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"), - OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"), - { OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback }, - { OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback }, - OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback), + 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_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), + OPT_BIT('p', "porcelain", &output_option, N_("Show in a format designed for machine consumption"), OUTPUT_PORCELAIN), + OPT_BIT(0, "line-porcelain", &output_option, N_("Show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN), + OPT_BIT('c', NULL, &output_option, N_("Use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT), + OPT_BIT('t', NULL, &output_option, N_("Show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP), + OPT_BIT('l', NULL, &output_option, N_("Show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME), + OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR), + OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL), + OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE), + OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL), + OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")), + 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__ABBREV(&abbrev), OPT_END() }; diff --git a/builtin/branch.c b/builtin/branch.c index 0e060f2e4a..5cb6d78f2c 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -17,12 +17,13 @@ #include "revision.h" #include "string-list.h" #include "column.h" +#include "utf8.h" static const char * const builtin_branch_usage[] = { - "git branch [options] [-r | -a] [--merged | --no-merged]", - "git branch [options] [-l] [-f] <branchname> [<start-point>]", - "git branch [options] [-r] (-d | -D) <branchname>...", - "git branch [options] (-m | -M) [<oldbranch>] <newbranch>", + N_("git branch [options] [-r | -a] [--merged | --no-merged]"), + N_("git branch [options] [-l] [-f] <branchname> [<start-point>]"), + N_("git branch [options] [-r] (-d | -D) <branchname>..."), + N_("git branch [options] (-m | -M) [<oldbranch>] <newbranch>"), NULL }; @@ -129,7 +130,7 @@ static int branch_merged(int kind, const char *name, if (!reference_rev) reference_rev = head_rev; - merged = in_merge_bases(rev, &reference_rev, 1); + merged = in_merge_bases(rev, reference_rev); /* * After the safety valve is fully redefined to "check with @@ -139,7 +140,7 @@ static int branch_merged(int kind, const char *name, * a gentle reminder is in order. */ if ((head_rev != reference_rev) && - in_merge_bases(rev, &head_rev, 1) != merged) { + in_merge_bases(rev, head_rev) != merged) { if (merged) warning(_("deleting branch '%s' that has been merged to\n" " '%s', but not yet merged to HEAD."), @@ -249,7 +250,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, struct ref_item { char *name; char *dest; - unsigned int kind, len; + unsigned int kind, width; struct commit *commit; }; @@ -354,14 +355,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, newitem->name = xstrdup(refname); newitem->kind = kind; newitem->commit = commit; - newitem->len = strlen(refname); + newitem->width = utf8_strwidth(refname); newitem->dest = resolve_symref(orig_refname, prefix); /* adjust for "remotes/" */ if (newitem->kind == REF_REMOTE_BRANCH && ref_list->kinds != REF_REMOTE_BRANCH) - newitem->len += 8; - if (newitem->len > ref_list->maxwidth) - ref_list->maxwidth = newitem->len; + newitem->width += 8; + if (newitem->width > ref_list->maxwidth) + ref_list->maxwidth = newitem->width; return 0; } @@ -490,11 +491,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } strbuf_addf(&name, "%s%s", prefix, item->name); - if (verbose) + if (verbose) { + int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf); strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), - maxwidth, name.buf, + maxwidth + utf8_compensation, name.buf, branch_get_color(BRANCH_COLOR_RESET)); - else + } else strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), name.buf, branch_get_color(BRANCH_COLOR_RESET)); @@ -519,8 +521,8 @@ static int calc_maxwidth(struct ref_list *refs) for (i = 0; i < refs->index; i++) { if (!matches_merge_filter(refs->list[i].commit)) continue; - if (refs->list[i].len > w) - w = refs->list[i].len; + if (refs->list[i].width > w) + w = refs->list[i].width; } return w; } @@ -533,12 +535,12 @@ static void show_detached(struct ref_list *ref_list) if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) { struct ref_item item; item.name = xstrdup(_("(no branch)")); - item.len = strlen(item.name); + item.width = utf8_strwidth(item.name); item.kind = REF_LOCAL_BRANCH; item.dest = NULL; item.commit = head_commit; - if (item.len > ref_list->maxwidth) - ref_list->maxwidth = item.len; + if (item.width > ref_list->maxwidth) + ref_list->maxwidth = item.width; print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, ""); free(item.name); } @@ -712,62 +714,65 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int delete = 0, rename = 0, force_create = 0, list = 0; int verbose = 0, abbrev = -1, detached = 0; int reflog = 0, edit_description = 0; - int quiet = 0; + int quiet = 0, unset_upstream = 0; + const char *new_upstream = NULL; enum branch_track track; int kinds = REF_LOCAL_BRANCH; struct commit_list *with_commit = NULL; struct option options[] = { - OPT_GROUP("Generic options"), + OPT_GROUP(N_("Generic options")), OPT__VERBOSE(&verbose, - "show hash and subject, give twice for upstream branch"), - OPT__QUIET(&quiet, "suppress informational messages"), - OPT_SET_INT('t', "track", &track, "set up tracking mode (see git-pull(1))", + N_("show hash and subject, give twice for upstream branch")), + OPT__QUIET(&quiet, N_("suppress informational messages")), + OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"), BRANCH_TRACK_EXPLICIT), - OPT_SET_INT( 0, "set-upstream", &track, "change upstream info", + OPT_SET_INT( 0, "set-upstream", &track, N_("change upstream info"), BRANCH_TRACK_OVERRIDE), - OPT__COLOR(&branch_use_color, "use colored output"), - OPT_SET_INT('r', "remotes", &kinds, "act on remote-tracking branches", + 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__COLOR(&branch_use_color, N_("use colored output")), + OPT_SET_INT('r', "remotes", &kinds, N_("act on remote-tracking branches"), REF_REMOTE_BRANCH), { - OPTION_CALLBACK, 0, "contains", &with_commit, "commit", - "print only branches that contain the commit", + OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"), + N_("print only branches that contain the commit"), PARSE_OPT_LASTARG_DEFAULT, parse_opt_with_commit, (intptr_t)"HEAD", }, { - OPTION_CALLBACK, 0, "with", &with_commit, "commit", - "print only branches that contain the commit", + OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"), + N_("print only branches that contain the commit"), PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT, parse_opt_with_commit, (intptr_t) "HEAD", }, OPT__ABBREV(&abbrev), - OPT_GROUP("Specific git-branch actions:"), - OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches", + OPT_GROUP(N_("Specific git-branch actions:")), + OPT_SET_INT('a', "all", &kinds, N_("list both remote-tracking and local branches"), REF_REMOTE_BRANCH | REF_LOCAL_BRANCH), - OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1), - OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2), - OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1), - OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2), - OPT_BOOLEAN(0, "list", &list, "list branch names"), - OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"), + OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), + 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, - "edit the description for the branch"), - OPT__FORCE(&force_create, "force creation (when already exists)"), + 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, - "commit", "print only not merged branches", + N_("commit"), N_("print only not merged branches"), PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, opt_parse_merge_filter, (intptr_t) "HEAD", }, { OPTION_CALLBACK, 0, "merged", &merge_filter_ref, - "commit", "print only merged branches", + N_("commit"), N_("print only merged branches"), PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, opt_parse_merge_filter, (intptr_t) "HEAD", }, - OPT_COLUMN(0, "column", &colopts, "list branches in columns"), + OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), OPT_END(), }; @@ -794,10 +799,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, 0); - if (!delete && !rename && !edit_description && argc == 0) + if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0) list = 1; - if (!!delete + !!rename + !!force_create + !!list > 1) + if (!!delete + !!rename + !!force_create + !!list + !!new_upstream + !!unset_upstream > 1) usage_with_options(builtin_branch_usage, options); if (abbrev == -1) @@ -852,11 +857,62 @@ int cmd_branch(int argc, const char **argv, const char *prefix) rename_branch(argv[0], argv[1], rename > 1); else usage_with_options(builtin_branch_usage, options); + } else if (new_upstream) { + struct branch *branch = branch_get(argv[0]); + + if (!ref_exists(branch->refname)) + die(_("branch '%s' does not exist"), branch->name); + + /* + * create_branch takes care of setting up the tracking + * info and making sure new_upstream is correct + */ + create_branch(head, branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE); + } else if (unset_upstream) { + struct branch *branch = branch_get(argv[0]); + struct strbuf buf = STRBUF_INIT; + + if (!branch_has_merge_config(branch)) { + die(_("Branch '%s' has no upstream information"), branch->name); + } + + strbuf_addf(&buf, "branch.%s.remote", branch->name); + git_config_set_multivar(buf.buf, NULL, NULL, 1); + strbuf_reset(&buf); + strbuf_addf(&buf, "branch.%s.merge", branch->name); + git_config_set_multivar(buf.buf, NULL, NULL, 1); + strbuf_release(&buf); } else if (argc > 0 && argc <= 2) { + struct branch *branch = branch_get(argv[0]); + int branch_existed = 0, remote_tracking = 0; + struct strbuf buf = STRBUF_INIT; + if (kinds != REF_LOCAL_BRANCH) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); + + if (track == BRANCH_TRACK_OVERRIDE) + fprintf(stderr, _("The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to\n")); + + strbuf_addf(&buf, "refs/remotes/%s", branch->name); + remote_tracking = ref_exists(buf.buf); + strbuf_release(&buf); + + branch_existed = ref_exists(branch->refname); create_branch(head, argv[0], (argc == 2) ? argv[1] : head, force_create, reflog, 0, quiet, track); + + /* + * We only show the instructions if the user gave us + * one branch which doesn't exist locally, but is the + * name of a remote-tracking branch. + */ + if (argc == 1 && track == BRANCH_TRACK_OVERRIDE && + !branch_existed && remote_tracking) { + fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name); + fprintf(stderr, _(" git branch -d %s\n"), branch->name); + fprintf(stderr, _(" git branch --set-upstream-to %s\n"), branch->name); + } + } else usage_with_options(builtin_branch_usage, options); diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 0eca2d7bd0..00528ddc38 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -244,8 +244,8 @@ static int batch_objects(int print_contents) } static const char * const cat_file_usage[] = { - "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>", - "git cat-file (--batch|--batch-check) < <list_of_objects>", + N_("git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"), + N_("git cat-file (--batch|--batch-check) < <list_of_objects>"), NULL }; @@ -263,19 +263,19 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) const char *exp_type = NULL, *obj_name = NULL; const struct option options[] = { - OPT_GROUP("<type> can be one of: blob, tree, commit, tag"), - OPT_SET_INT('t', NULL, &opt, "show object type", 't'), - OPT_SET_INT('s', NULL, &opt, "show object size", 's'), + OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")), + OPT_SET_INT('t', NULL, &opt, N_("show object type"), 't'), + OPT_SET_INT('s', NULL, &opt, N_("show object size"), 's'), OPT_SET_INT('e', NULL, &opt, - "exit with zero when there's no error", 'e'), - OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'), + N_("exit with zero when there's no error"), 'e'), + OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'), OPT_SET_INT(0, "textconv", &opt, - "for blob objects, run textconv on object's content", 'c'), + N_("for blob objects, run textconv on object's content"), 'c'), OPT_SET_INT(0, "batch", &batch, - "show info and content of objects fed from the standard input", + N_("show info and content of objects fed from the standard input"), BATCH), OPT_SET_INT(0, "batch-check", &batch, - "show info about objects fed from the standard input", + N_("show info about objects fed from the standard input"), BATCH_CHECK), OPT_END() }; diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 44c421eb0f..e1ff575daa 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -8,19 +8,19 @@ static int all_attrs; static int cached_attrs; static int stdin_paths; static const char * const check_attr_usage[] = { -"git check-attr [-a | --all | attr...] [--] pathname...", -"git check-attr --stdin [-a | --all | attr...] < <list-of-paths>", +N_("git check-attr [-a | --all | attr...] [--] pathname..."), +N_("git check-attr --stdin [-a | --all | attr...] < <list-of-paths>"), NULL }; static int null_term_line; static const struct option check_attr_options[] = { - OPT_BOOLEAN('a', "all", &all_attrs, "report all attributes set on file"), - OPT_BOOLEAN(0, "cached", &cached_attrs, "use .gitattributes only from the index"), - OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"), + 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, - "input paths are terminated by a null character"), + N_("input paths are terminated by a null character")), OPT_END() }; diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index c16d82b7de..b1feda7d5e 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -123,7 +123,7 @@ static void checkout_all(const char *prefix, int prefix_length) } static const char * const builtin_checkout_index_usage[] = { - "git checkout-index [options] [--] [<file>...]", + N_("git checkout-index [options] [--] [<file>...]"), NULL }; @@ -184,27 +184,27 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) int force = 0, quiet = 0, not_new = 0; struct option builtin_checkout_index_options[] = { OPT_BOOLEAN('a', "all", &all, - "checks out all files in the index"), - OPT__FORCE(&force, "forces overwrite of existing files"), + N_("check out all files in the index")), + OPT__FORCE(&force, N_("force overwrite of existing files")), OPT__QUIET(&quiet, - "no warning for existing files and files not in index"), + N_("no warning for existing files and files not in index")), OPT_BOOLEAN('n', "no-create", ¬_new, - "don't checkout new files"), + N_("don't checkout new files")), { OPTION_CALLBACK, 'u', "index", &newfd, NULL, - "update stat information in the index file", + N_("update stat information in the index file"), PARSE_OPT_NOARG, option_parse_u }, { OPTION_CALLBACK, 'z', NULL, NULL, NULL, - "paths are separated with NUL character", + N_("paths are separated with NUL character"), PARSE_OPT_NOARG, option_parse_z }, OPT_BOOLEAN(0, "stdin", &read_from_stdin, - "read list of paths from the standard input"), + N_("read list of paths from the standard input")), OPT_BOOLEAN(0, "temp", &to_tempfile, - "write the content to temporary files"), - OPT_CALLBACK(0, "prefix", NULL, "string", - "when creating files, prepend <string>", + N_("write the content to temporary files")), + OPT_CALLBACK(0, "prefix", NULL, N_("string"), + N_("when creating files, prepend <string>"), option_parse_prefix), OPT_CALLBACK(0, "stage", NULL, NULL, - "copy out the files from named stage", + N_("copy out the files from named stage"), option_parse_stage), OPT_END() }; diff --git a/builtin/checkout.c b/builtin/checkout.c index 7d922c612a..d287ee6e48 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -22,8 +22,8 @@ #include "argv-array.h" static const char * const checkout_usage[] = { - "git checkout [options] <branch>", - "git checkout [options] [<branch>] -- <file>...", + N_("git checkout [options] <branch>"), + N_("git checkout [options] [<branch>] -- <file>..."), NULL, }; @@ -933,28 +933,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) int patch_mode = 0; int dwim_new_local_branch = 1; struct option options[] = { - OPT__QUIET(&opts.quiet, "suppress progress reporting"), - OPT_STRING('b', NULL, &opts.new_branch, "branch", - "create and checkout a new branch"), - OPT_STRING('B', NULL, &opts.new_branch_force, "branch", - "create/reset and checkout a branch"), - OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"), - OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"), - OPT_SET_INT('t', "track", &opts.track, "set upstream info for new branch", + OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), + OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), + 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_SET_INT('t', "track", &opts.track, N_("set upstream info for new branch"), BRANCH_TRACK_EXPLICIT), - OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"), - OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files", + OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")), + OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"), 2), - OPT_SET_INT('3', "theirs", &opts.writeout_stage, "checkout their version for unmerged files", + OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"), 3), - OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"), - OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"), - OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"), - OPT_STRING(0, "conflict", &conflict_style, "style", - "conflict style (merge or diff3)"), - OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), + 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_STRING(0, "conflict", &conflict_style, N_("style"), + N_("conflict style (merge or diff3)")), + OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")), { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL, - "second guess 'git checkout no-such-branch'", + N_("second guess 'git checkout no-such-branch'"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END(), }; diff --git a/builtin/clean.c b/builtin/clean.c index 0c7b3d0f4c..69c1cda906 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -16,7 +16,7 @@ static int force = -1; /* unset */ static const char *const builtin_clean_usage[] = { - "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...", + N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."), NULL }; @@ -48,16 +48,16 @@ int cmd_clean(int argc, const char **argv, const char *prefix) const char *qname; char *seen = NULL; struct option options[] = { - OPT__QUIET(&quiet, "do not print names of files removed"), - OPT__DRY_RUN(&show_only, "dry run"), - OPT__FORCE(&force, "force"), + OPT__QUIET(&quiet, N_("do not print names of files removed")), + OPT__DRY_RUN(&show_only, N_("dry run")), + OPT__FORCE(&force, N_("force")), OPT_BOOLEAN('d', NULL, &remove_directories, - "remove whole directories"), - { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern", - "add <pattern> to ignore rules", PARSE_OPT_NONEG, exclude_cb }, - OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), + 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, - "remove only ignored files"), + N_("remove only ignored files")), OPT_END() }; diff --git a/builtin/clone.c b/builtin/clone.c index e314b0b6d2..5e8f3ba22c 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -33,7 +33,7 @@ * */ static const char * const builtin_clone_usage[] = { - "git clone [options] [--] <repo> [<dir>]", + N_("git clone [options] [--] <repo> [<dir>]"), NULL }; @@ -61,43 +61,43 @@ static int opt_parse_reference(const struct option *opt, const char *arg, int un static struct option builtin_clone_options[] = { OPT__VERBOSITY(&option_verbosity), OPT_BOOL(0, "progress", &option_progress, - "force progress reporting"), + N_("force progress reporting")), OPT_BOOLEAN('n', "no-checkout", &option_no_checkout, - "don't create a checkout"), - OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"), + N_("don't create a checkout")), + OPT_BOOLEAN(0, "bare", &option_bare, N_("create a bare repository")), { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL, - "create a bare repository", + N_("create a bare repository"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_BOOLEAN(0, "mirror", &option_mirror, - "create a mirror repository (implies bare)"), + N_("create a mirror repository (implies bare)")), OPT_BOOL('l', "local", &option_local, - "to clone from a local repository"), + N_("to clone from a local repository")), OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks, - "don't use local hardlinks, always copy"), + N_("don't use local hardlinks, always copy")), OPT_BOOLEAN('s', "shared", &option_shared, - "setup as shared repository"), + N_("setup as shared repository")), OPT_BOOLEAN(0, "recursive", &option_recursive, - "initialize submodules in the clone"), + N_("initialize submodules in the clone")), OPT_BOOLEAN(0, "recurse-submodules", &option_recursive, - "initialize submodules in the clone"), - OPT_STRING(0, "template", &option_template, "template-directory", - "directory from which templates will be used"), - OPT_CALLBACK(0 , "reference", &option_reference, "repo", - "reference repository", &opt_parse_reference), - OPT_STRING('o', "origin", &option_origin, "name", - "use <name> instead of 'origin' to track upstream"), - OPT_STRING('b', "branch", &option_branch, "branch", - "checkout <branch> instead of the remote's HEAD"), - OPT_STRING('u', "upload-pack", &option_upload_pack, "path", - "path to git-upload-pack on the remote"), - OPT_STRING(0, "depth", &option_depth, "depth", - "create a shallow clone of that depth"), + N_("initialize submodules in the clone")), + OPT_STRING(0, "template", &option_template, N_("template-directory"), + N_("directory from which templates will be used")), + OPT_CALLBACK(0 , "reference", &option_reference, N_("repo"), + N_("reference repository"), &opt_parse_reference), + OPT_STRING('o', "origin", &option_origin, N_("name"), + N_("use <name> instead of 'origin' to track upstream")), + OPT_STRING('b', "branch", &option_branch, N_("branch"), + N_("checkout <branch> instead of the remote's HEAD")), + OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"), + N_("path to git-upload-pack on the remote")), + OPT_STRING(0, "depth", &option_depth, N_("depth"), + N_("create a shallow clone of that depth")), OPT_BOOL(0, "single-branch", &option_single_branch, - "clone only one branch, HEAD or --branch"), - OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir", - "separate git dir from working tree"), - OPT_STRING_LIST('c', "config", &option_config, "key=value", - "set config inside the new repository"), + N_("clone only one branch, HEAD or --branch")), + OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), + N_("separate git dir from working tree")), + OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), + N_("set config inside the new repository")), OPT_END() }; diff --git a/builtin/column.c b/builtin/column.c index 5ea798a7ca..e125a55fc9 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -6,7 +6,7 @@ #include "column.h" static const char * const builtin_column_usage[] = { - "git column [options]", + N_("git column [options]"), NULL }; static unsigned int colopts; @@ -23,13 +23,13 @@ int cmd_column(int argc, const char **argv, const char *prefix) struct column_options copts; const char *command = NULL, *real_command = NULL; struct option options[] = { - OPT_STRING(0, "command", &real_command, "name", "lookup config vars"), - OPT_COLUMN(0, "mode", &colopts, "layout to use"), - OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"), - OPT_INTEGER(0, "width", &copts.width, "Maximum width"), - OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"), - OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"), - OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"), + OPT_STRING(0, "command", &real_command, N_("name"), N_("lookup config vars")), + OPT_COLUMN(0, "mode", &colopts, N_("layout to use")), + OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")), + OPT_INTEGER(0, "width", &copts.width, N_("Maximum width")), + OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("Padding space on left border")), + OPT_INTEGER(0, "nl", &copts.nl, N_("Padding space on right border")), + OPT_INTEGER(0, "padding", &copts.padding, N_("Padding space between columns")), OPT_END() }; diff --git a/builtin/commit.c b/builtin/commit.c index 20cef95d60..778cf16fde 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -30,12 +30,12 @@ #include "column.h" static const char * const builtin_commit_usage[] = { - "git commit [options] [--] <filepattern>...", + N_("git commit [options] [--] <filepattern>..."), NULL }; static const char * const builtin_status_usage[] = { - "git status [options] [--] <filepattern>...", + N_("git status [options] [--] <filepattern>..."), NULL }; @@ -478,6 +478,20 @@ static void export_one(const char *var, const char *s, const char *e, int hack) strbuf_release(&buf); } +static int sane_ident_split(struct ident_split *person) +{ + if (!person->name_begin || !person->name_end || + person->name_begin == person->name_end) + return 0; /* no human readable name */ + if (!person->mail_begin || !person->mail_end || + person->mail_begin == person->mail_end) + return 0; /* no usable mail */ + if (!person->date_begin || !person->date_end || + !person->tz_begin || !person->tz_end) + return 0; + return 1; +} + static void determine_author_info(struct strbuf *author_ident) { char *name, *email, *date; @@ -530,7 +544,8 @@ static void determine_author_info(struct strbuf *author_ident) if (force_date) date = force_date; strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT)); - if (!split_ident_line(&author, author_ident->buf, author_ident->len)) { + if (!split_ident_line(&author, author_ident->buf, author_ident->len) && + sane_ident_split(&author)) { export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@'); @@ -1169,26 +1184,26 @@ int cmd_status(int argc, const char **argv, const char *prefix) int fd; unsigned char sha1[20]; static struct option builtin_status_options[] = { - OPT__VERBOSE(&verbose, "be verbose"), + OPT__VERBOSE(&verbose, N_("be verbose")), OPT_SET_INT('s', "short", &status_format, - "show status concisely", STATUS_FORMAT_SHORT), + N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOLEAN('b', "branch", &s.show_branch, - "show branch information"), + N_("show branch information")), OPT_SET_INT(0, "porcelain", &status_format, - "machine-readable output", + N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &s.null_termination, - "terminate entries with NUL"), + N_("terminate entries with NUL")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, - "mode", - "show untracked files, optional modes: all, normal, no. (Default: all)", + 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, - "show ignored files"), - { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when", - "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)", + 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" }, - OPT_COLUMN(0, "column", &s.colopts, "list untracked files in columns"), + OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")), OPT_END(), }; @@ -1369,53 +1384,53 @@ int cmd_commit(int argc, const char **argv, const char *prefix) { static struct wt_status s; static struct option builtin_commit_options[] = { - OPT__QUIET(&quiet, "suppress summary after successful commit"), - OPT__VERBOSE(&verbose, "show diff in commit message template"), - - OPT_GROUP("Commit message options"), - OPT_FILENAME('F', "file", &logfile, "read message from file"), - OPT_STRING(0, "author", &force_author, "author", "override author for commit"), - OPT_STRING(0, "date", &force_date, "date", "override date for commit"), - OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m), - OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"), - OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"), - OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"), - OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"), - OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"), - OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"), - OPT_FILENAME('t', "template", &template_file, "use specified template file"), - OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"), - OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"), - OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id", - "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + OPT__QUIET(&quiet, N_("suppress summary after successful commit")), + OPT__VERBOSE(&verbose, N_("show diff in commit message template")), + + OPT_GROUP(N_("Commit message options")), + OPT_FILENAME('F', "file", &logfile, N_("read message from file")), + OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")), + OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")), + OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m), + OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")), + 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_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")), + { 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("Commit contents options"), - OPT_BOOLEAN('a', "all", &all, "commit all changed files"), - OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"), - OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"), - OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"), - OPT_BOOLEAN('o', "only", &only, "commit only specified files"), - OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"), - OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"), - OPT_SET_INT(0, "short", &status_format, "show status concisely", + 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_SET_INT(0, "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), - OPT_BOOLEAN(0, "branch", &s.show_branch, "show branch information"), + OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")), OPT_SET_INT(0, "porcelain", &status_format, - "machine-readable output", STATUS_FORMAT_PORCELAIN), + N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &s.null_termination, - "terminate entries with NUL"), - OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), - OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"), - { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + 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")), + { 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, - "ok to record an empty change", + N_("ok to record an empty change"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL, - "ok to record a change with an empty message", + N_("ok to record a change with an empty message"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END() diff --git a/builtin/config.c b/builtin/config.c index ada6e12114..e1c33e0691 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -4,7 +4,7 @@ #include "parse-options.h" static const char *const builtin_config_usage[] = { - "git config [options]", + N_("git config [options]"), NULL }; @@ -49,33 +49,33 @@ static int respect_includes = -1; #define TYPE_PATH (1<<3) static struct option builtin_config_options[] = { - OPT_GROUP("Config file location"), - OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"), - OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"), - OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"), - OPT_STRING('f', "file", &given_config_file, "file", "use given config file"), - OPT_GROUP("Action"), - OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET), - OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL), - OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP), - OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL), - OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD), - OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET), - OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL), - OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION), - OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION), - OPT_BIT('l', "list", &actions, "list all", ACTION_LIST), - OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT), - OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"), - OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"), - OPT_GROUP("Type"), - OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL), - OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT), - OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT), - OPT_BIT(0, "path", &types, "value is a path (file or directory name)", TYPE_PATH), - OPT_GROUP("Other"), - OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"), - OPT_BOOL(0, "includes", &respect_includes, "respect include directives on lookup"), + 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_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")), + 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, "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), + OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL), + OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION), + OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION), + OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST), + OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT), + OPT_STRING(0, "get-color", &get_color_slot, N_("slot"), N_("find the color configured: [default]")), + OPT_STRING(0, "get-colorbool", &get_colorbool_slot, N_("slot"), N_("find the color setting: [stdout-is-tty]")), + OPT_GROUP(N_("Type")), + OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL), + OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT), + 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(0, "includes", &respect_includes, N_("respect include directives on lookup")), OPT_END(), }; @@ -400,8 +400,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) */ die("$HOME not set"); - if (access(user_config, R_OK) && - xdg_config && !access(xdg_config, R_OK)) + if (access_or_warn(user_config, R_OK) && + xdg_config && !access_or_warn(xdg_config, R_OK)) given_config_file = xdg_config; else given_config_file = user_config; diff --git a/builtin/count-objects.c b/builtin/count-objects.c index c37cb98c31..9afaa88f77 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -66,7 +66,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose, } static char const * const count_objects_usage[] = { - "git count-objects [-v]", + N_("git count-objects [-v]"), NULL }; @@ -79,7 +79,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0; off_t loose_size = 0; struct option opts[] = { - OPT__VERBOSE(&verbose, "be verbose"), + OPT__VERBOSE(&verbose, N_("be verbose")), OPT_END(), }; diff --git a/builtin/describe.c b/builtin/describe.c index 9f63067f50..9fe11ed9de 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -12,8 +12,8 @@ #define MAX_TAGS (FLAG_BITS - 1) static const char * const describe_usage[] = { - "git describe [options] <committish>*", - "git describe [options] --dirty", + N_("git describe [options] <committish>*"), + N_("git describe [options] --dirty"), NULL }; @@ -400,22 +400,22 @@ int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; struct option options[] = { - OPT_BOOLEAN(0, "contains", &contains, "find the tag that comes after the commit"), - OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"), - OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"), - OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"), - OPT_BOOLEAN(0, "long", &longformat, "always use long format"), + 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 in .git/refs")), + OPT_BOOLEAN(0, "tags", &tags, N_("use any tag in .git/refs/tags")), + OPT_BOOLEAN(0, "long", &longformat, N_("always use long format")), OPT__ABBREV(&abbrev), OPT_SET_INT(0, "exact-match", &max_candidates, - "only output exact matches", 0), + N_("only output exact matches"), 0), OPT_INTEGER(0, "candidates", &max_candidates, - "consider <n> most recent tags (default: 10)"), - OPT_STRING(0, "match", &pattern, "pattern", - "only consider tags matching <pattern>"), + 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, - "show abbreviated commit object as fallback"), - {OPTION_STRING, 0, "dirty", &dirty, "mark", - "append <mark> on dirty working tree (default: \"-dirty\")", + 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"}, OPT_END(), }; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 9ab6db3fb0..12220ad8da 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -19,7 +19,7 @@ #include "quote.h" static const char *fast_export_usage[] = { - "git fast-export [rev-list-opts]", + N_("git fast-export [rev-list-opts]"), NULL }; @@ -632,24 +632,24 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) char *export_filename = NULL, *import_filename = NULL; struct option options[] = { OPT_INTEGER(0, "progress", &progress, - "show progress after <n> objects"), - OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode", - "select handling of signed tags", + N_("show progress after <n> objects")), + OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"), + N_("select handling of signed tags"), parse_opt_signed_tag_mode), - OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode", - "select handling of tags that tag filtered objects", + OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"), + N_("select handling of tags that tag filtered objects"), parse_opt_tag_of_filtered_mode), - OPT_STRING(0, "export-marks", &export_filename, "file", - "Dump marks to this file"), - OPT_STRING(0, "import-marks", &import_filename, "file", - "Import marks from this file"), + OPT_STRING(0, "export-marks", &export_filename, N_("file"), + 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, - "Fake a tagger when tags lack one"), + N_("Fake a tagger when tags lack one")), OPT_BOOLEAN(0, "full-tree", &full_tree, - "Output full tree for each commit"), + N_("Output full tree for each commit")), OPT_BOOLEAN(0, "use-done-feature", &use_done_feature, - "Use the done feature to terminate the stream"), - OPT_BOOL(0, "no-data", &no_data, "Skip output of blob data"), + 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.c b/builtin/fetch.c index bb9a0743ff..24be754e1b 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -14,12 +14,13 @@ #include "transport.h" #include "submodule.h" #include "connected.h" +#include "argv-array.h" static const char * const builtin_fetch_usage[] = { - "git fetch [<options>] [<repository> [<refspec>...]]", - "git fetch [<options>] <group>", - "git fetch --multiple [<options>] [(<repository> | <group>)...]", - "git fetch --all [<options>]", + N_("git fetch [<options>] [<repository> [<refspec>...]]"), + N_("git fetch [<options>] <group>"), + N_("git fetch --multiple [<options>] [(<repository> | <group>)...]"), + N_("git fetch --all [<options>]"), NULL }; @@ -56,36 +57,36 @@ static int option_parse_recurse_submodules(const struct option *opt, static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), OPT_BOOLEAN(0, "all", &all, - "fetch from all remotes"), + N_("fetch from all remotes")), OPT_BOOLEAN('a', "append", &append, - "append to .git/FETCH_HEAD instead of overwriting"), - OPT_STRING(0, "upload-pack", &upload_pack, "path", - "path to upload pack on remote end"), - OPT__FORCE(&force, "force overwrite of local branch"), + 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, - "fetch from multiple remotes"), + N_("fetch from multiple remotes")), OPT_SET_INT('t', "tags", &tags, - "fetch all tags and associated objects", TAGS_SET), + N_("fetch all tags and associated objects"), TAGS_SET), OPT_SET_INT('n', NULL, &tags, - "do not fetch all tags (--no-tags)", TAGS_UNSET), + N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), OPT_BOOLEAN('p', "prune", &prune, - "prune remote-tracking branches no longer on remote"), - { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "on-demand", - "control recursive fetching of submodules", + 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, - "dry run"), - OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"), + N_("dry run")), + OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")), OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, - "allow updating of HEAD ref"), - OPT_BOOL(0, "progress", &progress, "force progress reporting"), - OPT_STRING(0, "depth", &depth, "depth", - "deepen history of shallow clone"), - { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir", - "prepend this to submodule path output", PARSE_OPT_HIDDEN }, + N_("allow updating of HEAD ref")), + OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), + OPT_STRING(0, "depth", &depth, N_("depth"), + N_("deepen history of shallow clone")), + { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), + N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, { OPTION_STRING, 0, "recurse-submodules-default", &recurse_submodules_default, NULL, - "default mode for recursion", PARSE_OPT_HIDDEN }, + N_("default mode for recursion"), PARSE_OPT_HIDDEN }, OPT_END() }; @@ -323,7 +324,7 @@ static int update_local_ref(struct ref *ref, return r; } - if (in_merge_bases(current, &updated, 1)) { + if (in_merge_bases(current, updated)) { char quickref[83]; int r; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); @@ -841,38 +842,35 @@ static int add_remote_or_group(const char *name, struct string_list *list) return 1; } -static void add_options_to_argv(int *argc, const char **argv) +static void add_options_to_argv(struct argv_array *argv) { if (dry_run) - argv[(*argc)++] = "--dry-run"; + argv_array_push(argv, "--dry-run"); if (prune) - argv[(*argc)++] = "--prune"; + argv_array_push(argv, "--prune"); if (update_head_ok) - argv[(*argc)++] = "--update-head-ok"; + argv_array_push(argv, "--update-head-ok"); if (force) - argv[(*argc)++] = "--force"; + argv_array_push(argv, "--force"); if (keep) - argv[(*argc)++] = "--keep"; + argv_array_push(argv, "--keep"); if (recurse_submodules == RECURSE_SUBMODULES_ON) - argv[(*argc)++] = "--recurse-submodules"; + argv_array_push(argv, "--recurse-submodules"); else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) - argv[(*argc)++] = "--recurse-submodules=on-demand"; + argv_array_push(argv, "--recurse-submodules=on-demand"); if (verbosity >= 2) - argv[(*argc)++] = "-v"; + argv_array_push(argv, "-v"); if (verbosity >= 1) - argv[(*argc)++] = "-v"; + argv_array_push(argv, "-v"); else if (verbosity < 0) - argv[(*argc)++] = "-q"; + argv_array_push(argv, "-q"); } static int fetch_multiple(struct string_list *list) { int i, result = 0; - const char *argv[12] = { "fetch", "--append" }; - int argc = 2; - - add_options_to_argv(&argc, argv); + struct argv_array argv = ARGV_ARRAY_INIT; if (!append && !dry_run) { int errcode = truncate_fetch_head(); @@ -880,18 +878,22 @@ static int fetch_multiple(struct string_list *list) return errcode; } + argv_array_pushl(&argv, "fetch", "--append", NULL); + add_options_to_argv(&argv); + for (i = 0; i < list->nr; i++) { const char *name = list->items[i].string; - argv[argc] = name; - argv[argc + 1] = NULL; + argv_array_push(&argv, name); if (verbosity >= 0) printf(_("Fetching %s\n"), name); - if (run_command_v_opt(argv, RUN_GIT_CMD)) { + if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) { error(_("Could not fetch %s"), name); result = 1; } + argv_array_pop(&argv); } + argv_array_clear(&argv); return result; } @@ -1007,13 +1009,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) { - const char *options[10]; - int num_options = 0; - add_options_to_argv(&num_options, options); - result = fetch_populated_submodules(num_options, options, + struct argv_array options = ARGV_ARRAY_INIT; + + add_options_to_argv(&options); + result = fetch_populated_submodules(&options, submodule_prefix, recurse_submodules, verbosity < 0); + argv_array_clear(&options); } /* All names were strdup()ed or strndup()ed */ diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 2c4d435da1..e2e27b2c40 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -10,7 +10,7 @@ #include "gpg-interface.h" static const char * const fmt_merge_msg_usage[] = { - "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]", + N_("git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"), NULL }; @@ -650,16 +650,16 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) const char *message = NULL; int shortlog_len = -1; struct option options[] = { - { OPTION_INTEGER, 0, "log", &shortlog_len, "n", - "populate log with at most <n> entries from shortlog", + { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), + N_("populate log with at most <n> entries from shortlog"), PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN }, - { OPTION_INTEGER, 0, "summary", &shortlog_len, "n", - "alias for --log (deprecated)", + { OPTION_INTEGER, 0, "summary", &shortlog_len, N_("n"), + N_("alias for --log (deprecated)"), PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL, DEFAULT_MERGE_LOG_LEN }, - OPT_STRING('m', "message", &message, "text", - "use <text> as start of message"), - OPT_FILENAME('F', "file", &inpath, "file to read from"), + OPT_STRING('m', "message", &message, N_("text"), + N_("use <text> as start of message")), + OPT_FILENAME('F', "file", &inpath, N_("file to read from")), OPT_END() }; diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 0c5294e5e8..7f059c31df 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -976,7 +976,7 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset) } static char const * const for_each_ref_usage[] = { - "git for-each-ref [options] [<pattern>]", + N_("git for-each-ref [options] [<pattern>]"), NULL }; @@ -991,19 +991,19 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) struct option opts[] = { OPT_BIT('s', "shell", "e_style, - "quote placeholders suitably for shells", QUOTE_SHELL), + N_("quote placeholders suitably for shells"), QUOTE_SHELL), OPT_BIT('p', "perl", "e_style, - "quote placeholders suitably for perl", QUOTE_PERL), + N_("quote placeholders suitably for perl"), QUOTE_PERL), OPT_BIT(0 , "python", "e_style, - "quote placeholders suitably for python", QUOTE_PYTHON), + N_("quote placeholders suitably for python"), QUOTE_PYTHON), OPT_BIT(0 , "tcl", "e_style, - "quote placeholders suitably for tcl", QUOTE_TCL), + N_("quote placeholders suitably for tcl"), QUOTE_TCL), OPT_GROUP(""), - OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"), - OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), - OPT_CALLBACK(0 , "sort", sort_tail, "key", - "field name to sort on", &opt_parse_sort), + OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), + OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), + OPT_CALLBACK(0 , "sort", sort_tail, N_("key"), + N_("field name to sort on"), &opt_parse_sort), OPT_END(), }; diff --git a/builtin/fsck.c b/builtin/fsck.c index a710227a64..bb9a2cd447 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -605,23 +605,23 @@ static int fsck_cache_tree(struct cache_tree *it) } static char const * const fsck_usage[] = { - "git fsck [options] [<object>...]", + N_("git fsck [options] [<object>...]"), NULL }; static struct option fsck_opts[] = { - OPT__VERBOSE(&verbose, "be verbose"), - OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"), - OPT_BOOL(0, "dangling", &show_dangling, "show dangling objects"), - OPT_BOOLEAN(0, "tags", &show_tags, "report tags"), - OPT_BOOLEAN(0, "root", &show_root, "report root nodes"), - OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"), - OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"), - OPT_BOOLEAN(0, "full", &check_full, "also consider packs and alternate objects"), - OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"), + OPT__VERBOSE(&verbose, N_("be verbose")), + OPT_BOOLEAN(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, - "write dangling objects in .git/lost-found"), - OPT_BOOL(0, "progress", &show_progress, "show progress"), + 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 9b4232c8f3..6d46608fc9 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -19,7 +19,7 @@ #define FAILED_RUN "failed to run %s" static const char * const builtin_gc_usage[] = { - "git gc [options]", + N_("git gc [options]"), NULL }; @@ -174,12 +174,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix) int quiet = 0; struct option builtin_gc_options[] = { - OPT__QUIET(&quiet, "suppress progress reporting"), - { OPTION_STRING, 0, "prune", &prune_expire, "date", - "prune unreferenced objects", + 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, "be more thorough (increased runtime)"), - OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), + OPT_BOOLEAN(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), + OPT_BOOLEAN(0, "auto", &auto_gc, N_("enable auto-gc mode")), OPT_END() }; diff --git a/builtin/grep.c b/builtin/grep.c index 29adb0ac93..09ca4c980e 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -19,7 +19,7 @@ #include "dir.h" static char const * const grep_usage[] = { - "git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]", + N_("git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"), NULL }; @@ -260,6 +260,53 @@ static int wait_all(void) } #endif +static int parse_pattern_type_arg(const char *opt, const char *arg) +{ + if (!strcmp(arg, "default")) + return GREP_PATTERN_TYPE_UNSPECIFIED; + else if (!strcmp(arg, "basic")) + return GREP_PATTERN_TYPE_BRE; + else if (!strcmp(arg, "extended")) + return GREP_PATTERN_TYPE_ERE; + else if (!strcmp(arg, "fixed")) + return GREP_PATTERN_TYPE_FIXED; + else if (!strcmp(arg, "perl")) + return GREP_PATTERN_TYPE_PCRE; + die("bad %s argument: %s", opt, arg); +} + +static void grep_pattern_type_options(const int pattern_type, struct grep_opt *opt) +{ + switch (pattern_type) { + case GREP_PATTERN_TYPE_UNSPECIFIED: + /* fall through */ + + case GREP_PATTERN_TYPE_BRE: + opt->fixed = 0; + opt->pcre = 0; + opt->regflags &= ~REG_EXTENDED; + break; + + case GREP_PATTERN_TYPE_ERE: + opt->fixed = 0; + opt->pcre = 0; + opt->regflags |= REG_EXTENDED; + break; + + case GREP_PATTERN_TYPE_FIXED: + opt->fixed = 1; + opt->pcre = 0; + opt->regflags &= ~REG_EXTENDED; + break; + + case GREP_PATTERN_TYPE_PCRE: + opt->fixed = 0; + opt->pcre = 1; + opt->regflags &= ~REG_EXTENDED; + break; + } +} + static int grep_config(const char *var, const char *value, void *cb) { struct grep_opt *opt = cb; @@ -270,12 +317,17 @@ static int grep_config(const char *var, const char *value, void *cb) if (!strcmp(var, "grep.extendedregexp")) { if (git_config_bool(var, value)) - opt->regflags |= REG_EXTENDED; + opt->extended_regexp_option = 1; else - opt->regflags &= ~REG_EXTENDED; + opt->extended_regexp_option = 0; return 0; } + if (!strcmp(var, "grep.patterntype")) { + opt->pattern_type_option = parse_pattern_type_arg(var, value); + return 0; + } + if (!strcmp(var, "grep.linenumber")) { opt->linenum = git_config_bool(var, value); return 0; @@ -669,95 +721,88 @@ int cmd_grep(int argc, const char **argv, const char *prefix) int i; int dummy; int use_index = 1; - enum { - pattern_type_unspecified = 0, - pattern_type_bre, - pattern_type_ere, - pattern_type_fixed, - pattern_type_pcre, - }; - int pattern_type = pattern_type_unspecified; + int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; struct option options[] = { OPT_BOOLEAN(0, "cached", &cached, - "search in index instead of in the work tree"), + N_("search in index instead of in the work tree")), OPT_NEGBIT(0, "no-index", &use_index, - "finds in contents not managed by git", 1), + N_("find in contents not managed by git"), 1), OPT_BOOLEAN(0, "untracked", &untracked, - "search in both tracked and untracked files"), + N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, - "search also in ignored files", 1), + N_("search also in ignored files"), 1), OPT_GROUP(""), OPT_BOOLEAN('v', "invert-match", &opt.invert, - "show non-matching lines"), + N_("show non-matching lines")), OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, - "case insensitive matching"), + N_("case insensitive matching")), OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, - "match patterns only at word boundaries"), + N_("match patterns only at word boundaries")), OPT_SET_INT('a', "text", &opt.binary, - "process binary files as text", GREP_BINARY_TEXT), + N_("process binary files as text"), GREP_BINARY_TEXT), OPT_SET_INT('I', NULL, &opt.binary, - "don't match patterns in binary files", + N_("don't match patterns in binary files"), GREP_BINARY_NOMATCH), - { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth", - "descend at most <depth> levels", PARSE_OPT_NONEG, + { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), + N_("descend at most <depth> levels"), PARSE_OPT_NONEG, NULL, 1 }, OPT_GROUP(""), - OPT_SET_INT('E', "extended-regexp", &pattern_type, - "use extended POSIX regular expressions", - pattern_type_ere), - OPT_SET_INT('G', "basic-regexp", &pattern_type, - "use basic POSIX regular expressions (default)", - pattern_type_bre), - OPT_SET_INT('F', "fixed-strings", &pattern_type, - "interpret patterns as fixed strings", - pattern_type_fixed), - OPT_SET_INT('P', "perl-regexp", &pattern_type, - "use Perl-compatible regular expressions", - pattern_type_pcre), + OPT_SET_INT('E', "extended-regexp", &pattern_type_arg, + N_("use extended POSIX regular expressions"), + GREP_PATTERN_TYPE_ERE), + OPT_SET_INT('G', "basic-regexp", &pattern_type_arg, + N_("use basic POSIX regular expressions (default)"), + GREP_PATTERN_TYPE_BRE), + OPT_SET_INT('F', "fixed-strings", &pattern_type_arg, + N_("interpret patterns as fixed strings"), + GREP_PATTERN_TYPE_FIXED), + OPT_SET_INT('P', "perl-regexp", &pattern_type_arg, + N_("use Perl-compatible regular expressions"), + GREP_PATTERN_TYPE_PCRE), OPT_GROUP(""), - OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"), - OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1), - OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1), + OPT_BOOLEAN('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, - "show filenames relative to top directory", 1), + N_("show filenames relative to top directory"), 1), OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, - "show only filenames instead of matching lines"), + N_("show only filenames instead of matching lines")), OPT_BOOLEAN(0, "name-only", &opt.name_only, - "synonym for --files-with-matches"), + N_("synonym for --files-with-matches")), OPT_BOOLEAN('L', "files-without-match", &opt.unmatch_name_only, - "show only the names of files without match"), + N_("show only the names of files without match")), OPT_BOOLEAN('z', "null", &opt.null_following_name, - "print NUL after filenames"), + N_("print NUL after filenames")), OPT_BOOLEAN('c', "count", &opt.count, - "show the number of matches instead of matching lines"), - OPT__COLOR(&opt.color, "highlight matches"), + N_("show the number of matches instead of matching lines")), + OPT__COLOR(&opt.color, N_("highlight matches")), OPT_BOOLEAN(0, "break", &opt.file_break, - "print empty line between matches from different files"), + N_("print empty line between matches from different files")), OPT_BOOLEAN(0, "heading", &opt.heading, - "show filename only once above matches from same file"), + N_("show filename only once above matches from same file")), OPT_GROUP(""), - OPT_CALLBACK('C', "context", &opt, "n", - "show <n> context lines before and after matches", + OPT_CALLBACK('C', "context", &opt, N_("n"), + N_("show <n> context lines before and after matches"), context_callback), OPT_INTEGER('B', "before-context", &opt.pre_context, - "show <n> context lines before matches"), + N_("show <n> context lines before matches")), OPT_INTEGER('A', "after-context", &opt.post_context, - "show <n> context lines after matches"), - OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM", + 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, - "show a line with the function name before matches"), + N_("show a line with the function name before matches")), OPT_BOOLEAN('W', "function-context", &opt.funcbody, - "show the surrounding function"), + N_("show the surrounding function")), OPT_GROUP(""), - OPT_CALLBACK('f', NULL, &opt, "file", - "read patterns from file", file_callback), - { OPTION_CALLBACK, 'e', NULL, &opt, "pattern", - "match <pattern>", PARSE_OPT_NONEG, pattern_callback }, + OPT_CALLBACK('f', NULL, &opt, N_("file"), + N_("read patterns from file"), file_callback), + { OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"), + N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, - "combine patterns specified with -e", + N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOLEAN(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", @@ -769,16 +814,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, close_callback }, OPT__QUIET(&opt.status_only, - "indicate hit with exit status without output"), + N_("indicate hit with exit status without output")), OPT_BOOLEAN(0, "all-match", &opt.all_match, - "show only matches from files that match all patterns"), + N_("show only matches from files that match all patterns")), OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, - "pager", "show matching files in the 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, - "allow calling of grep(1) (ignored by this build)"), - { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage", + 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() }; @@ -799,6 +844,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.header_tail = &opt.header_list; opt.regflags = REG_NEWLINE; opt.max_depth = -1; + opt.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED; + opt.extended_regexp_option = 0; strcpy(opt.color_context, ""); strcpy(opt.color_filename, ""); @@ -824,28 +871,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION | PARSE_OPT_NO_INTERNAL_HELP); - switch (pattern_type) { - case pattern_type_fixed: - opt.fixed = 1; - opt.pcre = 0; - break; - case pattern_type_bre: - opt.fixed = 0; - opt.pcre = 0; - opt.regflags &= ~REG_EXTENDED; - break; - case pattern_type_ere: - opt.fixed = 0; - opt.pcre = 0; - opt.regflags |= REG_EXTENDED; - break; - case pattern_type_pcre: - opt.fixed = 0; - opt.pcre = 1; - break; - default: - break; /* nothing */ - } + + if (pattern_type_arg != GREP_PATTERN_TYPE_UNSPECIFIED) + grep_pattern_type_options(pattern_type_arg, &opt); + else if (opt.pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED) + grep_pattern_type_options(opt.pattern_type_option, &opt); + else if (opt.extended_regexp_option) + grep_pattern_type_options(GREP_PATTERN_TYPE_ERE, &opt); if (use_index && !startup_info->have_repository) /* die the same way as if we did it at the beginning */ diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 33911fd5e9..8d184f1a99 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -57,8 +57,8 @@ static void hash_stdin_paths(const char *type, int write_objects) } static const char * const hash_object_usage[] = { - "git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...", - "git hash-object --stdin-paths < <list-of-paths>", + N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."), + N_("git hash-object --stdin-paths < <list-of-paths>"), NULL }; @@ -69,12 +69,12 @@ static int stdin_paths; static const char *vpath; static const struct option hash_object_options[] = { - OPT_STRING('t', NULL, &type, "type", "object type"), - OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"), - OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"), - OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"), - OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"), - OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"), + 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_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")), OPT_END() }; diff --git a/builtin/help.c b/builtin/help.c index efea4f55e1..bd86253d83 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -40,17 +40,17 @@ static int show_all = 0; static unsigned int colopts; static enum help_format help_format = HELP_FORMAT_NONE; static struct option builtin_help_options[] = { - OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), - OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), - OPT_SET_INT('w', "web", &help_format, "show manual in web browser", + OPT_BOOLEAN('a', "all", &show_all, N_("print all available commands")), + OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN), + OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"), HELP_FORMAT_WEB), - OPT_SET_INT('i', "info", &help_format, "show info page", + OPT_SET_INT('i', "info", &help_format, N_("show info page"), HELP_FORMAT_INFO), OPT_END(), }; static const char * const builtin_help_usage[] = { - "git help [--all] [--man|--web|--info] [command]", + N_("git help [--all] [--man|--web|--info] [command]"), NULL }; diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 953dd3004e..43d364b8d5 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -291,7 +291,7 @@ static void parse_pack_header(void) if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) die(_("pack signature mismatch")); if (!pack_version_ok(hdr->hdr_version)) - die("pack version %"PRIu32" unsupported", + die(_("pack version %"PRIu32" unsupported"), ntohl(hdr->hdr_version)); nr_objects = ntohl(hdr->hdr_entries); @@ -1061,7 +1061,8 @@ static void resolve_deltas(void) int ret = pthread_create(&thread_data[i].thread, NULL, threaded_second_pass, thread_data + i); if (ret) - die("unable to create thread: %s", strerror(ret)); + die(_("unable to create thread: %s"), + strerror(ret)); } for (i = 0; i < nr_threads; i++) pthread_join(thread_data[i].thread, NULL); @@ -1108,7 +1109,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha * sizeof(*objects)); f = sha1fd(output_fd, curr_pack); fix_unresolved_deltas(f, nr_unresolved); - sprintf(msg, "completed with %d local objects", + sprintf(msg, _("completed with %d local objects"), nr_objects - nr_objects_initial); stop_progress_msg(&progress, msg); sha1close(f, tail_sha1, 0); @@ -1117,8 +1118,8 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha curr_pack, nr_objects, read_sha1, consumed_bytes-20); if (hashcmp(read_sha1, tail_sha1) != 0) - die("Unexpected tail checksum for %s " - "(disk corruption?)", curr_pack); + die(_("Unexpected tail checksum for %s " + "(disk corruption?)"), curr_pack); } if (nr_deltas != nr_resolved_deltas) die(Q_("pack has %d unresolved delta", @@ -1327,17 +1328,17 @@ static int git_index_pack_config(const char *k, const char *v, void *cb) if (!strcmp(k, "pack.indexversion")) { opts->version = git_config_int(k, v); if (opts->version > 2) - die("bad pack.indexversion=%"PRIu32, opts->version); + die(_("bad pack.indexversion=%"PRIu32), opts->version); return 0; } if (!strcmp(k, "pack.threads")) { nr_threads = git_config_int(k, v); if (nr_threads < 0) - die("invalid number of threads specified (%d)", + die(_("invalid number of threads specified (%d)"), nr_threads); #ifdef NO_PTHREADS if (nr_threads != 1) - warning("no threads support, ignoring %s", k); + warning(_("no threads support, ignoring %s"), k); nr_threads = 1; #endif return 0; @@ -1510,8 +1511,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) usage(index_pack_usage); #ifdef NO_PTHREADS if (nr_threads != 1) - warning("no threads support, " - "ignoring %s", arg); + warning(_("no threads support, " + "ignoring %s"), arg); nr_threads = 1; #endif } else if (!prefixcmp(arg, "--pack_header=")) { diff --git a/builtin/init-db.c b/builtin/init-db.c index 244fb7fc32..78aa3872dd 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -464,7 +464,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset) } static const char *const init_db_usage[] = { - "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]", + N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]"), NULL }; @@ -482,17 +482,17 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *template_dir = NULL; unsigned int flags = 0; const struct option init_db_options[] = { - OPT_STRING(0, "template", &template_dir, "template-directory", - "directory from which templates will be used"), + OPT_STRING(0, "template", &template_dir, N_("template-directory"), + N_("directory from which templates will be used")), OPT_SET_INT(0, "bare", &is_bare_repository_cfg, - "create a bare repository", 1), + N_("create a bare repository"), 1), { OPTION_CALLBACK, 0, "shared", &init_shared_repository, - "permissions", - "specify that the git repository is to be shared amongst several users", + N_("permissions"), + N_("specify that the git repository is to be shared amongst several users"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0}, - OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET), - OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir", - "separate git dir from working tree"), + OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET), + OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), + N_("separate git dir from working tree")), OPT_END() }; diff --git a/builtin/log.c b/builtin/log.c index ecc2793690..09cf43e6d4 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -34,8 +34,8 @@ static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; static const char * const builtin_log_usage[] = { - "git log [<options>] [<since>..<until>] [[--] <path>...]\n" - " or: git show [options] <object>...", + N_("git log [<options>] [<since>..<until>] [[--] <path>...]\n") + N_(" or: git show [options] <object>..."), NULL }; @@ -97,9 +97,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, int quiet = 0, source = 0; const struct option builtin_log_options[] = { - OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"), - OPT_BOOLEAN(0, "source", &source, "show source"), - { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options", + OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")), + OPT_BOOLEAN(0, "source", &source, N_("show source")), + { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"), PARSE_OPT_OPTARG, decorate_callback}, OPT_END() }; @@ -109,9 +109,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); - argc = setup_revisions(argc, argv, rev, opt); if (quiet) rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT; + argc = setup_revisions(argc, argv, rev, opt); /* Any arguments at this point are not recognized */ if (argc > 1) @@ -456,7 +456,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) init_revisions(&rev, prefix); rev.diff = 1; rev.always_show_header = 1; - rev.no_walk = 1; + rev.no_walk = REVISION_WALK_NO_WALK_SORTED; rev.diffopt.stat_width = -1; /* Scale to real terminal size */ memset(&opt, 0, sizeof(opt)); @@ -696,7 +696,7 @@ static int reopen_stdout(struct commit *commit, const char *subject, return 0; } -static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix) +static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) { struct rev_info check_rev; struct commit *commit; @@ -717,7 +717,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha init_patch_ids(ids); /* given a range a..b get all patch ids for b..a */ - init_revisions(&check_rev, prefix); + init_revisions(&check_rev, rev->prefix); + check_rev.max_parents = 1; o1->flags ^= UNINTERESTING; o2->flags ^= UNINTERESTING; add_pending_object(&check_rev, o1, "o1"); @@ -726,10 +727,6 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha die(_("revision walk setup failed")); while ((commit = get_revision(&check_rev)) != NULL) { - /* ignore merges */ - if (commit->parents && commit->parents->next) - continue; - add_commit_patch_id(commit, ids); } @@ -890,7 +887,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory) } static const char * const builtin_format_patch_usage[] = { - "git format-patch [options] [<since> | <revision range>]", + N_("git format-patch [options] [<since> | <revision range>]"), NULL }; @@ -1063,61 +1060,61 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) char *branch_name = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, - "use [PATCH n/m] even with a single patch", + N_("use [PATCH n/m] even with a single patch"), PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, - "use [PATCH] even with multiple patches", + N_("use [PATCH] even with multiple patches"), PARSE_OPT_NOARG, no_numbered_callback }, - OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"), + OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")), OPT_BOOLEAN(0, "stdout", &use_stdout, - "print patches to standard out"), + N_("print patches to standard out")), OPT_BOOLEAN(0, "cover-letter", &cover_letter, - "generate a cover letter"), + N_("generate a cover letter")), OPT_BOOLEAN(0, "numbered-files", &numbered_files, - "use simple number sequence for output file names"), - OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx", - "use <sfx> instead of '.patch'"), + N_("use simple number sequence for output file names")), + OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), + N_("use <sfx> instead of '.patch'")), OPT_INTEGER(0, "start-number", &start_number, - "start numbering patches at <n> instead of 1"), - { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix", - "Use [<prefix>] instead of [PATCH]", + N_("start numbering patches at <n> instead of 1")), + { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"), + N_("Use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback }, { OPTION_CALLBACK, 'o', "output-directory", &output_directory, - "dir", "store resulting files in <dir>", + N_("dir"), N_("store resulting files in <dir>"), PARSE_OPT_NONEG, output_directory_callback }, { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, - "don't strip/add [PATCH]", + N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOLEAN(0, "no-binary", &no_binary_diff, - "don't output binary diffs"), + N_("don't output binary diffs")), OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, - "don't include a patch matching a commit upstream"), + N_("don't include a patch matching a commit upstream")), { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL, - "show patch format instead of default (patch + stat)", + N_("show patch format instead of default (patch + stat)"), PARSE_OPT_NONEG | PARSE_OPT_NOARG }, - OPT_GROUP("Messaging"), - { OPTION_CALLBACK, 0, "add-header", NULL, "header", - "add email header", 0, header_callback }, - { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header", + OPT_GROUP(N_("Messaging")), + { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"), + N_("add email header"), 0, header_callback }, + { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"), 0, to_callback }, - { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", + { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"), 0, cc_callback }, - OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id", - "make first mail a reply to <message-id>"), - { OPTION_CALLBACK, 0, "attach", &rev, "boundary", - "attach the patch", PARSE_OPT_OPTARG, + OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), + N_("make first mail a reply to <message-id>")), + { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"), + N_("attach the patch"), PARSE_OPT_OPTARG, attach_callback }, - { OPTION_CALLBACK, 0, "inline", &rev, "boundary", - "inline the patch", + { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"), + N_("inline the patch"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback }, - { OPTION_CALLBACK, 0, "thread", &thread, "style", - "enable message threading, styles: shallow, deep", + { OPTION_CALLBACK, 0, "thread", &thread, N_("style"), + N_("enable message threading, styles: shallow, deep"), PARSE_OPT_OPTARG, thread_callback }, - OPT_STRING(0, "signature", &signature, "signature", - "add a signature"), + OPT_STRING(0, "signature", &signature, N_("signature"), + N_("add a signature")), OPT_BOOLEAN(0, "quiet", &quiet, - "don't print the patch filenames"), + N_("don't print the patch filenames")), OPT_END() }; @@ -1306,7 +1303,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } - get_patch_ids(&rev, &ids, prefix); + get_patch_ids(&rev, &ids); } if (!use_stdout) @@ -1443,7 +1440,7 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) } static const char * const cherry_usage[] = { - "git cherry [-v] [<upstream> [<head> [<limit>]]]", + N_("git cherry [-v] [<upstream> [<head> [<limit>]]]"), NULL }; @@ -1477,7 +1474,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) struct option options[] = { OPT__ABBREV(&abbrev), - OPT__VERBOSE(&verbose, "be verbose"), + OPT__VERBOSE(&verbose, N_("be verbose")), OPT_END() }; @@ -1508,10 +1505,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) } init_revisions(&revs, prefix); - revs.diff = 1; - revs.combine_merges = 0; - revs.ignore_merges = 1; - DIFF_OPT_SET(&revs.diffopt, RECURSIVE); + revs.max_parents = 1; if (add_pending_commit(head, &revs, 0)) die(_("Unknown commit %s"), head); @@ -1525,7 +1519,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) return 0; } - get_patch_ids(&revs, &ids, prefix); + get_patch_ids(&revs, &ids); if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) die(_("Unknown commit %s"), limit); @@ -1534,10 +1528,6 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); while ((commit = get_revision(&revs)) != NULL) { - /* ignore merges */ - if (commit->parents && commit->parents->next) - continue; - commit_list_insert(commit, &list); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 31b3f2d900..b5434af0c8 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -405,7 +405,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char } static const char * const ls_files_usage[] = { - "git ls-files [options] [<file>...]", + N_("git ls-files [options] [<file>...]"), NULL }; @@ -457,57 +457,57 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) struct dir_struct dir; struct option builtin_ls_files_options[] = { { OPTION_CALLBACK, 'z', NULL, NULL, NULL, - "paths are separated with NUL character", + N_("paths are separated with NUL character"), PARSE_OPT_NOARG, option_parse_z }, OPT_BOOLEAN('t', NULL, &show_tag, - "identify the file status with tags"), + N_("identify the file status with tags")), OPT_BOOLEAN('v', NULL, &show_valid_bit, - "use lowercase letters for 'assume unchanged' files"), + N_("use lowercase letters for 'assume unchanged' files")), OPT_BOOLEAN('c', "cached", &show_cached, - "show cached files in the output (default)"), + N_("show cached files in the output (default)")), OPT_BOOLEAN('d', "deleted", &show_deleted, - "show deleted files in the output"), + N_("show deleted files in the output")), OPT_BOOLEAN('m', "modified", &show_modified, - "show modified files in the output"), + N_("show modified files in the output")), OPT_BOOLEAN('o', "others", &show_others, - "show other files in the output"), + N_("show other files in the output")), OPT_BIT('i', "ignored", &dir.flags, - "show ignored files in the output", + N_("show ignored files in the output"), DIR_SHOW_IGNORED), OPT_BOOLEAN('s', "stage", &show_stage, - "show staged contents' object name in the output"), + N_("show staged contents' object name in the output")), OPT_BOOLEAN('k', "killed", &show_killed, - "show files on the filesystem that need to be removed"), + N_("show files on the filesystem that need to be removed")), OPT_BIT(0, "directory", &dir.flags, - "show 'other' directories' name only", + N_("show 'other' directories' name only"), DIR_SHOW_OTHER_DIRECTORIES), OPT_NEGBIT(0, "empty-directory", &dir.flags, - "don't show empty directories", + N_("don't show empty directories"), DIR_HIDE_EMPTY_DIRECTORIES), OPT_BOOLEAN('u', "unmerged", &show_unmerged, - "show unmerged files in the output"), + N_("show unmerged files in the output")), OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo, - "show resolve-undo information"), - { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern", - "skip files matching pattern", + N_("show resolve-undo information")), + { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], N_("pattern"), + N_("skip files matching pattern"), 0, option_parse_exclude }, - { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file", - "exclude patterns are read from <file>", + { OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"), + N_("exclude patterns are read from <file>"), 0, option_parse_exclude_from }, - OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file", - "read additional per-directory exclude patterns in <file>"), + OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"), + N_("read additional per-directory exclude patterns in <file>")), { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL, - "add the standard git exclusions", + N_("add the standard git exclusions"), PARSE_OPT_NOARG, option_parse_exclude_standard }, { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL, - "make the output relative to the project top directory", + N_("make the output relative to the project top directory"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL }, OPT_BOOLEAN(0, "error-unmatch", &error_unmatch, - "if any <file> is not in the index, treat this as an error"), - OPT_STRING(0, "with-tree", &with_tree, "tree-ish", - "pretend that paths removed since <tree-ish> are still present"), + 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, "show debugging data"), + OPT_BOOLEAN(0, "debug", &debug_mode, N_("show debugging data")), OPT_END() }; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 6b666e1e87..235c17cc01 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -24,7 +24,7 @@ static int chomp_prefix; static const char *ls_tree_prefix; static const char * const ls_tree_usage[] = { - "git ls-tree [<options>] <tree-ish> [<path>...]", + N_("git ls-tree [<options>] <tree-ish> [<path>...]"), NULL }; @@ -122,25 +122,25 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) struct tree *tree; int i, full_tree = 0; const struct option ls_tree_options[] = { - OPT_BIT('d', NULL, &ls_options, "only show trees", + OPT_BIT('d', NULL, &ls_options, N_("only show trees"), LS_TREE_ONLY), - OPT_BIT('r', NULL, &ls_options, "recurse into subtrees", + OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"), LS_RECURSIVE), - OPT_BIT('t', NULL, &ls_options, "show trees when recursing", + OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"), LS_SHOW_TREES), OPT_SET_INT('z', NULL, &line_termination, - "terminate entries with NUL byte", 0), - OPT_BIT('l', "long", &ls_options, "include object size", + N_("terminate entries with NUL byte"), 0), + OPT_BIT('l', "long", &ls_options, N_("include object size"), LS_SHOW_SIZE), - OPT_BIT(0, "name-only", &ls_options, "list only filenames", + OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"), LS_NAME_ONLY), - OPT_BIT(0, "name-status", &ls_options, "list only filenames", + OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"), LS_NAME_ONLY), OPT_SET_INT(0, "full-name", &chomp_prefix, - "use full path names", 0), + N_("use full path names"), 0), OPT_BOOLEAN(0, "full-tree", &full_tree, - "list entire tree; not just current directory " - "(implies --full-name)"), + N_("list entire tree; not just current directory " + "(implies --full-name)")), OPT__ABBREV(&abbrev), OPT_END() }; diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index eaf9e157a3..dd4f925475 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -481,36 +481,12 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg) return out; } -/* - * When there is no known charset, guess. - * - * Right now we assume that if the target is UTF-8 (the default), - * and it already looks like UTF-8 (which includes US-ASCII as its - * subset, of course) then that is what it is and there is nothing - * to do. - * - * Otherwise, we default to assuming it is Latin1 for historical - * reasons. - */ -static const char *guess_charset(const struct strbuf *line, const char *target_charset) -{ - if (is_encoding_utf8(target_charset)) { - if (is_utf8(line->buf)) - return NULL; - } - return "ISO8859-1"; -} - static void convert_to_utf8(struct strbuf *line, const char *charset) { char *out; - if (!charset || !*charset) { - charset = guess_charset(line, metainfo_charset); - if (!charset) - return; - } - + if (!charset || !*charset) + return; if (!strcasecmp(metainfo_charset, charset)) return; out = reencode_string(line->buf, metainfo_charset, charset); diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 4f30f1b0c8..1bc7991048 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -23,9 +23,10 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) } static const char * const merge_base_usage[] = { - "git merge-base [-a|--all] <commit> <commit>...", - "git merge-base [-a|--all] --octopus <commit>...", - "git merge-base --independent <commit>...", + N_("git merge-base [-a|--all] <commit> <commit>..."), + N_("git merge-base [-a|--all] --octopus <commit>..."), + N_("git merge-base --independent <commit>..."), + N_("git merge-base --is-ancestor <commit> <commit>"), NULL }; @@ -70,6 +71,20 @@ static int handle_octopus(int count, const char **args, int reduce, int show_all return 0; } +static int handle_is_ancestor(int argc, const char **argv) +{ + struct commit *one, *two; + + if (argc != 2) + die("--is-ancestor takes exactly two commits"); + one = get_commit_reference(argv[0]); + two = get_commit_reference(argv[1]); + if (in_merge_bases(one, two)) + return 0; + else + return 1; +} + int cmd_merge_base(int argc, const char **argv, const char *prefix) { struct commit **rev; @@ -77,11 +92,14 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) int show_all = 0; int octopus = 0; int reduce = 0; + int is_ancestor = 0; struct option options[] = { - OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"), - OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"), - OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"), + 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_END() }; @@ -89,6 +107,10 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); if (!octopus && !reduce && argc < 2) usage_with_options(merge_base_usage, options); + if (is_ancestor && (show_all | octopus | reduce)) + die("--is-ancestor cannot be used with other options"); + if (is_ancestor) + return handle_is_ancestor(argc, argv); if (reduce && (show_all || octopus)) die("--independent cannot be used with other options"); diff --git a/builtin/merge-file.c b/builtin/merge-file.c index 6f0efef43c..c0570f2407 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -5,7 +5,7 @@ #include "parse-options.h" static const char *const merge_file_usage[] = { - "git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2", + N_("git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"), NULL }; @@ -30,19 +30,19 @@ 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, "send results to standard output"), - OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3), - OPT_SET_INT(0, "ours", &xmp.favor, "for conflicts, use our version", + OPT_BOOLEAN('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), - OPT_SET_INT(0, "theirs", &xmp.favor, "for conflicts, use their version", + OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"), XDL_MERGE_FAVOR_THEIRS), - OPT_SET_INT(0, "union", &xmp.favor, "for conflicts, use a union version", + OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"), XDL_MERGE_FAVOR_UNION), OPT_INTEGER(0, "marker-size", &xmp.marker_size, - "for conflicts, use this marker size"), - OPT__QUIET(&quiet, "do not warn about conflicts"), - OPT_CALLBACK('L', NULL, names, "name", - "set labels for file1/orig_file/file2", &label_cb), + N_("for conflicts, use this marker size")), + OPT__QUIET(&quiet, N_("do not warn about conflicts")), + OPT_CALLBACK('L', NULL, names, N_("name"), + N_("set labels for file1/orig_file/file2"), &label_cb), OPT_END(), }; diff --git a/builtin/merge.c b/builtin/merge.c index e81fde6d79..0ec8f0d449 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -40,9 +40,9 @@ struct strategy { }; static const char * const builtin_merge_usage[] = { - "git merge [options] [<commit>...]", - "git merge [options] <msg> HEAD <commit>", - "git merge --abort", + N_("git merge [options] [<commit>...]"), + N_("git merge [options] <msg> HEAD <commit>"), + N_("git merge --abort"), NULL }; @@ -180,39 +180,39 @@ static int option_parse_n(const struct option *opt, static struct option builtin_merge_options[] = { { OPTION_CALLBACK, 'n', NULL, NULL, NULL, - "do not show a diffstat at the end of the merge", + N_("do not show a diffstat at the end of the merge"), PARSE_OPT_NOARG, option_parse_n }, OPT_BOOLEAN(0, "stat", &show_diffstat, - "show a diffstat at the end of the merge"), - OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"), - { OPTION_INTEGER, 0, "log", &shortlog_len, "n", - "add (at most <n>) entries from shortlog to merge commit message", + N_("show a diffstat at the end of the merge")), + OPT_BOOLEAN(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, - "create a single commit instead of doing a merge"), + N_("create a single commit instead of doing a merge")), OPT_BOOLEAN(0, "commit", &option_commit, - "perform a commit if the merge succeeds (default)"), + N_("perform a commit if the merge succeeds (default)")), OPT_BOOL('e', "edit", &option_edit, - "edit message before committing"), + N_("edit message before committing")), OPT_BOOLEAN(0, "ff", &allow_fast_forward, - "allow fast-forward (default)"), + N_("allow fast-forward (default)")), OPT_BOOLEAN(0, "ff-only", &fast_forward_only, - "abort if fast-forward is not possible"), + N_("abort if fast-forward is not possible")), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), - OPT_CALLBACK('s', "strategy", &use_strategies, "strategy", - "merge strategy to use", option_parse_strategy), - OPT_CALLBACK('X', "strategy-option", &xopts, "option=value", - "option for selected merge strategy", option_parse_x), - OPT_CALLBACK('m', "message", &merge_msg, "message", - "merge commit message (for a non-fast-forward merge)", + OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"), + N_("merge strategy to use"), option_parse_strategy), + OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"), + N_("option for selected merge strategy"), option_parse_x), + OPT_CALLBACK('m', "message", &merge_msg, N_("message"), + N_("merge commit message (for a non-fast-forward merge)"), option_parse_message), OPT__VERBOSITY(&verbosity), OPT_BOOLEAN(0, "abort", &abort_current_merge, - "abort the current in-progress merge"), - OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id", - "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"), + 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_END() }; diff --git a/builtin/mktree.c b/builtin/mktree.c index 4ae1c412d4..f92ba404ab 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -64,7 +64,7 @@ static void write_tree(unsigned char *sha1) } static const char *mktree_usage[] = { - "git mktree [-z] [--missing] [--batch]", + N_("git mktree [-z] [--missing] [--batch]"), NULL }; @@ -150,9 +150,9 @@ int cmd_mktree(int ac, const char **av, const char *prefix) int got_eof = 0; const struct option option[] = { - OPT_SET_INT('z', NULL, &line_termination, "input is NUL terminated", '\0'), - OPT_SET_INT( 0 , "missing", &allow_missing, "allow missing objects", 1), - OPT_SET_INT( 0 , "batch", &is_batch_mode, "allow creation of more than one tree", 1), + OPT_SET_INT('z', NULL, &line_termination, N_("input is NUL terminated"), '\0'), + OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1), + OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1), OPT_END() }; diff --git a/builtin/mv.c b/builtin/mv.c index 2a144b011c..034fec92a1 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -11,7 +11,7 @@ #include "parse-options.h" static const char * const builtin_mv_usage[] = { - "git mv [options] <source>... <destination>", + N_("git mv [options] <source>... <destination>"), NULL }; @@ -59,10 +59,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix) int i, newfd; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { - OPT__VERBOSE(&verbose, "be verbose"), - OPT__DRY_RUN(&show_only, "dry run"), - OPT__FORCE(&force, "force move/rename even if target exists"), - OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), + 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_END(), }; const char **source, **destination, **dest_path; diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 1b374583c2..6238247974 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -172,9 +172,9 @@ static void show_name(const struct object *obj, } static char const * const name_rev_usage[] = { - "git name-rev [options] <commit>...", - "git name-rev [options] --all", - "git name-rev [options] --stdin", + N_("git name-rev [options] <commit>..."), + N_("git name-rev [options] --all"), + N_("git name-rev [options] --stdin"), NULL }; @@ -226,16 +226,16 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0; struct name_ref_data data = { 0, 0, NULL }; struct option opts[] = { - OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), - OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"), - OPT_STRING(0, "refs", &data.ref_filter, "pattern", - "only use refs matching <pattern>"), + 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_STRING(0, "refs", &data.ref_filter, N_("pattern"), + N_("only use refs matching <pattern>")), OPT_GROUP(""), - OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), - OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), - OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"), + 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, - "show abbreviated commit object as fallback"), + N_("show abbreviated commit object as fallback")), OPT_END(), }; diff --git a/builtin/notes.c b/builtin/notes.c index 3644d140ec..554c80167c 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -20,71 +20,71 @@ #include "notes-merge.h" static const char * const git_notes_usage[] = { - "git notes [--ref <notes_ref>] [list [<object>]]", - "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]", - "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>", - "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]", - "git notes [--ref <notes_ref>] edit [<object>]", - "git notes [--ref <notes_ref>] show [<object>]", - "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>", - "git notes merge --commit [-v | -q]", - "git notes merge --abort [-v | -q]", - "git notes [--ref <notes_ref>] remove [<object>...]", - "git notes [--ref <notes_ref>] prune [-n | -v]", - "git notes [--ref <notes_ref>] get-ref", + N_("git notes [--ref <notes_ref>] [list [<object>]]"), + N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), + N_("git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"), + N_("git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), + N_("git notes [--ref <notes_ref>] edit [<object>]"), + N_("git notes [--ref <notes_ref>] show [<object>]"), + N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"), + N_("git notes merge --commit [-v | -q]"), + N_("git notes merge --abort [-v | -q]"), + N_("git notes [--ref <notes_ref>] remove [<object>...]"), + N_("git notes [--ref <notes_ref>] prune [-n | -v]"), + N_("git notes [--ref <notes_ref>] get-ref"), NULL }; static const char * const git_notes_list_usage[] = { - "git notes [list [<object>]]", + N_("git notes [list [<object>]]"), NULL }; static const char * const git_notes_add_usage[] = { - "git notes add [<options>] [<object>]", + N_("git notes add [<options>] [<object>]"), NULL }; static const char * const git_notes_copy_usage[] = { - "git notes copy [<options>] <from-object> <to-object>", - "git notes copy --stdin [<from-object> <to-object>]...", + N_("git notes copy [<options>] <from-object> <to-object>"), + N_("git notes copy --stdin [<from-object> <to-object>]..."), NULL }; static const char * const git_notes_append_usage[] = { - "git notes append [<options>] [<object>]", + N_("git notes append [<options>] [<object>]"), NULL }; static const char * const git_notes_edit_usage[] = { - "git notes edit [<object>]", + N_("git notes edit [<object>]"), NULL }; static const char * const git_notes_show_usage[] = { - "git notes show [<object>]", + N_("git notes show [<object>]"), NULL }; static const char * const git_notes_merge_usage[] = { - "git notes merge [<options>] <notes_ref>", - "git notes merge --commit [<options>]", - "git notes merge --abort [<options>]", + N_("git notes merge [<options>] <notes_ref>"), + N_("git notes merge --commit [<options>]"), + N_("git notes merge --abort [<options>]"), NULL }; static const char * const git_notes_remove_usage[] = { - "git notes remove [<object>]", + N_("git notes remove [<object>]"), NULL }; static const char * const git_notes_prune_usage[] = { - "git notes prune [<options>]", + N_("git notes prune [<options>]"), NULL }; static const char * const git_notes_get_ref_usage[] = { - "git notes get-ref", + N_("git notes get-ref"), NULL }; @@ -531,19 +531,19 @@ static int add(int argc, const char **argv, const char *prefix) const unsigned char *note; struct msg_arg msg = { 0, 0, STRBUF_INIT }; struct option options[] = { - { OPTION_CALLBACK, 'm', "message", &msg, "msg", - "note contents as a string", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'm', "message", &msg, N_("message"), + N_("note contents as a string"), PARSE_OPT_NONEG, parse_msg_arg}, - { OPTION_CALLBACK, 'F', "file", &msg, "file", - "note contents in a file", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'F', "file", &msg, N_("file"), + N_("note contents in a file"), PARSE_OPT_NONEG, parse_file_arg}, - { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object", - "reuse and edit specified note object", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"), + N_("reuse and edit specified note object"), PARSE_OPT_NONEG, parse_reedit_arg}, - { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object", - "reuse specified note object", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"), + N_("reuse specified note object"), PARSE_OPT_NONEG, parse_reuse_arg}, - OPT__FORCE(&force, "replace existing notes"), + OPT__FORCE(&force, N_("replace existing notes")), OPT_END() }; @@ -611,11 +611,11 @@ static int copy(int argc, const char **argv, const char *prefix) struct notes_tree *t; const char *rewrite_cmd = NULL; struct option options[] = { - OPT__FORCE(&force, "replace existing notes"), - OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"), - OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command", - "load rewriting config for <command> (implies " - "--stdin)"), + OPT__FORCE(&force, N_("replace existing notes")), + OPT_BOOLEAN(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)")), OPT_END() }; @@ -688,17 +688,17 @@ static int append_edit(int argc, const char **argv, const char *prefix) const char * const *usage; struct msg_arg msg = { 0, 0, STRBUF_INIT }; struct option options[] = { - { OPTION_CALLBACK, 'm', "message", &msg, "msg", - "note contents as a string", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'm', "message", &msg, N_("message"), + N_("note contents as a string"), PARSE_OPT_NONEG, parse_msg_arg}, - { OPTION_CALLBACK, 'F', "file", &msg, "file", - "note contents in a file", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'F', "file", &msg, N_("file"), + N_("note contents in a file"), PARSE_OPT_NONEG, parse_file_arg}, - { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object", - "reuse and edit specified note object", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"), + N_("reuse and edit specified note object"), PARSE_OPT_NONEG, parse_reedit_arg}, - { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object", - "reuse specified note object", PARSE_OPT_NONEG, + { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"), + N_("reuse specified note object"), PARSE_OPT_NONEG, parse_reuse_arg}, OPT_END() }; @@ -861,19 +861,19 @@ static int merge(int argc, const char **argv, const char *prefix) int verbosity = 0, result; const char *strategy = NULL; struct option options[] = { - OPT_GROUP("General options"), + OPT_GROUP(N_("General options")), OPT__VERBOSITY(&verbosity), - OPT_GROUP("Merge options"), - OPT_STRING('s', "strategy", &strategy, "strategy", - "resolve notes conflicts using the given strategy " - "(manual/ours/theirs/union/cat_sort_uniq)"), - OPT_GROUP("Committing unmerged notes"), + OPT_GROUP(N_("Merge options")), + OPT_STRING('s', "strategy", &strategy, N_("strategy"), + 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, - "finalize notes merge by committing unmerged notes", + N_("finalize notes merge by committing unmerged notes"), PARSE_OPT_NOARG | PARSE_OPT_NONEG }, - OPT_GROUP("Aborting notes merge resolution"), + OPT_GROUP(N_("Aborting notes merge resolution")), { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL, - "abort notes merge", + N_("abort notes merge"), PARSE_OPT_NOARG | PARSE_OPT_NONEG }, OPT_END() }; @@ -980,10 +980,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) int from_stdin = 0; struct option options[] = { OPT_BIT(0, "ignore-missing", &flag, - "attempt to remove non-existent note is not an error", + N_("attempt to remove non-existent note is not an error"), IGNORE_MISSING), OPT_BOOLEAN(0, "stdin", &from_stdin, - "read object names from the standard input"), + N_("read object names from the standard input")), OPT_END() }; struct notes_tree *t; @@ -1064,8 +1064,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix) int result; const char *override_notes_ref = NULL; struct option options[] = { - OPT_STRING(0, "ref", &override_notes_ref, "notes_ref", - "use notes from <notes_ref>"), + OPT_STRING(0, "ref", &override_notes_ref, N_("notes_ref"), + N_("use notes from <notes_ref>")), OPT_END() }; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 782e7d0c38..5e14064094 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -20,8 +20,8 @@ #include "thread-utils.h" static const char *pack_usage[] = { - "git pack-objects --stdout [options...] [< ref-list | < object-list]", - "git pack-objects [options...] base-name [< ref-list | < object-list]", + N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"), + N_("git pack-objects [options...] base-name [< ref-list | < object-list]"), NULL }; @@ -2445,67 +2445,67 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; struct option pack_objects_options[] = { OPT_SET_INT('q', "quiet", &progress, - "do not show progress meter", 0), + N_("do not show progress meter"), 0), OPT_SET_INT(0, "progress", &progress, - "show progress meter", 1), + N_("show progress meter"), 1), OPT_SET_INT(0, "all-progress", &progress, - "show progress meter during object writing phase", 2), + N_("show progress meter during object writing phase"), 2), OPT_BOOL(0, "all-progress-implied", &all_progress_implied, - "similar to --all-progress when progress meter is shown"), - { OPTION_CALLBACK, 0, "index-version", NULL, "version[,offset]", - "write the pack index file in the specified idx format version", + N_("similar to --all-progress when progress meter is shown")), + { OPTION_CALLBACK, 0, "index-version", NULL, N_("version[,offset]"), + N_("write the pack index file in the specified idx format version"), 0, option_parse_index_version }, OPT_ULONG(0, "max-pack-size", &pack_size_limit, - "maximum size of each output pack file"), + N_("maximum size of each output pack file")), OPT_BOOL(0, "local", &local, - "ignore borrowed objects from alternate object store"), + N_("ignore borrowed objects from alternate object store")), OPT_BOOL(0, "incremental", &incremental, - "ignore packed objects"), + N_("ignore packed objects")), OPT_INTEGER(0, "window", &window, - "limit pack window by objects"), + N_("limit pack window by objects")), OPT_ULONG(0, "window-memory", &window_memory_limit, - "limit pack window by memory in addition to object limit"), + N_("limit pack window by memory in addition to object limit")), OPT_INTEGER(0, "depth", &depth, - "maximum length of delta chain allowed in the resulting pack"), + N_("maximum length of delta chain allowed in the resulting pack")), OPT_BOOL(0, "reuse-delta", &reuse_delta, - "reuse existing deltas"), + N_("reuse existing deltas")), OPT_BOOL(0, "reuse-object", &reuse_object, - "reuse existing objects"), + N_("reuse existing objects")), OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta, - "use OFS_DELTA objects"), + N_("use OFS_DELTA objects")), OPT_INTEGER(0, "threads", &delta_search_threads, - "use threads when searching for best delta matches"), + N_("use threads when searching for best delta matches")), OPT_BOOL(0, "non-empty", &non_empty, - "do not create an empty pack output"), + N_("do not create an empty pack output")), OPT_BOOL(0, "revs", &use_internal_rev_list, - "read revision arguments from standard input"), + N_("read revision arguments from standard input")), { OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL, - "limit the objects to those that are not yet packed", + N_("limit the objects to those that are not yet packed"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, { OPTION_SET_INT, 0, "all", &rev_list_all, NULL, - "include objects reachable from any reference", + N_("include objects reachable from any reference"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL, - "include objects referred by reflog entries", + N_("include objects referred by reflog entries"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, OPT_BOOL(0, "stdout", &pack_to_stdout, - "output pack to stdout"), + N_("output pack to stdout")), OPT_BOOL(0, "include-tag", &include_tag, - "include tag objects that refer to objects to be packed"), + N_("include tag objects that refer to objects to be packed")), OPT_BOOL(0, "keep-unreachable", &keep_unreachable, - "keep unreachable objects"), - { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, "time", - "unpack unreachable objects newer than <time>", + N_("keep unreachable objects")), + { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"), + N_("unpack unreachable objects newer than <time>"), PARSE_OPT_OPTARG, option_parse_unpack_unreachable }, OPT_BOOL(0, "thin", &thin, - "create thin packs"), + N_("create thin packs")), OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep, - "ignore packs that have companion .keep file"), + N_("ignore packs that have companion .keep file")), OPT_INTEGER(0, "compression", &pack_compression_level, - "pack compression level"), + N_("pack compression level")), OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, - "do not hide commits by grafts", 0), + N_("do not hide commits by grafts"), 0), OPT_END(), }; diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index 39a9d89fbd..b5a0f88eb8 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -3,7 +3,7 @@ #include "pack-refs.h" static char const * const pack_refs_usage[] = { - "git pack-refs [options]", + N_("git pack-refs [options]"), NULL }; @@ -11,8 +11,8 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) { unsigned int flags = PACK_REFS_PRUNE; struct option opts[] = { - OPT_BIT(0, "all", &flags, "pack everything", PACK_REFS_ALL), - OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE), + OPT_BIT(0, "all", &flags, N_("pack everything"), PACK_REFS_ALL), + OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE), OPT_END(), }; if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0)) diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index b58a2e1eb2..83382c1fe1 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -4,7 +4,7 @@ #include "parse-options.h" static const char * const prune_packed_usage[] = { - "git prune-packed [-n|--dry-run] [-q|--quiet]", + N_("git prune-packed [-n|--dry-run] [-q|--quiet]"), NULL }; @@ -73,8 +73,8 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix) { int opts = isatty(2) ? VERBOSE : 0; const struct option prune_packed_options[] = { - OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN), - OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE), + OPT_BIT('n', "dry-run", &opts, N_("dry run"), DRY_RUN), + OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"), VERBOSE), OPT_END() }; diff --git a/builtin/prune.c b/builtin/prune.c index 6cb99443c1..9a03d24dad 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -9,7 +9,7 @@ #include "dir.h" static const char * const prune_usage[] = { - "git prune [-n] [-v] [--expire <time>] [--] [<head>...]", + N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"), NULL }; static int show_only; @@ -129,11 +129,11 @@ int cmd_prune(int argc, const char **argv, const char *prefix) struct rev_info revs; struct progress *progress = NULL; const struct option options[] = { - OPT__DRY_RUN(&show_only, "do not remove, show only"), - OPT__VERBOSE(&verbose, "report pruned objects"), - OPT_BOOL(0, "progress", &show_progress, "show progress"), + OPT__DRY_RUN(&show_only, N_("do not remove, show only")), + OPT__VERBOSE(&verbose, N_("report pruned objects")), + OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_DATE(0, "expire", &expire, - "expire objects older than <time>"), + N_("expire objects older than <time>")), OPT_END() }; char *s; diff --git a/builtin/push.c b/builtin/push.c index fdfcc6c716..db9ba30b08 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -11,7 +11,7 @@ #include "submodule.h" static const char * const push_usage[] = { - "git push [<options>] [<repository> [<refspec>...]]", + N_("git push [<options>] [<repository> [<refspec>...]]"), NULL, }; @@ -147,12 +147,37 @@ static void setup_push_upstream(struct remote *remote, int simple) add_refspec(refspec.buf); } +static char warn_unspecified_push_default_msg[] = +N_("push.default is unset; its implicit value is changing in\n" + "Git 2.0 from 'matching' to 'simple'. To squelch this message\n" + "and maintain the current behavior after the default changes, use:\n" + "\n" + " git config --global push.default matching\n" + "\n" + "To squelch this message and adopt the new behavior now, use:\n" + "\n" + " git config --global push.default simple\n" + "\n" + "See 'git help config' and search for 'push.default' for further information.\n" + "(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n" + "'current' instead of 'simple' if you sometimes use older versions of Git)"); + +static void warn_unspecified_push_default_configuration(void) +{ + static int warn_once; + + if (warn_once++) + return; + warning("%s\n", _(warn_unspecified_push_default_msg)); +} + static void setup_default_push_refspecs(struct remote *remote) { switch (push_default) { default: case PUSH_DEFAULT_UNSPECIFIED: default_matching_used = 1; + warn_unspecified_push_default_configuration(); /* fallthru */ case PUSH_DEFAULT_MATCHING: add_refspec(":"); @@ -186,8 +211,8 @@ static const char message_advice_pull_before_push[] = static const char message_advice_use_upstream[] = N_("Updates were rejected because a pushed branch tip is behind its remote\n" "counterpart. If you did not intend to push that branch, you may want to\n" - "specify branches to push or set the 'push.default' configuration\n" - "variable to 'current' or 'upstream' to push only the current branch."); + "specify branches to push or set the 'push.default' configuration variable\n" + "to 'simple', 'current' or 'upstream' to push only the current branch."); static const char message_advice_checkout_pull_push[] = N_("Updates were rejected because a pushed branch tip is behind its remote\n" @@ -354,25 +379,25 @@ int cmd_push(int argc, const char **argv, const char *prefix) const char *repo = NULL; /* default repository */ struct option options[] = { OPT__VERBOSITY(&verbosity), - OPT_STRING( 0 , "repo", &repo, "repository", "repository"), - OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL), - OPT_BIT( 0 , "mirror", &flags, "mirror all refs", + OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")), + 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, "delete refs"), - OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"), - OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN), - OPT_BIT( 0, "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN), - OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE), - { OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check", - "controls recursive pushing of submodules", + 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_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, "recurse-submodules", &flags, N_("check"), + N_("control recursive pushing of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, - OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"), - OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"), - OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"), - OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status", + OPT_BOOLEAN( 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"), TRANSPORT_PUSH_SET_UPSTREAM), - OPT_BOOL(0, "progress", &progress, "force progress reporting"), - OPT_BIT(0, "prune", &flags, "prune locally removed refs", + OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), + OPT_BIT(0, "prune", &flags, N_("prune locally removed refs"), TRANSPORT_PUSH_PRUNE), OPT_END() }; diff --git a/builtin/read-tree.c b/builtin/read-tree.c index df6c4c8819..042ac1b84f 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -33,7 +33,7 @@ static int list_tree(unsigned char *sha1) } static const char * const read_tree_usage[] = { - "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])", + N_("git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"), NULL }; @@ -104,37 +104,37 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) struct unpack_trees_options opts; int prefix_set = 0; const struct option read_tree_options[] = { - { OPTION_CALLBACK, 0, "index-output", NULL, "file", - "write resulting index to <file>", + { OPTION_CALLBACK, 0, "index-output", NULL, N_("file"), + N_("write resulting index to <file>"), PARSE_OPT_NONEG, index_output_cb }, OPT_SET_INT(0, "empty", &read_empty, - "only empty the index", 1), - OPT__VERBOSE(&opts.verbose_update, "be verbose"), - OPT_GROUP("Merging"), + N_("only empty the index"), 1), + OPT__VERBOSE(&opts.verbose_update, N_("be verbose")), + OPT_GROUP(N_("Merging")), OPT_SET_INT('m', NULL, &opts.merge, - "perform a merge in addition to a read", 1), + N_("perform a merge in addition to a read"), 1), OPT_SET_INT(0, "trivial", &opts.trivial_merges_only, - "3-way merge if no file level merging required", 1), + N_("3-way merge if no file level merging required"), 1), OPT_SET_INT(0, "aggressive", &opts.aggressive, - "3-way merge in presence of adds and removes", 1), + N_("3-way merge in presence of adds and removes"), 1), OPT_SET_INT(0, "reset", &opts.reset, - "same as -m, but discard unmerged entries", 1), - { OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/", - "read the tree into the index under <subdirectory>/", + N_("same as -m, but discard unmerged entries"), 1), + { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"), + N_("read the tree into the index under <subdirectory>/"), PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP }, OPT_SET_INT('u', NULL, &opts.update, - "update working tree with merge result", 1), + N_("update working tree with merge result"), 1), { OPTION_CALLBACK, 0, "exclude-per-directory", &opts, - "gitignore", - "allow explicitly ignored files to be overwritten", + N_("gitignore"), + N_("allow explicitly ignored files to be overwritten"), PARSE_OPT_NONEG, exclude_per_directory_cb }, OPT_SET_INT('i', NULL, &opts.index_only, - "don't check the working tree after merging", 1), - OPT__DRY_RUN(&opts.dry_run, "don't update the index or the work tree"), + N_("don't check the working tree after merging"), 1), + OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")), OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout, - "skip applying sparse checkout filter", 1), + N_("skip applying sparse checkout filter"), 1), OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack, - "debug unpack-trees", 1), + N_("debug unpack-trees"), 1), OPT_END() }; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 2cb854feb4..9145f1a595 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -480,7 +480,6 @@ static const char *update(struct command *cmd) !prefixcmp(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; - struct commit_list *bases, *ent; old_object = parse_object(old_sha1); new_object = parse_object(new_sha1); @@ -493,12 +492,7 @@ static const char *update(struct command *cmd) } old_commit = (struct commit *)old_object; new_commit = (struct commit *)new_object; - bases = get_merge_bases(old_commit, new_commit, 1); - for (ent = bases; ent; ent = ent->next) - if (!hashcmp(old_sha1, ent->item->object.sha1)) - break; - free_commit_list(bases); - if (!ent) { + if (!in_merge_bases(old_commit, new_commit)) { rp_error("denying non-fast-forward %s" " (you should pull first)", name); return "non-fast-forward"; diff --git a/builtin/remote.c b/builtin/remote.c index 920262d76e..a5a4b23231 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -8,66 +8,66 @@ #include "refs.h" static const char * const builtin_remote_usage[] = { - "git remote [-v | --verbose]", - "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>", - "git remote rename <old> <new>", - "git remote rm <name>", - "git remote set-head <name> (-a | -d | <branch>)", - "git remote [-v | --verbose] show [-n] <name>", - "git remote prune [-n | --dry-run] <name>", - "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]", - "git remote set-branches [--add] <name> <branch>...", - "git remote set-url [--push] <name> <newurl> [<oldurl>]", - "git remote set-url --add <name> <newurl>", - "git remote set-url --delete <name> <url>", + N_("git remote [-v | --verbose]"), + N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>"), + N_("git remote rename <old> <new>"), + N_("git remote remove <name>"), + N_("git remote set-head <name> (-a | -d | <branch>)"), + N_("git remote [-v | --verbose] show [-n] <name>"), + N_("git remote prune [-n | --dry-run] <name>"), + N_("git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"), + N_("git remote set-branches [--add] <name> <branch>..."), + N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"), + N_("git remote set-url --add <name> <newurl>"), + N_("git remote set-url --delete <name> <url>"), NULL }; static const char * const builtin_remote_add_usage[] = { - "git remote add [<options>] <name> <url>", + N_("git remote add [<options>] <name> <url>"), NULL }; static const char * const builtin_remote_rename_usage[] = { - "git remote rename <old> <new>", + N_("git remote rename <old> <new>"), NULL }; static const char * const builtin_remote_rm_usage[] = { - "git remote rm <name>", + N_("git remote remove <name>"), NULL }; static const char * const builtin_remote_sethead_usage[] = { - "git remote set-head <name> (-a | -d | <branch>])", + N_("git remote set-head <name> (-a | -d | <branch>])"), NULL }; static const char * const builtin_remote_setbranches_usage[] = { - "git remote set-branches <name> <branch>...", - "git remote set-branches --add <name> <branch>...", + N_("git remote set-branches <name> <branch>..."), + N_("git remote set-branches --add <name> <branch>..."), NULL }; static const char * const builtin_remote_show_usage[] = { - "git remote show [<options>] <name>", + N_("git remote show [<options>] <name>"), NULL }; static const char * const builtin_remote_prune_usage[] = { - "git remote prune [<options>] <name>", + N_("git remote prune [<options>] <name>"), NULL }; static const char * const builtin_remote_update_usage[] = { - "git remote update [<options>] [<group> | <remote>]...", + N_("git remote update [<options>] [<group> | <remote>]..."), NULL }; static const char * const builtin_remote_seturl_usage[] = { - "git remote set-url [--push] <name> <newurl> [<oldurl>]", - "git remote set-url --add <name> <newurl>", - "git remote set-url --delete <name> <url>", + N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"), + N_("git remote set-url --add <name> <newurl>"), + N_("git remote set-url --delete <name> <url>"), NULL }; @@ -160,17 +160,17 @@ static int add(int argc, const char **argv) int i; struct option options[] = { - OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"), + OPT_BOOLEAN('f', "fetch", &fetch, N_("fetch the remote branches")), OPT_SET_INT(0, "tags", &fetch_tags, - "import all tags and associated objects when fetching", + N_("import all tags and associated objects when fetching"), TAGS_SET), OPT_SET_INT(0, NULL, &fetch_tags, - "or do not fetch any tag at all (--no-tags)", TAGS_UNSET), - OPT_STRING_LIST('t', "track", &track, "branch", - "branch(es) to track"), - OPT_STRING('m', "master", &master, "branch", "master branch"), - { OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch", - "set up remote as a mirror to push to or fetch from", + N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET), + OPT_STRING_LIST('t', "track", &track, N_("branch"), + N_("branch(es) to track")), + OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")), + { OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"), + N_("set up remote as a mirror to push to or fetch from"), PARSE_OPT_OPTARG, parse_mirror_opt }, OPT_END() }; @@ -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, "do not query remotes"), + OPT_BOOLEAN('n', NULL, &no_query, N_("do not query remotes")), OPT_END() }; struct ref_states states; @@ -1196,9 +1196,9 @@ static int set_head(int argc, const char **argv) struct option options[] = { OPT_BOOLEAN('a', "auto", &opt_a, - "set refs/remotes/<name>/HEAD according to remote"), + N_("set refs/remotes/<name>/HEAD according to remote")), OPT_BOOLEAN('d', "delete", &opt_d, - "delete refs/remotes/<name>/HEAD"), + N_("delete refs/remotes/<name>/HEAD")), OPT_END() }; argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage, @@ -1250,7 +1250,7 @@ static int prune(int argc, const char **argv) { int dry_run = 0, result = 0; struct option options[] = { - OPT__DRY_RUN(&dry_run, "dry run"), + OPT__DRY_RUN(&dry_run, N_("dry run")), OPT_END() }; @@ -1318,7 +1318,7 @@ static int update(int argc, const char **argv) int i, prune = 0; struct option options[] = { OPT_BOOLEAN('p', "prune", &prune, - "prune remotes after fetching"), + 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, "add branch"), + OPT_BOOLEAN('\0', "add", &add_mode, N_("add branch")), OPT_END() }; @@ -1433,11 +1433,11 @@ static int set_url(int argc, const char **argv) struct strbuf name_buf = STRBUF_INIT; struct option options[] = { OPT_BOOLEAN('\0', "push", &push_mode, - "manipulate push URLs"), + N_("manipulate push URLs")), OPT_BOOLEAN('\0', "add", &add_mode, - "add URL"), + N_("add URL")), OPT_BOOLEAN('\0', "delete", &delete_mode, - "delete URLs"), + N_("delete URLs")), OPT_END() }; argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage, @@ -1566,7 +1566,7 @@ static int show_all(void) int cmd_remote(int argc, const char **argv, const char *prefix) { struct option options[] = { - OPT__VERBOSE(&verbose, "be verbose; must be placed before a subcommand"), + OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")), OPT_END() }; int result; @@ -1580,7 +1580,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix) result = add(argc, argv); else if (!strcmp(argv[0], "rename")) result = mv(argc, argv); - else if (!strcmp(argv[0], "rm")) + else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) result = rm(argc, argv); else if (!strcmp(argv[0], "set-head")) result = set_head(argc, argv); diff --git a/builtin/replace.c b/builtin/replace.c index 4a8970e9c9..e3aaf70203 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -14,9 +14,9 @@ #include "parse-options.h" static const char * const git_replace_usage[] = { - "git replace [-f] <object> <replacement>", - "git replace -d <object>...", - "git replace -l [<pattern>]", + N_("git replace [-f] <object> <replacement>"), + N_("git replace -d <object>..."), + N_("git replace -l [<pattern>]"), NULL }; @@ -115,9 +115,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, "list replace refs"), - OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"), - OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"), + 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_END() }; diff --git a/builtin/rerere.c b/builtin/rerere.c index 08213c7c0b..dc1708e6d6 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -8,7 +8,7 @@ #include "xdiff-interface.h" static const char * const rerere_usage[] = { - "git rerere [clear | forget path... | status | remaining | diff | gc]", + N_("git rerere [clear | forget path... | status | remaining | diff | gc]"), NULL, }; @@ -53,7 +53,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_SET_INT(0, "rerere-autoupdate", &autoupdate, - "register clean resolutions in index", 1), + N_("register clean resolutions in index"), 1), OPT_END(), }; diff --git a/builtin/reset.c b/builtin/reset.c index 74442bd766..915cc9f86f 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -22,9 +22,9 @@ #include "cache-tree.h" static const char * const git_reset_usage[] = { - "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]", - "git reset [-q] <commit> [--] <paths>...", - "git reset --patch [<commit>] [--] [<paths>...]", + N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), + N_("git reset [-q] <commit> [--] <paths>..."), + N_("git reset --patch [<commit>] [--] [<paths>...]"), NULL }; @@ -235,17 +235,17 @@ int cmd_reset(int argc, const char **argv, const char *prefix) struct commit *commit; struct strbuf msg = STRBUF_INIT; const struct option options[] = { - OPT__QUIET(&quiet, "be quiet, only report errors"), + OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, - "reset HEAD and index", MIXED), - OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT), + N_("reset HEAD and index"), MIXED), + OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), OPT_SET_INT(0, "hard", &reset_type, - "reset HEAD, index and working tree", HARD), + N_("reset HEAD, index and working tree"), HARD), OPT_SET_INT(0, "merge", &reset_type, - "reset HEAD, index and working tree", MERGE), + N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, - "reset HEAD but keep local changes", KEEP), - OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), + N_("reset HEAD but keep local changes"), KEEP), + OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_END() }; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 25e225f067..f267a1d3b5 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -336,15 +336,15 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) { static int keep_dashdash = 0, stop_at_non_option = 0; static char const * const parseopt_usage[] = { - "git rev-parse --parseopt [options] -- [<args>...]", + N_("git rev-parse --parseopt [options] -- [<args>...]"), NULL }; static struct option parseopt_opts[] = { OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash, - "keep the `--` passed as an arg"), + N_("keep the `--` passed as an arg")), OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option, - "stop parsing after the " - "first non-option argument"), + N_("stop parsing after the " + "first non-option argument")), OPT_END(), }; @@ -461,11 +461,11 @@ static void die_no_single_rev(int quiet) } static const char builtin_rev_parse_usage[] = -"git rev-parse --parseopt [options] -- [<args>...]\n" -" or: git rev-parse --sq-quote [<arg>...]\n" -" or: git rev-parse [options] [<arg>...]\n" -"\n" -"Run \"git rev-parse --parseopt -h\" for more information on the first usage."; +N_("git rev-parse --parseopt [options] -- [<args>...]\n" + " or: git rev-parse --sq-quote [<arg>...]\n" + " or: git rev-parse [options] [<arg>...]\n" + "\n" + "Run \"git rev-parse --parseopt -h\" for more information on the first usage."); int cmd_rev_parse(int argc, const char **argv, const char *prefix) { diff --git a/builtin/revert.c b/builtin/revert.c index 82d1bf844b..c5e36b94c0 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -19,14 +19,14 @@ */ static const char * const revert_usage[] = { - "git revert [options] <commit-ish>", - "git revert <subcommand>", + N_("git revert [options] <commit-ish>"), + N_("git revert <subcommand>"), NULL }; static const char * const cherry_pick_usage[] = { - "git cherry-pick [options] <commit-ish>", - "git cherry-pick <subcommand>", + N_("git cherry-pick [options] <commit-ish>"), + N_("git cherry-pick <subcommand>"), NULL }; @@ -100,18 +100,19 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) int contin = 0; int rollback = 0; struct option options[] = { - OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"), - OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"), - OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"), - OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"), - OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"), + 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_NOOP_NOARG('r', NULL), - OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"), - OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"), + OPT_BOOLEAN('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, "strategy", "merge strategy"), - OPT_CALLBACK('X', "strategy-option", &opts, "option", - "option for merge strategy", option_parse_x), + OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")), + OPT_CALLBACK('X', "strategy-option", &opts, N_("option"), + N_("option for merge strategy"), option_parse_x), + OPT_END(), OPT_END(), OPT_END(), OPT_END(), @@ -121,10 +122,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, "append commit name"), - OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"), - OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"), - OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"), + 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_END(), }; if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra)) @@ -193,7 +195,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts) struct setup_revision_opt s_r_opt; opts->revs = xmalloc(sizeof(*opts->revs)); init_revisions(opts->revs, NULL); - opts->revs->no_walk = 1; + opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED; if (argc < 2) usage_with_options(usage_str, options); memset(&s_r_opt, 0, sizeof(s_r_opt)); diff --git a/builtin/rm.c b/builtin/rm.c index 90c8a5047c..b384c4c3cf 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -11,7 +11,7 @@ #include "parse-options.h" static const char * const builtin_rm_usage[] = { - "git rm [options] [--] <file>...", + N_("git rm [options] [--] <file>..."), NULL }; @@ -130,13 +130,13 @@ static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; static int ignore_unmatch = 0; static struct option builtin_rm_options[] = { - OPT__DRY_RUN(&show_only, "dry run"), - OPT__QUIET(&quiet, "do not list removed files"), - OPT_BOOLEAN( 0 , "cached", &index_only, "only remove from the index"), - OPT__FORCE(&force, "override the up-to-date check"), - OPT_BOOLEAN('r', NULL, &recursive, "allow recursive removal"), + 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__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, - "exit with a zero status even if nothing matched"), + N_("exit with a zero status even if nothing matched")), OPT_END(), }; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 37f3193a33..b316cf37ca 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -10,9 +10,9 @@ #include "parse-options.h" static char const * const shortlog_usage[] = { - "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]", + N_("git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"), "", - "[rev-opts] are documented in git-rev-list(1)", + N_("[rev-opts] are documented in git-rev-list(1)"), NULL }; @@ -250,13 +250,13 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) static const struct option options[] = { OPT_BOOLEAN('n', "numbered", &log.sort_by_number, - "sort output according to the number of commits per author"), + N_("sort output according to the number of commits per author")), OPT_BOOLEAN('s', "summary", &log.summary, - "Suppress commit descriptions, only provides commit count"), + N_("Suppress commit descriptions, only provides commit count")), OPT_BOOLEAN('e', "email", &log.email, - "Show the email address of each author"), - { OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]", - "Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args }, + 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 a59e088cf5..d208fd6c68 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -6,8 +6,8 @@ #include "parse-options.h" static const char* show_branch_usage[] = { - "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]", - "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]", + N_("git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), + N_("git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"), NULL }; @@ -648,36 +648,36 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) const char *reflog_base = NULL; struct option builtin_show_branch_options[] = { OPT_BOOLEAN('a', "all", &all_heads, - "show remote-tracking and local branches"), + N_("show remote-tracking and local branches")), OPT_BOOLEAN('r', "remotes", &all_remotes, - "show remote-tracking branches"), + N_("show remote-tracking branches")), OPT__COLOR(&showbranch_use_color, - "color '*!+-' corresponding to the branch"), - { OPTION_INTEGER, 0, "more", &extra, "n", - "show <n> more commits after the common ancestor", + 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, "synonym to more=-1", -1), - OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"), + 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, - "include the current branch"), + N_("include the current branch")), OPT_BOOLEAN(0, "sha1-name", &sha1_name, - "name commits with their object names"), + N_("name commits with their object names")), OPT_BOOLEAN(0, "merge-base", &merge_base, - "show possible merge bases"), + N_("show possible merge bases")), OPT_BOOLEAN(0, "independent", &independent, - "show refs unreachable from any other ref"), + N_("show refs unreachable from any other ref")), OPT_BOOLEAN(0, "topo-order", &lifo, - "show commits in topological order"), + N_("show commits in topological order")), OPT_BOOLEAN(0, "topics", &topics, - "show only commits not on the first branch"), + N_("show only commits not on the first branch")), OPT_SET_INT(0, "sparse", &dense, - "show merges reachable from only one tip", 0), + N_("show merges reachable from only one tip"), 0), OPT_SET_INT(0, "date-order", &lifo, - "show commits where no parent comes before its " - "children", 0), - { OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]", - "show <n> most recent ref-log entries starting at " - "base", + N_("show commits where no parent comes before its " + "children"), 0), + { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"), + N_("show <n> most recent ref-log entries starting at " + "base"), PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parse_reflog_param }, OPT_END() diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 3911661900..4eb016d6e5 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -7,8 +7,8 @@ #include "parse-options.h" static const char * const show_ref_usage[] = { - "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ", - "git show-ref --exclude-existing[=pattern] < ref-list", + N_("git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] "), + N_("git show-ref --exclude-existing[=pattern] < ref-list"), NULL }; @@ -179,26 +179,26 @@ 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, "only show tags (can be combined with heads)"), - OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"), - OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, " - "requires exact ref path"), + 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, " + "requires exact ref path")), { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL, - "show the HEAD reference", + N_("show the HEAD reference"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, - OPT_BOOLEAN(0, "head", &show_head, "show the HEAD reference"), + OPT_BOOLEAN(0, "head", &show_head, N_("show the HEAD reference")), OPT_BOOLEAN('d', "dereference", &deref_tags, - "dereference tags into object IDs"), - { OPTION_CALLBACK, 's', "hash", &abbrev, "n", - "only show SHA1 hash using <n> digits", + N_("dereference tags into object IDs")), + { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"), + N_("only show SHA1 hash using <n> digits"), PARSE_OPT_OPTARG, &hash_callback }, OPT__ABBREV(&abbrev), OPT__QUIET(&quiet, - "do not print results to stdout (useful with --verify)"), + N_("do not print results to stdout (useful with --verify)")), { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg, - "pattern", "show refs from stdin that aren't in local repository", + N_("pattern"), N_("show refs from stdin that aren't in local repository"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback }, - { OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage", + { OPTION_CALLBACK, 0, "help-all", NULL, NULL, N_("show usage"), PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() }; diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 801d62ece5..9e92828b3a 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -4,7 +4,7 @@ #include "parse-options.h" static const char * const git_symbolic_ref_usage[] = { - "git symbolic-ref [options] name [ref]", + N_("git symbolic-ref [options] name [ref]"), NULL }; @@ -35,9 +35,9 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) const char *msg = NULL; struct option options[] = { OPT__QUIET(&quiet, - "suppress error message for non-symbolic (detached) refs"), - OPT_BOOL(0, "short", &shorten, "shorten ref output"), - OPT_STRING('m', NULL, &msg, "reason", "reason of the update"), + N_("suppress error message for non-symbolic (detached) refs")), + OPT_BOOL(0, "short", &shorten, N_("shorten ref output")), + OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_END(), }; diff --git a/builtin/tag.c b/builtin/tag.c index 7b1be85e48..9c3e0673d5 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -19,11 +19,11 @@ #include "column.h" static const char * const git_tag_usage[] = { - "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]", - "git tag -d <tagname>...", - "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] " - "\n\t\t[<pattern>...]", - "git tag -v <tagname>...", + N_("git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"), + N_("git tag -d <tagname>..."), + N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] " + "\n\t\t[<pattern>...]"), + N_("git tag -v <tagname>..."), NULL }; @@ -444,37 +444,37 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct msg_arg msg = { 0, STRBUF_INIT }; struct commit_list *with_commit = NULL; struct option options[] = { - OPT_BOOLEAN('l', "list", &list, "list tag names"), - { OPTION_INTEGER, 'n', NULL, &lines, "n", - "print <n> lines of each tag message", + OPT_BOOLEAN('l', "list", &list, N_("list tag names")), + { 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, "delete tags"), - OPT_BOOLEAN('v', "verify", &verify, "verify tags"), + OPT_BOOLEAN('d', "delete", &delete, N_("delete tags")), + OPT_BOOLEAN('v', "verify", &verify, N_("verify tags")), - OPT_GROUP("Tag creation options"), + OPT_GROUP(N_("Tag creation options")), OPT_BOOLEAN('a', "annotate", &annotate, - "annotated tag, needs a message"), - OPT_CALLBACK('m', "message", &msg, "message", - "tag message", parse_msg_arg), - OPT_FILENAME('F', "file", &msgfile, "read message from file"), - OPT_BOOLEAN('s', "sign", &opt.sign, "annotated and GPG-signed tag"), - OPT_STRING(0, "cleanup", &cleanup_arg, "mode", - "how to strip spaces and #comments from message"), - OPT_STRING('u', "local-user", &keyid, "key-id", - "use another key to sign the tag"), - OPT__FORCE(&force, "replace the tag if exists"), - OPT_COLUMN(0, "column", &colopts, "show tag list in columns"), - - OPT_GROUP("Tag listing options"), + 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_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"), + N_("use another key to sign the tag")), + OPT__FORCE(&force, N_("replace the tag if exists")), + OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), + + OPT_GROUP(N_("Tag listing options")), { - OPTION_CALLBACK, 0, "contains", &with_commit, "commit", - "print only tags that contain the commit", + OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"), + N_("print only tags that contain the commit"), PARSE_OPT_LASTARG_DEFAULT, parse_opt_with_commit, (intptr_t)"HEAD", }, { - OPTION_CALLBACK, 0, "points-at", NULL, "object", - "print only tags of the object", 0, parse_opt_points_at + OPTION_CALLBACK, 0, "points-at", NULL, N_("object"), + N_("print only tags of the object"), 0, parse_opt_points_at }, OPT_END() }; diff --git a/builtin/update-index.c b/builtin/update-index.c index 4ce341ceeb..74986bf163 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -398,7 +398,7 @@ static void read_index_info(int line_termination) } static const char * const update_index_usage[] = { - "git update-index [options] [--] [<file>...]", + N_("git update-index [options] [--] [<file>...]"), NULL }; @@ -714,83 +714,83 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) int parseopt_state = PARSE_OPT_UNKNOWN; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, - "continue refresh even when index needs update", + N_("continue refresh even when index needs update"), REFRESH_QUIET), OPT_BIT(0, "ignore-submodules", &refresh_args.flags, - "refresh: ignore submodules", + N_("refresh: ignore submodules"), REFRESH_IGNORE_SUBMODULES), OPT_SET_INT(0, "add", &allow_add, - "do not ignore new files", 1), + N_("do not ignore new files"), 1), OPT_SET_INT(0, "replace", &allow_replace, - "let files replace directories and vice-versa", 1), + N_("let files replace directories and vice-versa"), 1), OPT_SET_INT(0, "remove", &allow_remove, - "notice files missing from worktree", 1), + N_("notice files missing from worktree"), 1), OPT_BIT(0, "unmerged", &refresh_args.flags, - "refresh even if index contains unmerged entries", + N_("refresh even if index contains unmerged entries"), REFRESH_UNMERGED), {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL, - "refresh stat information", + N_("refresh stat information"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, refresh_callback}, {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL, - "like --refresh, but ignore assume-unchanged setting", + N_("like --refresh, but ignore assume-unchanged setting"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, really_refresh_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL, - "<mode> <object> <path>", - "add the specified entry to the index", + N_("<mode> <object> <path>"), + N_("add the specified entry to the index"), PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, (parse_opt_cb *) cacheinfo_callback}, - {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x", - "override the executable bit of the listed files", + {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, N_("(+/-)x"), + N_("override the executable bit of the listed files"), PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, chmod_callback}, {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL, - "mark files as \"not changing\"", + N_("mark files as \"not changing\""), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL, - "clear assumed-unchanged bit", + N_("clear assumed-unchanged bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL, - "mark files as \"index-only\"", + N_("mark files as \"index-only\""), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL, - "clear skip-worktree bit", + N_("clear skip-worktree bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, OPT_SET_INT(0, "info-only", &info_only, - "add to index only; do not add content to object database", 1), + N_("add to index only; do not add content to object database"), 1), OPT_SET_INT(0, "force-remove", &force_remove, - "remove named paths even if present in worktree", 1), + N_("remove named paths even if present in worktree"), 1), OPT_SET_INT('z', NULL, &line_termination, - "with --stdin: input lines are terminated by null bytes", '\0'), + N_("with --stdin: input lines are terminated by null bytes"), '\0'), {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL, - "read list of paths to be updated from standard input", + N_("read list of paths to be updated from standard input"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL, - "add entries from standard input to the index", + N_("add entries from standard input to the index"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_cacheinfo_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL, - "repopulate stages #2 and #3 for the listed paths", + N_("repopulate stages #2 and #3 for the listed paths"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) unresolve_callback}, {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL, - "only update entries that differ from HEAD", + N_("only update entries that differ from HEAD"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) reupdate_callback}, OPT_BIT(0, "ignore-missing", &refresh_args.flags, - "ignore files missing from worktree", + N_("ignore files missing from worktree"), REFRESH_IGNORE_MISSING), OPT_SET_INT(0, "verbose", &verbose, - "report actions to standard output", 1), + N_("report actions to standard output"), 1), {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL, - "(for porcelains) forget saved unresolved conflicts", + N_("(for porcelains) forget saved unresolved conflicts"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, resolve_undo_clear_callback}, OPT_INTEGER(0, "index-version", &preferred_index_format, - "write index in this format"), + N_("write index in this format")), OPT_END() }; diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 835c62ab15..51d2684859 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -4,8 +4,8 @@ #include "parse-options.h" static const char * const git_update_ref_usage[] = { - "git update-ref [options] -d <refname> [<oldval>]", - "git update-ref [options] <refname> <newval> [<oldval>]", + N_("git update-ref [options] -d <refname> [<oldval>]"), + N_("git update-ref [options] <refname> <newval> [<oldval>]"), NULL }; @@ -15,10 +15,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) unsigned char sha1[20], oldsha1[20]; int delete = 0, no_deref = 0, flags = 0; struct option options[] = { - OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"), - OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"), + 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, - "update <refname> not the one it points to"), + N_("update <refname> not the one it points to")), OPT_END(), }; diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index 0d63c4498c..6c8cc3edc1 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -3,7 +3,7 @@ #include "parse-options.h" static const char * const update_server_info_usage[] = { - "git update-server-info [--force]", + N_("git update-server-info [--force]"), NULL }; @@ -11,7 +11,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix) { int force = 0; struct option options[] = { - OPT__FORCE(&force, "update the info files from scratch"), + OPT__FORCE(&force, N_("update the info files from scratch")), OPT_END() }; diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index e841b4a38d..66cd2df0f8 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -53,7 +53,7 @@ static int verify_one_pack(const char *path, unsigned int flags) } static const char * const verify_pack_usage[] = { - "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...", + N_("git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."), NULL }; @@ -63,9 +63,9 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix) unsigned int flags = 0; int i; const struct option verify_pack_options[] = { - OPT_BIT('v', "verbose", &flags, "verbose", + OPT_BIT('v', "verbose", &flags, N_("verbose"), VERIFY_PACK_VERBOSE), - OPT_BIT('s', "stat-only", &flags, "show statistics only", + OPT_BIT('s', "stat-only", &flags, N_("show statistics only"), VERIFY_PACK_STAT_ONLY), OPT_END() }; diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 986789f706..a8eee886a5 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -14,7 +14,7 @@ #include "gpg-interface.h" static const char * const verify_tag_usage[] = { - "git verify-tag [-v|--verbose] <tag>...", + N_("git verify-tag [-v|--verbose] <tag>..."), NULL }; @@ -70,7 +70,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; const struct option verify_tag_options[] = { - OPT__VERBOSE(&verbose, "print tag contents"), + OPT__VERBOSE(&verbose, N_("print tag contents")), OPT_END() }; diff --git a/builtin/write-tree.c b/builtin/write-tree.c index b223af416f..084c0df783 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -10,7 +10,7 @@ #include "parse-options.h" static const char * const write_tree_usage[] = { - "git write-tree [--missing-ok] [--prefix=<prefix>/]", + N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"), NULL }; @@ -21,13 +21,13 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) unsigned char sha1[20]; const char *me = "git-write-tree"; struct option write_tree_options[] = { - OPT_BIT(0, "missing-ok", &flags, "allow missing objects", + OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"), WRITE_TREE_MISSING_OK), - { OPTION_STRING, 0, "prefix", &prefix, "<prefix>/", - "write tree object for a subdirectory <prefix>" , + { OPTION_STRING, 0, "prefix", &prefix, N_("<prefix>/"), + N_("write tree object for a subdirectory <prefix>") , PARSE_OPT_LITERAL_ARGHELP }, { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL, - "only useful for debugging", + N_("only useful for debugging"), PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL, WRITE_TREE_IGNORE_CACHE_TREE }, OPT_END() @@ -607,28 +607,12 @@ static struct commit *interesting(struct commit_list *list) return NULL; } -static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos) +static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos) { struct commit_list *list = NULL; struct commit_list *result = NULL; int i; - for (i = 0; i < n; i++) { - if (one == twos[i]) - /* - * We do not mark this even with RESULT so we do not - * have to clean it up. - */ - return commit_list_insert(one, &result); - } - - if (parse_commit(one)) - return NULL; - for (i = 0; i < n; i++) { - if (parse_commit(twos[i])) - return NULL; - } - one->object.flags |= PARENT1; commit_list_insert_by_date(one, &list); for (i = 0; i < n; i++) { @@ -669,9 +653,34 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co } } - /* Clean up the result to remove stale ones */ free_commit_list(list); - list = result; result = NULL; + return result; +} + +static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos) +{ + struct commit_list *list = NULL; + struct commit_list *result = NULL; + int i; + + for (i = 0; i < n; i++) { + if (one == twos[i]) + /* + * We do not mark this even with RESULT so we do not + * have to clean it up. + */ + return commit_list_insert(one, &result); + } + + if (parse_commit(one)) + return NULL; + for (i = 0; i < n; i++) { + if (parse_commit(twos[i])) + return NULL; + } + + list = paint_down_to_common(one, n, twos); + while (list) { struct commit_list *next = list->next; if (!(list->item->object.flags & STALE)) @@ -709,6 +718,60 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in) return ret; } +static int remove_redundant(struct commit **array, int cnt) +{ + /* + * Some commit in the array may be an ancestor of + * another commit. Move such commit to the end of + * the array, and return the number of commits that + * are independent from each other. + */ + struct commit **work; + unsigned char *redundant; + int *filled_index; + int i, j, filled; + + work = xcalloc(cnt, sizeof(*work)); + redundant = xcalloc(cnt, 1); + filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1)); + + for (i = 0; i < cnt; i++) { + struct commit_list *common; + + if (redundant[i]) + continue; + for (j = filled = 0; j < cnt; j++) { + if (i == j || redundant[j]) + continue; + filled_index[filled] = j; + work[filled++] = array[j]; + } + common = paint_down_to_common(array[i], filled, work); + if (array[i]->object.flags & PARENT2) + redundant[i] = 1; + for (j = 0; j < filled; j++) + if (work[j]->object.flags & PARENT1) + redundant[filled_index[j]] = 1; + clear_commit_marks(array[i], all_flags); + for (j = 0; j < filled; j++) + clear_commit_marks(work[j], all_flags); + free_commit_list(common); + } + + /* Now collect the result */ + memcpy(work, array, sizeof(*array) * cnt); + for (i = filled = 0; i < cnt; i++) + if (!redundant[i]) + array[filled++] = work[i]; + for (j = filled, i = 0; i < cnt; i++) + if (redundant[i]) + array[j++] = work[i]; + free(work); + free(redundant); + free(filled_index); + return filled; +} + struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, @@ -717,7 +780,7 @@ struct commit_list *get_merge_bases_many(struct commit *one, struct commit_list *list; struct commit **rslt; struct commit_list *result; - int cnt, i, j; + int cnt, i; result = merge_bases_many(one, n, twos); for (i = 0; i < n; i++) { @@ -748,28 +811,11 @@ struct commit_list *get_merge_bases_many(struct commit *one, clear_commit_marks(one, all_flags); for (i = 0; i < n; i++) clear_commit_marks(twos[i], all_flags); - for (i = 0; i < cnt - 1; i++) { - for (j = i+1; j < cnt; j++) { - if (!rslt[i] || !rslt[j]) - continue; - result = merge_bases_many(rslt[i], 1, &rslt[j]); - clear_commit_marks(rslt[i], all_flags); - clear_commit_marks(rslt[j], all_flags); - for (list = result; list; list = list->next) { - if (rslt[i] == list->item) - rslt[i] = NULL; - if (rslt[j] == list->item) - rslt[j] = NULL; - } - } - } - /* Surviving ones in rslt[] are the independent results */ + cnt = remove_redundant(rslt, cnt); result = NULL; - for (i = 0; i < cnt; i++) { - if (rslt[i]) - commit_list_insert_by_date(rslt[i], &result); - } + for (i = 0; i < cnt; i++) + commit_list_insert_by_date(rslt[i], &result); free(rslt); return result; } @@ -780,6 +826,9 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two, return get_merge_bases_many(one, 1, &two, cleanup); } +/* + * Is "commit" a decendant of one of the elements on the "with_commit" list? + */ int is_descendant_of(struct commit *commit, struct commit_list *with_commit) { if (!with_commit) @@ -789,28 +838,28 @@ int is_descendant_of(struct commit *commit, struct commit_list *with_commit) other = with_commit->item; with_commit = with_commit->next; - if (in_merge_bases(other, &commit, 1)) + if (in_merge_bases(other, commit)) return 1; } return 0; } -int in_merge_bases(struct commit *commit, struct commit **reference, int num) +/* + * Is "commit" an ancestor of (i.e. reachable from) the "reference"? + */ +int in_merge_bases(struct commit *commit, struct commit *reference) { - struct commit_list *bases, *b; + struct commit_list *bases; int ret = 0; - if (num == 1) - bases = get_merge_bases(commit, *reference, 1); - else - die("not yet"); - for (b = bases; b; b = b->next) { - if (!hashcmp(commit->object.sha1, b->item->object.sha1)) { - ret = 1; - break; - } - } + if (parse_commit(commit) || parse_commit(reference)) + return ret; + bases = paint_down_to_common(commit, 1, &reference); + if (commit->object.flags & PARENT2) + ret = 1; + clear_commit_marks(commit, all_flags); + clear_commit_marks(reference, all_flags); free_commit_list(bases); return ret; } @@ -819,51 +868,31 @@ struct commit_list *reduce_heads(struct commit_list *heads) { struct commit_list *p; struct commit_list *result = NULL, **tail = &result; - struct commit **other; - size_t num_head, num_other; + struct commit **array; + int num_head, i; if (!heads) return NULL; - /* Avoid unnecessary reallocations */ - for (p = heads, num_head = 0; p; p = p->next) - num_head++; - other = xcalloc(sizeof(*other), num_head); - - /* For each commit, see if it can be reached by others */ - for (p = heads; p; p = p->next) { - struct commit_list *q, *base; - - /* Do we already have this in the result? */ - for (q = result; q; q = q->next) - if (p->item == q->item) - break; - if (q) + /* Uniquify */ + for (p = heads; p; p = p->next) + p->item->object.flags &= ~STALE; + for (p = heads, num_head = 0; p; p = p->next) { + if (p->item->object.flags & STALE) continue; - - num_other = 0; - for (q = heads; q; q = q->next) { - if (p->item == q->item) - continue; - other[num_other++] = q->item; + p->item->object.flags |= STALE; + num_head++; + } + array = xcalloc(sizeof(*array), num_head); + for (p = heads, i = 0; p; p = p->next) { + if (p->item->object.flags & STALE) { + array[i++] = p->item; + p->item->object.flags &= ~STALE; } - if (num_other) - base = get_merge_bases_many(p->item, num_other, other, 1); - else - base = NULL; - /* - * If p->item does not have anything common with other - * commits, there won't be any merge base. If it is - * reachable from some of the others, p->item will be - * the merge base. If its history is connected with - * others, but p->item is not reachable by others, we - * will get something other than p->item back. - */ - if (!base || (base->item != p->item)) - tail = &(commit_list_insert(p->item, tail)->next); - free_commit_list(base); } - free(other); + num_head = remove_redundant(array, num_head); + for (i = 0; i < num_head; i++) + tail = &commit_list_insert(array[i], tail)->next; return result; } @@ -1112,8 +1141,92 @@ int commit_tree(const struct strbuf *msg, unsigned char *tree, return result; } +static int find_invalid_utf8(const char *buf, int len) +{ + int offset = 0; + + while (len) { + unsigned char c = *buf++; + int bytes, bad_offset; + + len--; + offset++; + + /* Simple US-ASCII? No worries. */ + if (c < 0x80) + continue; + + bad_offset = offset-1; + + /* + * Count how many more high bits set: that's how + * many more bytes this sequence should have. + */ + bytes = 0; + while (c & 0x40) { + c <<= 1; + bytes++; + } + + /* Must be between 1 and 5 more bytes */ + if (bytes < 1 || bytes > 5) + return bad_offset; + + /* Do we *have* that many bytes? */ + if (len < bytes) + return bad_offset; + + offset += bytes; + len -= bytes; + + /* And verify that they are good continuation bytes */ + do { + if ((*buf++ & 0xc0) != 0x80) + return bad_offset; + } while (--bytes); + + /* We could/should check the value and length here too */ + } + return -1; +} + +/* + * This verifies that the buffer is in proper utf8 format. + * + * If it isn't, it assumes any non-utf8 characters are Latin1, + * and does the conversion. + * + * Fixme: we should probably also disallow overlong forms and + * invalid characters. But we don't do that currently. + */ +static int verify_utf8(struct strbuf *buf) +{ + int ok = 1; + long pos = 0; + + for (;;) { + int bad; + unsigned char c; + unsigned char replace[2]; + + bad = find_invalid_utf8(buf->buf + pos, buf->len - pos); + if (bad < 0) + return ok; + pos += bad; + ok = 0; + c = buf->buf[pos]; + strbuf_remove(buf, pos, 1); + + /* We know 'c' must be in the range 128-255 */ + replace[0] = 0xc0 + (c >> 6); + replace[1] = 0x80 + (c & 0x3f); + strbuf_insert(buf, pos, replace, 2); + pos += 2; + } +} + static const char commit_utf8_warn[] = -"Warning: commit message does not conform to UTF-8.\n" +"Warning: commit message did not conform to UTF-8.\n" "You may want to amend it after fixing the message, or set the config\n" "variable i18n.commitencoding to the encoding your project uses.\n"; @@ -1170,7 +1283,7 @@ int commit_tree_extended(const struct strbuf *msg, unsigned char *tree, strbuf_addbuf(&buffer, msg); /* And check the encoding */ - if (encoding_is_utf8 && !is_utf8(buffer.buf)) + if (encoding_is_utf8 && !verify_utf8(&buffer)) fprintf(stderr, commit_utf8_warn); if (sign_commit && do_sign_commit(&buffer, sign_commit)) @@ -171,7 +171,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads, int depth, int shallow_flag, int not_shallow_flag); int is_descendant_of(struct commit *, struct commit_list *); -int in_merge_bases(struct commit *, struct commit **, int); +int in_merge_bases(struct commit *, 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, diff --git a/compat/mkdir.c b/compat/mkdir.c new file mode 100644 index 0000000000..9e253fb72f --- /dev/null +++ b/compat/mkdir.c @@ -0,0 +1,24 @@ +#include "../git-compat-util.h" +#undef mkdir + +/* for platforms that can't deal with a trailing '/' */ +int compat_mkdir_wo_trailing_slash(const char *dir, mode_t mode) +{ + int retval; + char *tmp_dir = NULL; + size_t len = strlen(dir); + + if (len && dir[len-1] == '/') { + if ((tmp_dir = strdup(dir)) == NULL) + return -1; + tmp_dir[len-1] = '\0'; + } + else + tmp_dir = (char *)dir; + + retval = mkdir(tmp_dir, mode); + if (tmp_dir != dir) + free(tmp_dir); + + return retval; +} @@ -60,7 +60,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc path = buf.buf; } - if (!access(path, R_OK)) { + if (!access_or_warn(path, R_OK)) { if (++inc->depth > MAX_INCLUDE_DEPTH) die(include_depth_advice, MAX_INCLUDE_DEPTH, path, cf && cf->name ? cf->name : "the command line"); @@ -939,23 +939,23 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) home_config_paths(&user_config, &xdg_config, "config"); - if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { + if (git_config_system() && !access_or_warn(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), data); found += 1; } - if (xdg_config && !access(xdg_config, R_OK)) { + if (xdg_config && !access_or_warn(xdg_config, R_OK)) { ret += git_config_from_file(fn, xdg_config, data); found += 1; } - if (user_config && !access(user_config, R_OK)) { + if (user_config && !access_or_warn(user_config, R_OK)) { ret += git_config_from_file(fn, user_config, data); found += 1; } - if (repo_config && !access(repo_config, R_OK)) { + if (repo_config && !access_or_warn(repo_config, R_OK)) { ret += git_config_from_file(fn, repo_config, data); found += 1; } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ffedce751c..0492db924b 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1071,7 +1071,7 @@ _git_diff () } __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 + tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 codecompare " _git_difftool () @@ -2032,7 +2032,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm set-head set-branches set-url show prune update" + local subcommands="add rename remove set-head set-branches set-url show prune update" local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -2040,7 +2040,7 @@ _git_remote () fi case "$subcommand" in - rename|rm|set-url|show|prune) + rename|remove|set-url|show|prune) __gitcomp_nl "$(__git_remotes)" ;; set-head|set-branches) diff --git a/contrib/credential/gnome-keyring/.gitignore b/contrib/credential/gnome-keyring/.gitignore new file mode 100644 index 0000000000..88d8fcdbce --- /dev/null +++ b/contrib/credential/gnome-keyring/.gitignore @@ -0,0 +1 @@ +git-credential-gnome-keyring diff --git a/contrib/credential/gnome-keyring/Makefile b/contrib/credential/gnome-keyring/Makefile new file mode 100644 index 0000000000..e6561d8db6 --- /dev/null +++ b/contrib/credential/gnome-keyring/Makefile @@ -0,0 +1,24 @@ +MAIN:=git-credential-gnome-keyring +all:: $(MAIN) + +CC = gcc +RM = rm -f +CFLAGS = -g -O2 -Wall + +-include ../../../config.mak.autogen +-include ../../../config.mak + +INCS:=$(shell pkg-config --cflags gnome-keyring-1) +LIBS:=$(shell pkg-config --libs gnome-keyring-1) + +SRCS:=$(MAIN).c +OBJS:=$(SRCS:.c=.o) + +%.o: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +clean: + @$(RM) $(MAIN) $(OBJS) diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c new file mode 100644 index 0000000000..41f61c5db3 --- /dev/null +++ b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2011 John Szakmeister <john@szakmeister.net> + * 2012 Philipp A. Hartmann <pah@qo.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Credits: + * - GNOME Keyring API handling originally written by John Szakmeister + * - ported to credential helper API by Philipp A. Hartmann + */ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <gnome-keyring.h> + +/* + * This credential struct and API is simplified from git's credential.{h,c} + */ +struct credential +{ + char *protocol; + char *host; + unsigned short port; + char *path; + char *username; + char *password; +}; + +#define CREDENTIAL_INIT \ + { NULL,NULL,0,NULL,NULL,NULL } + +void credential_init(struct credential *c); +void credential_clear(struct credential *c); +int credential_read(struct credential *c); +void credential_write(const struct credential *c); + +typedef int (*credential_op_cb)(struct credential*); + +struct credential_operation +{ + char *name; + credential_op_cb op; +}; + +#define CREDENTIAL_OP_END \ + { NULL,NULL } + +/* + * Table with operation callbacks is defined in concrete + * credential helper implementation and contains entries + * like { "get", function_to_get_credential } terminated + * by CREDENTIAL_OP_END. + */ +struct credential_operation const credential_helper_ops[]; + +/* ---------------- common helper functions ----------------- */ + +static inline void free_password(char *password) +{ + char *c = password; + if (!password) + return; + + while (*c) *c++ = '\0'; + free(password); +} + +static inline void warning(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n" ); + va_end(ap); +} + +static inline void error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n" ); + va_end(ap); +} + +static inline void die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap,fmt); + error(fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +static inline void die_errno(int err) +{ + error("%s", strerror(err)); + exit(EXIT_FAILURE); +} + +static inline char *xstrdup(const char *str) +{ + char *ret = strdup(str); + if (!ret) + die_errno(errno); + + return ret; +} + +/* ----------------- GNOME Keyring functions ----------------- */ + +/* create a special keyring option string, if path is given */ +static char* keyring_object(struct credential *c) +{ + char* object = NULL; + + if (!c->path) + return object; + + object = (char*) malloc(strlen(c->host)+strlen(c->path)+8); + if(!object) + die_errno(errno); + + if(c->port) + sprintf(object,"%s:%hd/%s",c->host,c->port,c->path); + else + sprintf(object,"%s/%s",c->host,c->path); + + return object; +} + +int keyring_get(struct credential *c) +{ + char* object = NULL; + GList *entries; + GnomeKeyringNetworkPasswordData *password_data; + GnomeKeyringResult result; + + if (!c->protocol || !(c->host || c->path)) + return EXIT_FAILURE; + + object = keyring_object(c); + + result = gnome_keyring_find_network_password_sync( + c->username, + NULL /* domain */, + c->host, + object, + c->protocol, + NULL /* authtype */, + c->port, + &entries); + + free(object); + + if (result == GNOME_KEYRING_RESULT_NO_MATCH) + return EXIT_SUCCESS; + + if (result == GNOME_KEYRING_RESULT_CANCELLED) + return EXIT_SUCCESS; + + if (result != GNOME_KEYRING_RESULT_OK) { + error("%s",gnome_keyring_result_to_message(result)); + return EXIT_FAILURE; + } + + /* pick the first one from the list */ + password_data = (GnomeKeyringNetworkPasswordData *) entries->data; + + free_password(c->password); + c->password = xstrdup(password_data->password); + + if (!c->username) + c->username = xstrdup(password_data->user); + + gnome_keyring_network_password_list_free(entries); + + return EXIT_SUCCESS; +} + + +int keyring_store(struct credential *c) +{ + guint32 item_id; + char *object = NULL; + + /* + * Sanity check that what we are storing is actually sensible. + * In particular, we can't make a URL without a protocol field. + * Without either a host or pathname (depending on the scheme), + * we have no primary key. And without a username and password, + * we are not actually storing a credential. + */ + if (!c->protocol || !(c->host || c->path) || + !c->username || !c->password) + return EXIT_FAILURE; + + object = keyring_object(c); + + gnome_keyring_set_network_password_sync( + GNOME_KEYRING_DEFAULT, + c->username, + NULL /* domain */, + c->host, + object, + c->protocol, + NULL /* authtype */, + c->port, + c->password, + &item_id); + + free(object); + return EXIT_SUCCESS; +} + +int keyring_erase(struct credential *c) +{ + char *object = NULL; + GList *entries; + GnomeKeyringNetworkPasswordData *password_data; + GnomeKeyringResult result; + + /* + * Sanity check that we actually have something to match + * against. The input we get is a restrictive pattern, + * so technically a blank credential means "erase everything". + * But it is too easy to accidentally send this, since it is equivalent + * to empty input. So explicitly disallow it, and require that the + * pattern have some actual content to match. + */ + if (!c->protocol && !c->host && !c->path && !c->username) + return EXIT_FAILURE; + + object = keyring_object(c); + + result = gnome_keyring_find_network_password_sync( + c->username, + NULL /* domain */, + c->host, + object, + c->protocol, + NULL /* authtype */, + c->port, + &entries); + + free(object); + + if (result == GNOME_KEYRING_RESULT_NO_MATCH) + return EXIT_SUCCESS; + + if (result == GNOME_KEYRING_RESULT_CANCELLED) + return EXIT_SUCCESS; + + if (result != GNOME_KEYRING_RESULT_OK) + { + error("%s",gnome_keyring_result_to_message(result)); + return EXIT_FAILURE; + } + + /* pick the first one from the list (delete all matches?) */ + password_data = (GnomeKeyringNetworkPasswordData *) entries->data; + + result = gnome_keyring_item_delete_sync( + password_data->keyring, password_data->item_id); + + gnome_keyring_network_password_list_free(entries); + + if (result != GNOME_KEYRING_RESULT_OK) + { + error("%s",gnome_keyring_result_to_message(result)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* + * Table with helper operation callbacks, used by generic + * credential helper main function. + */ +struct credential_operation const credential_helper_ops[] = +{ + { "get", keyring_get }, + { "store", keyring_store }, + { "erase", keyring_erase }, + CREDENTIAL_OP_END +}; + +/* ------------------ credential functions ------------------ */ + +void credential_init(struct credential *c) +{ + memset(c, 0, sizeof(*c)); +} + +void credential_clear(struct credential *c) +{ + free(c->protocol); + free(c->host); + free(c->path); + free(c->username); + free_password(c->password); + + credential_init(c); +} + +int credential_read(struct credential *c) +{ + char buf[1024]; + ssize_t line_len = 0; + char *key = buf; + char *value; + + while (fgets(buf, sizeof(buf), stdin)) + { + line_len = strlen(buf); + + if(buf[line_len-1]=='\n') + buf[--line_len]='\0'; + + if(!line_len) + break; + + value = strchr(buf,'='); + if(!value) { + warning("invalid credential line: %s", key); + return -1; + } + *value++ = '\0'; + + if (!strcmp(key, "protocol")) { + free(c->protocol); + c->protocol = xstrdup(value); + } else if (!strcmp(key, "host")) { + free(c->host); + c->host = xstrdup(value); + value = strrchr(c->host,':'); + if (value) { + *value++ = '\0'; + c->port = atoi(value); + } + } else if (!strcmp(key, "path")) { + free(c->path); + c->path = xstrdup(value); + } else if (!strcmp(key, "username")) { + free(c->username); + c->username = xstrdup(value); + } else if (!strcmp(key, "password")) { + free_password(c->password); + c->password = xstrdup(value); + while (*value) *value++ = '\0'; + } + /* + * Ignore other lines; we don't know what they mean, but + * this future-proofs us when later versions of git do + * learn new lines, and the helpers are updated to match. + */ + } + return 0; +} + +void credential_write_item(FILE *fp, const char *key, const char *value) +{ + if (!value) + return; + fprintf(fp, "%s=%s\n", key, value); +} + +void credential_write(const struct credential *c) +{ + /* only write username/password, if set */ + credential_write_item(stdout, "username", c->username); + credential_write_item(stdout, "password", c->password); +} + +static void usage(const char *name) +{ + struct credential_operation const *try_op = credential_helper_ops; + const char *basename = strrchr(name,'/'); + + basename = (basename) ? basename + 1 : name; + fprintf(stderr, "Usage: %s <", basename); + while(try_op->name) { + fprintf(stderr,"%s",(try_op++)->name); + if(try_op->name) + fprintf(stderr,"%s","|"); + } + fprintf(stderr,"%s",">\n"); +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_SUCCESS; + + struct credential_operation const *try_op = credential_helper_ops; + struct credential cred = CREDENTIAL_INIT; + + if (!argv[1]) { + usage(argv[0]); + goto out; + } + + /* lookup operation callback */ + while(try_op->name && strcmp(argv[1], try_op->name)) + try_op++; + + /* unsupported operation given -- ignore silently */ + if(!try_op->name || !try_op->op) + goto out; + + ret = credential_read(&cred); + if(ret) + goto out; + + /* perform credential operation */ + ret = (*try_op->op)(&cred); + + credential_write(&cred); + +out: + credential_clear(&cred); + return ret; +} diff --git a/contrib/credential/wincred/Makefile b/contrib/credential/wincred/Makefile new file mode 100644 index 0000000000..bad45ca47a --- /dev/null +++ b/contrib/credential/wincred/Makefile @@ -0,0 +1,14 @@ +all: git-credential-wincred.exe + +CC = gcc +RM = rm -f +CFLAGS = -O2 -Wall + +-include ../../../config.mak.autogen +-include ../../../config.mak + +git-credential-wincred.exe : git-credential-wincred.c + $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +clean: + $(RM) git-credential-wincred.exe diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c new file mode 100644 index 0000000000..cbaec5f24b --- /dev/null +++ b/contrib/credential/wincred/git-credential-wincred.c @@ -0,0 +1,357 @@ +/* + * A git credential helper that interface with Windows' Credential Manager + * + */ +#include <windows.h> +#include <stdio.h> +#include <io.h> +#include <fcntl.h> + +/* common helpers */ + +static void die(const char *err, ...) +{ + char msg[4096]; + va_list params; + va_start(params, err); + vsnprintf(msg, sizeof(msg), err, params); + fprintf(stderr, "%s\n", msg); + va_end(params); + exit(1); +} + +static void *xmalloc(size_t size) +{ + void *ret = malloc(size); + if (!ret && !size) + ret = malloc(1); + if (!ret) + die("Out of memory"); + return ret; +} + +static char *xstrdup(const char *str) +{ + char *ret = strdup(str); + if (!ret) + die("Out of memory"); + return ret; +} + +/* MinGW doesn't have wincred.h, so we need to define stuff */ + +typedef struct _CREDENTIAL_ATTRIBUTEW { + LPWSTR Keyword; + DWORD Flags; + DWORD ValueSize; + LPBYTE Value; +} CREDENTIAL_ATTRIBUTEW, *PCREDENTIAL_ATTRIBUTEW; + +typedef struct _CREDENTIALW { + DWORD Flags; + DWORD Type; + LPWSTR TargetName; + LPWSTR Comment; + FILETIME LastWritten; + DWORD CredentialBlobSize; + LPBYTE CredentialBlob; + DWORD Persist; + DWORD AttributeCount; + PCREDENTIAL_ATTRIBUTEW Attributes; + LPWSTR TargetAlias; + LPWSTR UserName; +} CREDENTIALW, *PCREDENTIALW; + +#define CRED_TYPE_GENERIC 1 +#define CRED_PERSIST_LOCAL_MACHINE 2 +#define CRED_MAX_ATTRIBUTES 64 + +typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD); +typedef BOOL (WINAPI *CredUnPackAuthenticationBufferWT)(DWORD, PVOID, DWORD, + LPWSTR, DWORD *, LPWSTR, DWORD *, LPWSTR, DWORD *); +typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *, + PCREDENTIALW **); +typedef BOOL (WINAPI *CredPackAuthenticationBufferWT)(DWORD, LPWSTR, LPWSTR, + PBYTE, DWORD *); +typedef VOID (WINAPI *CredFreeT)(PVOID); +typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD); + +static HMODULE advapi, credui; +static CredWriteWT CredWriteW; +static CredUnPackAuthenticationBufferWT CredUnPackAuthenticationBufferW; +static CredEnumerateWT CredEnumerateW; +static CredPackAuthenticationBufferWT CredPackAuthenticationBufferW; +static CredFreeT CredFree; +static CredDeleteWT CredDeleteW; + +static void load_cred_funcs(void) +{ + /* load DLLs */ + advapi = LoadLibrary("advapi32.dll"); + credui = LoadLibrary("credui.dll"); + if (!advapi || !credui) + die("failed to load DLLs"); + + /* get function pointers */ + CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW"); + CredUnPackAuthenticationBufferW = (CredUnPackAuthenticationBufferWT) + GetProcAddress(credui, "CredUnPackAuthenticationBufferW"); + CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi, + "CredEnumerateW"); + CredPackAuthenticationBufferW = (CredPackAuthenticationBufferWT) + GetProcAddress(credui, "CredPackAuthenticationBufferW"); + CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree"); + CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW"); + if (!CredWriteW || !CredUnPackAuthenticationBufferW || + !CredEnumerateW || !CredPackAuthenticationBufferW || !CredFree || + !CredDeleteW) + die("failed to load functions"); +} + +static char target_buf[1024]; +static char *protocol, *host, *path, *username; +static WCHAR *wusername, *password, *target; + +static void write_item(const char *what, WCHAR *wbuf) +{ + char *buf; + int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, NULL, 0, NULL, + FALSE); + buf = xmalloc(len); + + if (!WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, len, NULL, FALSE)) + die("WideCharToMultiByte failed!"); + + printf("%s=", what); + fwrite(buf, 1, len - 1, stdout); + putchar('\n'); + free(buf); +} + +static int match_attr(const CREDENTIALW *cred, const WCHAR *keyword, + const char *want) +{ + int i; + if (!want) + return 1; + + for (i = 0; i < cred->AttributeCount; ++i) + if (!wcscmp(cred->Attributes[i].Keyword, keyword)) + return !strcmp((const char *)cred->Attributes[i].Value, + want); + + return 0; /* not found */ +} + +static int match_cred(const CREDENTIALW *cred) +{ + return (!wusername || !wcscmp(wusername, cred->UserName)) && + match_attr(cred, L"git_protocol", protocol) && + match_attr(cred, L"git_host", host) && + match_attr(cred, L"git_path", path); +} + +static void get_credential(void) +{ + WCHAR *user_buf, *pass_buf; + DWORD user_buf_size = 0, pass_buf_size = 0; + CREDENTIALW **creds, *cred = NULL; + DWORD num_creds; + int i; + + if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds)) + return; + + /* search for the first credential that matches username */ + for (i = 0; i < num_creds; ++i) + if (match_cred(creds[i])) { + cred = creds[i]; + break; + } + if (!cred) + return; + + CredUnPackAuthenticationBufferW(0, cred->CredentialBlob, + cred->CredentialBlobSize, NULL, &user_buf_size, NULL, NULL, + NULL, &pass_buf_size); + + user_buf = xmalloc(user_buf_size * sizeof(WCHAR)); + pass_buf = xmalloc(pass_buf_size * sizeof(WCHAR)); + + if (!CredUnPackAuthenticationBufferW(0, cred->CredentialBlob, + cred->CredentialBlobSize, user_buf, &user_buf_size, NULL, NULL, + pass_buf, &pass_buf_size)) + die("CredUnPackAuthenticationBuffer failed"); + + CredFree(creds); + + /* zero-terminate (sizes include zero-termination) */ + user_buf[user_buf_size - 1] = L'\0'; + pass_buf[pass_buf_size - 1] = L'\0'; + + write_item("username", user_buf); + write_item("password", pass_buf); + + free(user_buf); + free(pass_buf); +} + +static void write_attr(CREDENTIAL_ATTRIBUTEW *attr, const WCHAR *keyword, + const char *value) +{ + attr->Keyword = (LPWSTR)keyword; + attr->Flags = 0; + attr->ValueSize = strlen(value) + 1; /* store zero-termination */ + attr->Value = (LPBYTE)value; +} + +static void store_credential(void) +{ + CREDENTIALW cred; + BYTE *auth_buf; + DWORD auth_buf_size = 0; + CREDENTIAL_ATTRIBUTEW attrs[CRED_MAX_ATTRIBUTES]; + + if (!wusername || !password) + return; + + /* query buffer size */ + CredPackAuthenticationBufferW(0, wusername, password, + NULL, &auth_buf_size); + + auth_buf = xmalloc(auth_buf_size); + + if (!CredPackAuthenticationBufferW(0, wusername, password, + auth_buf, &auth_buf_size)) + die("CredPackAuthenticationBuffer failed"); + + cred.Flags = 0; + cred.Type = CRED_TYPE_GENERIC; + cred.TargetName = target; + cred.Comment = L"saved by git-credential-wincred"; + cred.CredentialBlobSize = auth_buf_size; + cred.CredentialBlob = auth_buf; + cred.Persist = CRED_PERSIST_LOCAL_MACHINE; + cred.AttributeCount = 1; + cred.Attributes = attrs; + cred.TargetAlias = NULL; + cred.UserName = wusername; + + write_attr(attrs, L"git_protocol", protocol); + + if (host) { + write_attr(attrs + cred.AttributeCount, L"git_host", host); + cred.AttributeCount++; + } + + if (path) { + write_attr(attrs + cred.AttributeCount, L"git_path", path); + cred.AttributeCount++; + } + + if (!CredWriteW(&cred, 0)) + die("CredWrite failed"); +} + +static void erase_credential(void) +{ + CREDENTIALW **creds; + DWORD num_creds; + int i; + + if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds)) + return; + + for (i = 0; i < num_creds; ++i) { + if (match_cred(creds[i])) + CredDeleteW(creds[i]->TargetName, creds[i]->Type, 0); + } + + CredFree(creds); +} + +static WCHAR *utf8_to_utf16_dup(const char *str) +{ + int wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR *wstr = xmalloc(sizeof(WCHAR) * wlen); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, wlen); + return wstr; +} + +static void read_credential(void) +{ + char buf[1024]; + + while (fgets(buf, sizeof(buf), stdin)) { + char *v; + + if (!strcmp(buf, "\n")) + break; + buf[strlen(buf)-1] = '\0'; + + v = strchr(buf, '='); + if (!v) + die("bad input: %s", buf); + *v++ = '\0'; + + if (!strcmp(buf, "protocol")) + protocol = xstrdup(v); + else if (!strcmp(buf, "host")) + host = xstrdup(v); + else if (!strcmp(buf, "path")) + path = xstrdup(v); + else if (!strcmp(buf, "username")) { + username = xstrdup(v); + wusername = utf8_to_utf16_dup(v); + } else if (!strcmp(buf, "password")) + password = utf8_to_utf16_dup(v); + else + die("unrecognized input"); + } +} + +int main(int argc, char *argv[]) +{ + const char *usage = + "Usage: git credential-wincred <get|store|erase>\n"; + + if (!argv[1]) + die(usage); + + /* git use binary pipes to avoid CRLF-issues */ + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + + read_credential(); + + load_cred_funcs(); + + if (!protocol || !(host || path)) + return 0; + + /* prepare 'target', the unique key for the credential */ + strncat(target_buf, "git:", sizeof(target_buf)); + strncat(target_buf, protocol, sizeof(target_buf)); + strncat(target_buf, "://", sizeof(target_buf)); + if (username) { + strncat(target_buf, username, sizeof(target_buf)); + strncat(target_buf, "@", sizeof(target_buf)); + } + if (host) + strncat(target_buf, host, sizeof(target_buf)); + if (path) { + strncat(target_buf, "/", sizeof(target_buf)); + strncat(target_buf, path, sizeof(target_buf)); + } + + target = utf8_to_utf16_dup(target_buf); + + if (!strcmp(argv[1], "get")) + get_credential(); + else if (!strcmp(argv[1], "store")) + store_credential(); + else if (!strcmp(argv[1], "erase")) + erase_credential(); + /* otherwise, ignore unknown action */ + return 0; +} diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c index 0d54aa7061..8bc8c7533a 100644 --- a/contrib/examples/builtin-fetch--tool.c +++ b/contrib/examples/builtin-fetch--tool.c @@ -96,7 +96,7 @@ static int update_local_ref(const char *name, strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); - if (in_merge_bases(current, &updated, 1)) { + if (in_merge_bases(current, updated)) { fprintf(stderr, "* %s: fast-forward to %s\n", name, note); fprintf(stderr, " old..new: %s..%s\n", oldh, newh); @@ -30,6 +30,7 @@ static const char daemon_usage[] = " [--interpolated-path=<path>]\n" " [--reuseaddr] [--pid-file=<file>]\n" " [--(enable|disable|allow-override|forbid-override)=<service>]\n" +" [--access-hook=<path>]\n" " [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n" " [--detach] [--user=<user> [--group=<group>]]\n" " [<directory>...]"; @@ -256,6 +257,71 @@ static int daemon_error(const char *dir, const char *msg) return -1; } +static char *access_hook; + +static int run_access_hook(struct daemon_service *service, const char *dir, const char *path) +{ + struct child_process child; + struct strbuf buf = STRBUF_INIT; + const char *argv[8]; + const char **arg = argv; + char *eol; + int seen_errors = 0; + +#define STRARG(x) ((x) ? (x) : "") + *arg++ = access_hook; + *arg++ = service->name; + *arg++ = path; + *arg++ = STRARG(hostname); + *arg++ = STRARG(canon_hostname); + *arg++ = STRARG(ip_address); + *arg++ = STRARG(tcp_port); + *arg = NULL; +#undef STRARG + + memset(&child, 0, sizeof(child)); + child.use_shell = 1; + child.argv = argv; + child.no_stdin = 1; + child.no_stderr = 1; + child.out = -1; + if (start_command(&child)) { + logerror("daemon access hook '%s' failed to start", + access_hook); + goto error_return; + } + if (strbuf_read(&buf, child.out, 0) < 0) { + logerror("failed to read from pipe to daemon access hook '%s'", + access_hook); + strbuf_reset(&buf); + seen_errors = 1; + } + if (close(child.out) < 0) { + logerror("failed to close pipe to daemon access hook '%s'", + access_hook); + seen_errors = 1; + } + if (finish_command(&child)) + seen_errors = 1; + + if (!seen_errors) { + strbuf_release(&buf); + return 0; + } + +error_return: + strbuf_ltrim(&buf); + if (!buf.len) + strbuf_addstr(&buf, "service rejected"); + eol = strchr(buf.buf, '\n'); + if (eol) + *eol = '\0'; + errno = EACCES; + daemon_error(dir, buf.buf); + strbuf_release(&buf); + return -1; +} + static int run_service(char *dir, struct daemon_service *service) { const char *path; @@ -304,6 +370,13 @@ static int run_service(char *dir, struct daemon_service *service) } /* + * Optionally, a hook can choose to deny access to the + * repository depending on the phase of the moon. + */ + if (access_hook && run_access_hook(service, dir, path)) + return -1; + + /* * We'll ignore SIGTERM from now on, we have a * good client. */ @@ -1142,6 +1215,10 @@ int main(int argc, char **argv) export_all_trees = 1; continue; } + if (!prefixcmp(arg, "--access-hook=")) { + access_hook = arg + 14; + continue; + } if (!prefixcmp(arg, "--timeout=")) { timeout = atoi(arg+10); continue; @@ -397,6 +397,8 @@ int add_excludes_from_file_to_list(const char *fname, fd = open(fname, O_RDONLY); if (fd < 0 || fstat(fd, &st) < 0) { + if (errno != ENOENT) + warn_on_inaccessible(fname); if (0 <= fd) close(fd); if (!check_index || @@ -1311,9 +1313,9 @@ void setup_standard_excludes(struct dir_struct *dir) home_config_paths(NULL, &xdg_path, "ignore"); excludes_file = xdg_path; } - if (!access(path, R_OK)) + if (!access_or_warn(path, R_OK)) add_excludes_from_file(dir, path); - if (excludes_file && !access(excludes_file, R_OK)) + if (excludes_file && !access_or_warn(excludes_file, R_OK)) add_excludes_from_file(dir, excludes_file); } diff --git a/fast-import.c b/fast-import.c index eed97c8fa9..c2a814ec66 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1691,7 +1691,7 @@ static int update_branch(struct branch *b) return error("Branch %s is missing commits.", b->name); } - if (!in_merge_bases(old_cmit, &new_cmit, 1)) { + if (!in_merge_bases(old_cmit, new_cmit)) { unlock_ref(lock); warning("Not updating %s" " (new tip %s does not contain %s)", @@ -44,6 +44,8 @@ extern int use_gettext_poison(void); static inline FORMAT_PRESERVING(1) const char *_(const char *msgid) { + if (!*msgid) + return ""; return use_gettext_poison() ? "# GETTEXT POISON #" : gettext(msgid); } @@ -855,8 +855,8 @@ did you forget to use 'git add'?" eval_gettextln 'Patch failed at $msgnum $FIRSTLINE' if test "$(git config --bool advice.amworkdir)" != false then - eval_gettextln "The copy of the patch that failed is found in: - $dotest/patch" + eval_gettextln 'The copy of the patch that failed is found in: + $dotest/patch' fi stop_here_user_resolve $this fi diff --git a/git-compat-util.h b/git-compat-util.h index 35b095e8ae..24b5432462 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -162,6 +162,22 @@ #define probe_utf8_pathname_composition(a,b) #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); +#endif + +#ifdef NO_STRUCT_ITIMERVAL +struct itimerval { + struct timeval it_interval; + struct timeval it_value; +} +#endif + +#ifdef NO_SETITIMER +#define setitimer(which,value,ovalue) +#endif + #ifndef NO_LIBGEN_H #include <libgen.h> #else @@ -604,6 +620,12 @@ int rmdir_or_warn(const char *path); */ int remove_or_warn(unsigned int mode, const char *path); +/* Call access(2), but warn for any error besides ENOENT. */ +int access_or_warn(const char *path, int mode); + +/* Warn on an inaccessible file that ought to be accessible */ +void warn_on_inaccessible(const char *path); + /* Get the passwd entry for the UID of the current process. */ struct passwd *xgetpwuid_self(void); diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 8d41610bcf..8032f23202 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -889,10 +889,37 @@ sub commit { $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY ** $xtag =~ tr/_/\./ if ( $opt_u ); $xtag =~ s/[\/]/$opt_s/g; - $xtag =~ s/\[//g; - system('git' , 'tag', '-f', $xtag, $cid) == 0 - or die "Cannot create tag $xtag: $!\n"; + # See refs.c for these rules. + # Tag cannot contain bad chars. (See bad_ref_char in refs.c.) + $xtag =~ s/[ ~\^:\\\*\?\[]//g; + # Other bad strings for tags: + # (See check_refname_component in refs.c.) + 1 while $xtag =~ s/ + (?: \.\. # Tag cannot contain '..'. + | \@{ # Tag cannot contain '@{'. + | ^ - # Tag cannot begin with '-'. + | \.lock $ # Tag cannot end with '.lock'. + | ^ \. # Tag cannot begin... + | \. $ # ...or end with '.' + )//xg; + # Tag cannot be empty. + if ($xtag eq '') { + warn("warning: ignoring tag '$tag'", + " with invalid tagname\n"); + return; + } + + if (system('git' , 'tag', '-f', $xtag, $cid) != 0) { + # We did our best to sanitize the tag, but still failed + # for whatever reason. Bail out, and give the user + # enough information to understand if/how we should + # improve the translation in the future. + if ($tag ne $xtag) { + print "Translated '$tag' tag to '$xtag'\n"; + } + die "Cannot create tag $xtag: $!\n"; + } print "Created tag '$xtag' on '$branch'\n" if $opt_v; } diff --git a/git-difftool.perl b/git-difftool.perl index c0798540ad..edd0493a08 100755 --- a/git-difftool.perl +++ b/git-difftool.perl @@ -18,17 +18,11 @@ use File::Copy; use File::Compare; use File::Find; use File::stat; -use File::Path qw(mkpath); +use File::Path qw(mkpath rmtree); use File::Temp qw(tempdir); use Getopt::Long qw(:config pass_through); use Git; -my @tools; -my @working_tree; -my $rc; -my $repo = Git->repository(); -my $repo_path = $repo->repo_path(); - sub usage { my $exitcode = shift; @@ -45,6 +39,8 @@ USAGE sub find_worktree { + my ($repo) = @_; + # Git->repository->wc_path() does not honor changes to the working # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree' # config variable. @@ -63,10 +59,9 @@ sub find_worktree return $worktree; } -my $workdir = find_worktree(); - sub filter_tool_scripts { + my ($tools) = @_; if (-d $_) { if ($_ ne ".") { # Ignore files in subdirectories @@ -74,17 +69,17 @@ sub filter_tool_scripts } } else { if ((-f $_) && ($_ ne "defaults")) { - push(@tools, $_); + push(@$tools, $_); } } } sub print_tool_help { - my ($cmd, @found, @notfound); + my ($cmd, @found, @notfound, @tools); my $gitpath = Git::exec_path(); - find(\&filter_tool_scripts, "$gitpath/mergetools"); + find(sub { filter_tool_scripts(\@tools) }, "$gitpath/mergetools"); foreach my $tool (@tools) { $cmd = "TOOL_MODE=diff"; @@ -98,34 +93,52 @@ sub print_tool_help } } - print "'git difftool --tool=<tool>' may be set to one of the following:\n"; + print << 'EOF'; +'git difftool --tool=<tool>' may be set to one of the following: +EOF print "\t$_\n" for (sort(@found)); - print "\nThe following tools are valid, but not currently available:\n"; + print << 'EOF'; + +The following tools are valid, but not currently available: +EOF print "\t$_\n" for (sort(@notfound)); - print "\nNOTE: Some of the tools listed above only work in a windowed\n"; - print "environment. If run in a terminal-only session, they will fail.\n"; + print << 'EOF'; +NOTE: Some of the tools listed above only work in a windowed +environment. If run in a terminal-only session, they will fail. +EOF exit(0); } +sub exit_cleanup +{ + my ($tmpdir, $status) = @_; + my $errno = $!; + rmtree($tmpdir); + if ($status and $errno) { + my ($package, $file, $line) = caller(); + warn "$file line $line: $errno\n"; + } + exit($status | ($status >> 8)); +} + sub setup_dir_diff { + my ($repo, $workdir, $symlinks) = @_; + # Run the diff; exit immediately if no diff found # 'Repository' and 'WorkingCopy' must be explicitly set to insure that # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used # by Git->repository->command*. - my $diffrepo = Git->repository(Repository => $repo_path, WorkingCopy => $workdir); - my $diffrtn = $diffrepo->command_oneline('diff', '--raw', '--no-abbrev', '-z', @ARGV); - exit(0) if (length($diffrtn) == 0); + my $repo_path = $repo->repo_path(); + my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir); + my $diffrepo = Git->repository(%repo_args); - # Setup temp directories - my $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1); - my $ldir = "$tmpdir/left"; - my $rdir = "$tmpdir/right"; - mkpath($ldir) or die $!; - mkpath($rdir) or die $!; + my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV); + my $diffrtn = $diffrepo->command_oneline(@gitargs); + exit(0) unless defined($diffrtn); # Build index info for left and right sides of the diff my $submodule_mode = '160000'; @@ -136,16 +149,21 @@ sub setup_dir_diff my $rindex = ''; my %submodule; my %symlink; + my @working_tree = (); my @rawdiff = split('\0', $diffrtn); my $i = 0; while ($i < $#rawdiff) { if ($rawdiff[$i] =~ /^::/) { - print "Combined diff formats ('-c' and '--cc') are not supported in directory diff mode.\n"; + warn << 'EOF'; +Combined diff formats ('-c' and '--cc') are not supported in +directory diff mode ('-d' and '--dir-diff'). +EOF exit(1); } - my ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1)); + my ($lmode, $rmode, $lsha1, $rsha1, $status) = + split(' ', substr($rawdiff[$i], 1)); my $src_path = $rawdiff[$i + 1]; my $dst_path; @@ -157,7 +175,7 @@ sub setup_dir_diff $i += 2; } - if (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) { + if ($lmode eq $submodule_mode or $rmode eq $submodule_mode) { $submodule{$src_path}{left} = $lsha1; if ($lsha1 ne $rsha1) { $submodule{$dst_path}{right} = $rsha1; @@ -168,14 +186,16 @@ sub setup_dir_diff } if ($lmode eq $symlink_mode) { - $symlink{$src_path}{left} = $diffrepo->command_oneline('show', "$lsha1"); + $symlink{$src_path}{left} = + $diffrepo->command_oneline('show', "$lsha1"); } if ($rmode eq $symlink_mode) { - $symlink{$dst_path}{right} = $diffrepo->command_oneline('show', "$rsha1"); + $symlink{$dst_path}{right} = + $diffrepo->command_oneline('show', "$rsha1"); } - if (($lmode ne $null_mode) and ($status !~ /^C/)) { + if ($lmode ne $null_mode and $status !~ /^C/) { $lindex .= "$lmode $lsha1\t$src_path\0"; } @@ -188,6 +208,13 @@ sub setup_dir_diff } } + # Setup temp directories + my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1); + my $ldir = "$tmpdir/left"; + my $rdir = "$tmpdir/right"; + mkpath($ldir) or exit_cleanup($tmpdir, 1); + mkpath($rdir) or exit_cleanup($tmpdir, 1); + # If $GIT_DIR is not set prior to calling 'git update-index' and # 'git checkout-index', then those commands will fail if difftool # is called from a directory other than the repo root. @@ -200,18 +227,22 @@ sub setup_dir_diff # Populate the left and right directories based on each index file my ($inpipe, $ctx); $ENV{GIT_INDEX_FILE} = "$tmpdir/lindex"; - ($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/); + ($inpipe, $ctx) = + $repo->command_input_pipe(qw(update-index -z --index-info)); print($inpipe $lindex); $repo->command_close_pipe($inpipe, $ctx); - $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/"); - exit($rc | ($rc >> 8)) if ($rc != 0); + + my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/"); + exit_cleanup($tmpdir, $rc) if $rc != 0; $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex"; - ($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/); + ($inpipe, $ctx) = + $repo->command_input_pipe(qw(update-index -z --index-info)); print($inpipe $rindex); $repo->command_close_pipe($inpipe, $ctx); + $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/"); - exit($rc | ($rc >> 8)) if ($rc != 0); + exit_cleanup($tmpdir, $rc) if $rc != 0; # If $GIT_DIR was explicitly set just for the update/checkout # commands, then it should be unset before continuing. @@ -223,37 +254,55 @@ sub setup_dir_diff for my $file (@working_tree) { my $dir = dirname($file); unless (-d "$rdir/$dir") { - mkpath("$rdir/$dir") or die $!; + mkpath("$rdir/$dir") or + exit_cleanup($tmpdir, 1); + } + if ($symlinks) { + symlink("$workdir/$file", "$rdir/$file") or + exit_cleanup($tmpdir, 1); + } else { + copy("$workdir/$file", "$rdir/$file") or + exit_cleanup($tmpdir, 1); + + my $mode = stat("$workdir/$file")->mode; + chmod($mode, "$rdir/$file") or + exit_cleanup($tmpdir, 1); } - copy("$workdir/$file", "$rdir/$file") or die $!; - chmod(stat("$workdir/$file")->mode, "$rdir/$file") or die $!; } # Changes to submodules require special treatment. This loop writes a # temporary file to both the left and right directories to show the # change in the recorded SHA1 for the submodule. for my $path (keys %submodule) { + my $ok; if (defined($submodule{$path}{left})) { - write_to_file("$ldir/$path", "Subproject commit $submodule{$path}{left}"); + $ok = write_to_file("$ldir/$path", + "Subproject commit $submodule{$path}{left}"); } if (defined($submodule{$path}{right})) { - write_to_file("$rdir/$path", "Subproject commit $submodule{$path}{right}"); + $ok = write_to_file("$rdir/$path", + "Subproject commit $submodule{$path}{right}"); } + exit_cleanup($tmpdir, 1) if not $ok; } # Symbolic links require special treatment. The standard "git diff" # shows only the link itself, not the contents of the link target. # This loop replicates that behavior. for my $path (keys %symlink) { + my $ok; if (defined($symlink{$path}{left})) { - write_to_file("$ldir/$path", $symlink{$path}{left}); + $ok = write_to_file("$ldir/$path", + $symlink{$path}{left}); } if (defined($symlink{$path}{right})) { - write_to_file("$rdir/$path", $symlink{$path}{right}); + $ok = write_to_file("$rdir/$path", + $symlink{$path}{right}); } + exit_cleanup($tmpdir, 1) if not $ok; } - return ($ldir, $rdir); + return ($ldir, $rdir, $tmpdir, @working_tree); } sub write_to_file @@ -264,85 +313,141 @@ sub write_to_file # Make sure the path to the file exists my $dir = dirname($path); unless (-d "$dir") { - mkpath("$dir") or die $!; + mkpath("$dir") or return 0; } # If the file already exists in that location, delete it. This # is required in the case of symbolic links. - unlink("$path"); + unlink($path); - open(my $fh, '>', "$path") or die $!; + open(my $fh, '>', $path) or return 0; print($fh $value); close($fh); -} -# parse command-line options. all unrecognized options and arguments -# are passed through to the 'git diff' command. -my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help); -GetOptions('g|gui!' => \$gui, - 'd|dir-diff' => \$dirdiff, - 'h' => \$help, - 'prompt!' => \$prompt, - 'y' => sub { $prompt = 0; }, - 't|tool:s' => \$difftool_cmd, - 'tool-help' => \$tool_help, - 'x|extcmd:s' => \$extcmd); - -if (defined($help)) { - usage(0); -} -if (defined($tool_help)) { - print_tool_help(); + return 1; } -if (defined($difftool_cmd)) { - if (length($difftool_cmd) > 0) { - $ENV{GIT_DIFF_TOOL} = $difftool_cmd; - } else { - print "No <tool> given for --tool=<tool>\n"; - usage(1); + +sub main +{ + # parse command-line options. all unrecognized options and arguments + # are passed through to the 'git diff' command. + my %opts = ( + difftool_cmd => undef, + dirdiff => undef, + extcmd => undef, + gui => undef, + help => undef, + prompt => undef, + symlinks => $^O ne 'cygwin' && + $^O ne 'MSWin32' && $^O ne 'msys', + tool_help => undef, + ); + GetOptions('g|gui!' => \$opts{gui}, + 'd|dir-diff' => \$opts{dirdiff}, + 'h' => \$opts{help}, + 'prompt!' => \$opts{prompt}, + 'y' => sub { $opts{prompt} = 0; }, + 'symlinks' => \$opts{symlinks}, + 'no-symlinks' => sub { $opts{symlinks} = 0; }, + 't|tool:s' => \$opts{difftool_cmd}, + 'tool-help' => \$opts{tool_help}, + 'x|extcmd:s' => \$opts{extcmd}); + + if (defined($opts{help})) { + usage(0); } -} -if (defined($extcmd)) { - if (length($extcmd) > 0) { - $ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd; - } else { - print "No <cmd> given for --extcmd=<cmd>\n"; - usage(1); + if (defined($opts{tool_help})) { + print_tool_help(); } -} -if ($gui) { - my $guitool = ''; - $guitool = Git::config('diff.guitool'); - if (length($guitool) > 0) { - $ENV{GIT_DIFF_TOOL} = $guitool; + if (defined($opts{difftool_cmd})) { + if (length($opts{difftool_cmd}) > 0) { + $ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd}; + } else { + print "No <tool> given for --tool=<tool>\n"; + usage(1); + } + } + if (defined($opts{extcmd})) { + if (length($opts{extcmd}) > 0) { + $ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd}; + } else { + print "No <cmd> given for --extcmd=<cmd>\n"; + usage(1); + } + } + if ($opts{gui}) { + my $guitool = Git::config('diff.guitool'); + if (length($guitool) > 0) { + $ENV{GIT_DIFF_TOOL} = $guitool; + } + } + + # In directory diff mode, 'git-difftool--helper' is called once + # to compare the a/b directories. In file diff mode, 'git diff' + # will invoke a separate instance of 'git-difftool--helper' for + # each file that changed. + if (defined($opts{dirdiff})) { + dir_diff($opts{extcmd}, $opts{symlinks}); + } else { + file_diff($opts{prompt}); } } -# In directory diff mode, 'git-difftool--helper' is called once -# to compare the a/b directories. In file diff mode, 'git diff' -# will invoke a separate instance of 'git-difftool--helper' for -# each file that changed. -if (defined($dirdiff)) { - my ($a, $b) = setup_dir_diff(); +sub dir_diff +{ + my ($extcmd, $symlinks) = @_; + my $rc; + my $error = 0; + my $repo = Git->repository(); + my $workdir = find_worktree($repo); + my ($a, $b, $tmpdir, @worktree) = + setup_dir_diff($repo, $workdir, $symlinks); + if (defined($extcmd)) { $rc = system($extcmd, $a, $b); } else { $ENV{GIT_DIFFTOOL_DIRDIFF} = 'true'; $rc = system('git', 'difftool--helper', $a, $b); } - - exit($rc | ($rc >> 8)) if ($rc != 0); - # If the diff including working copy files and those # files were modified during the diff, then the changes - # should be copied back to the working tree - for my $file (@working_tree) { - if (-e "$b/$file" && compare("$b/$file", "$workdir/$file")) { - copy("$b/$file", "$workdir/$file") or die $!; - chmod(stat("$b/$file")->mode, "$workdir/$file") or die $!; + # should be copied back to the working tree. + # Do not copy back files when symlinks are used and the + # external tool did not replace the original link with a file. + for my $file (@worktree) { + next if $symlinks && -l "$b/$file"; + next if ! -f "$b/$file"; + + my $diff = compare("$b/$file", "$workdir/$file"); + if ($diff == 0) { + next; + } elsif ($diff == -1) { + my $errmsg = "warning: Could not compare "; + $errmsg += "'$b/$file' with '$workdir/$file'\n"; + warn $errmsg; + $error = 1; + } elsif ($diff == 1) { + my $mode = stat("$b/$file")->mode; + copy("$b/$file", "$workdir/$file") or + exit_cleanup($tmpdir, 1); + + chmod($mode, "$workdir/$file") or + exit_cleanup($tmpdir, 1); } } -} else { + if ($error) { + warn "warning: Temporary files exist in '$tmpdir'.\n"; + warn "warning: You may want to cleanup or recover these.\n"; + exit(1); + } else { + exit_cleanup($tmpdir, $rc); + } +} + +sub file_diff +{ + my ($prompt) = @_; + if (defined($prompt)) { if ($prompt) { $ENV{GIT_DIFFTOOL_PROMPT} = 'true'; @@ -362,3 +467,5 @@ if (defined($dirdiff)) { my $rc = system('git', 'diff', @ARGV); exit($rc | ($rc >> 8)); } + +main(); diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index f730253c0e..54cb708254 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -126,7 +126,7 @@ list_merge_tool_candidates () { else tools="opendiff kdiff3 tkdiff xxdiff meld $tools" fi - tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3" + tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3 codecompare" fi case "${VISUAL:-$EDITOR}" in *vim*) @@ -1818,19 +1818,41 @@ class P4Sync(Command, P4UserMap): return files def stripRepoPath(self, path, prefixes): - if self.useClientSpec: - return self.clientSpecDirs.map_in_client(path) + """When streaming files, this is called to map a p4 depot path + to where it should go in git. The prefixes are either + self.depotPaths, or self.branchPrefixes in the case of + branch detection.""" - if self.keepRepoPath: - prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])] + if self.useClientSpec: + # branch detection moves files up a level (the branch name) + # from what client spec interpretation gives + path = self.clientSpecDirs.map_in_client(path) + if self.detectBranches: + for b in self.knownBranches: + if path.startswith(b + "/"): + path = path[len(b)+1:] + + elif self.keepRepoPath: + # Preserve everything in relative path name except leading + # //depot/; just look at first prefix as they all should + # be in the same depot. + depot = re.sub("^(//[^/]+/).*", r'\1', prefixes[0]) + if p4PathStartsWith(path, depot): + path = path[len(depot):] - for p in prefixes: - if p4PathStartsWith(path, p): - path = path[len(p):] + else: + for p in prefixes: + if p4PathStartsWith(path, p): + path = path[len(p):] + break + path = wildcard_decode(path) return path def splitFilesIntoBranches(self, commit): + """Look at each depotFile in the commit to figure out to what + branch it belongs.""" + branches = {} fnum = 0 while commit.has_key("depotFile%s" % fnum): @@ -1848,12 +1870,16 @@ class P4Sync(Command, P4UserMap): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = self.stripRepoPath(path, self.depotPaths) - relPath = wildcard_decode(relPath) + # start with the full relative path where this file would + # go in a p4 client + if self.useClientSpec: + relPath = self.clientSpecDirs.map_in_client(path) + else: + relPath = self.stripRepoPath(path, self.depotPaths) for branch in self.knownBranches.keys(): - - # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 + # add a trailing slash so that a commit into qt/4.2foo + # doesn't end up in qt/4.2, e.g. if relPath.startswith(branch + "/"): if branch not in branches: branches[branch] = [] @@ -1867,7 +1893,6 @@ class P4Sync(Command, P4UserMap): def streamOneP4File(self, file, contents): relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes) - relPath = wildcard_decode(relPath) if verbose: sys.stderr.write("%s\n" % relPath) @@ -1936,7 +1961,6 @@ class P4Sync(Command, P4UserMap): def streamOneP4Deletion(self, file): relPath = self.stripRepoPath(file['path'], self.branchPrefixes) - relPath = wildcard_decode(relPath) if verbose: sys.stderr.write("delete %s\n" % relPath) self.gitStream.write("D %s\n" % relPath) @@ -2041,10 +2065,9 @@ class P4Sync(Command, P4UserMap): gitStream.write(description) gitStream.write("\n") - def commit(self, details, files, branch, branchPrefixes, parent = ""): + def commit(self, details, files, branch, parent = ""): epoch = details["time"] author = details["user"] - self.branchPrefixes = branchPrefixes if self.verbose: print "commit into %s" % branch @@ -2053,7 +2076,7 @@ class P4Sync(Command, P4UserMap): # create a commit. new_files = [] for f in files: - if [p for p in branchPrefixes if p4PathStartsWith(f['path'], p)]: + if [p for p in self.branchPrefixes if p4PathStartsWith(f['path'], p)]: new_files.append (f) else: sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path']) @@ -2070,8 +2093,8 @@ class P4Sync(Command, P4UserMap): self.gitStream.write("data <<EOT\n") self.gitStream.write(details["desc"]) - self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s" - % (','.join (branchPrefixes), details["change"])) + self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s" % + (','.join(self.branchPrefixes), details["change"])) if len(details['options']) > 0: self.gitStream.write(": options = %s" % details['options']) self.gitStream.write("]\nEOT\n\n") @@ -2094,7 +2117,7 @@ class P4Sync(Command, P4UserMap): print "Change %s is labelled %s" % (change, labelDetails) files = p4CmdList(["files"] + ["%s...@%s" % (p, change) - for p in branchPrefixes]) + for p in self.branchPrefixes]) if len(files) == len(labelRevisions): @@ -2405,6 +2428,7 @@ class P4Sync(Command, P4UserMap): for branch in branches.keys(): ## HACK --hwn branchPrefix = self.depotPaths[0] + branch + "/" + self.branchPrefixes = [ branchPrefix ] parent = "" @@ -2449,19 +2473,19 @@ class P4Sync(Command, P4UserMap): tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change)) if self.verbose: print "Creating temporary branch: " + tempBranch - self.commit(description, filesForCommit, tempBranch, [branchPrefix]) + self.commit(description, filesForCommit, tempBranch) self.tempBranches.append(tempBranch) self.checkpoint() blob = self.searchParent(parent, branch, tempBranch) if blob: - self.commit(description, filesForCommit, branch, [branchPrefix], blob) + self.commit(description, filesForCommit, branch, blob) else: if self.verbose: print "Parent of %s not found. Committing into head of %s" % (branch, parent) - self.commit(description, filesForCommit, branch, [branchPrefix], parent) + self.commit(description, filesForCommit, branch, parent) else: files = self.extractFilesFromCommit(description) - self.commit(description, files, self.branch, self.depotPaths, + self.commit(description, files, self.branch, self.initialParent) self.initialParent = "" except IOError: @@ -2525,7 +2549,7 @@ class P4Sync(Command, P4UserMap): self.updateOptionDict(details) try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) + self.commit(details, self.extractFilesFromCommit(details), self.branch) except IOError: print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() @@ -2683,6 +2707,9 @@ class P4Sync(Command, P4UserMap): self.depotPaths = newPaths + # --detect-branches may change this for each branch + self.branchPrefixes = self.depotPaths + self.loadUserMapFromCache() self.labels = {} if self.detectLabels: diff --git a/git-send-email.perl b/git-send-email.perl index 664713709c..aea66a0d47 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -681,6 +681,7 @@ sub ask { my ($prompt, %arg) = @_; my $valid_re = $arg{valid_re}; my $default = $arg{default}; + my $confirm_only = $arg{confirm_only}; my $resp; my $i = 0; return defined $default ? $default : undef @@ -698,6 +699,12 @@ sub ask { if (!defined $valid_re or $resp =~ /$valid_re/) { return $resp; } + if ($confirm_only) { + my $yesno = $term->readline("Are you sure you want to use <$resp> [y/N]? "); + if (defined $yesno && $yesno =~ /y/i) { + return $resp; + } + } } return undef; } @@ -745,13 +752,16 @@ my $prompting = 0; if (!defined $sender) { $sender = $repoauthor || $repocommitter || ''; $sender = ask("Who should the emails appear to be from? [$sender] ", - default => $sender); + default => $sender, + valid_re => qr/\@.*\./, confirm_only => 1); print "Emails will be sent from: ", $sender, "\n"; $prompting++; } if (!@initial_to && !defined $to_cmd) { - my $to = ask("Who should the emails be sent to? "); + my $to = ask("Who should the emails be sent to (if any)? ", + default => "", + valid_re => qr/\@.*\./, confirm_only => 1); push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later $prompting++; } @@ -777,7 +787,9 @@ sub expand_one_alias { if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to = ask( - "Message-ID to be used as In-Reply-To for the first email? "); + "Message-ID to be used as In-Reply-To for the first email (if any)? ", + default => "", + valid_re => qr/\@.*\./, confirm_only => 1); } if (defined $initial_reply_to) { $initial_reply_to =~ s/^\s*<?//; diff --git a/git-svn.perl b/git-svn.perl index 828b8f0c8e..0d77ffb0b9 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -29,7 +29,16 @@ use Git::SVN::Prompt; use Git::SVN::Log; use Git::SVN::Migration; -use Git::SVN::Utils qw(fatal can_compress); +use Git::SVN::Utils qw( + fatal + can_compress + canonicalize_path + canonicalize_url + join_paths + add_path_to_url + join_paths +); + use Git qw( git_cmd_try command @@ -1231,7 +1240,7 @@ sub cmd_show_ignore { my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); $gs ||= Git::SVN->new; my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); - $gs->prop_walk($gs->{path}, $r, sub { + $gs->prop_walk($gs->path, $r, sub { my ($gs, $path, $props) = @_; print STDOUT "\n# $path\n"; my $s = $props->{'svn:ignore'} or return; @@ -1247,7 +1256,7 @@ sub cmd_show_externals { my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); $gs ||= Git::SVN->new; my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); - $gs->prop_walk($gs->{path}, $r, sub { + $gs->prop_walk($gs->path, $r, sub { my ($gs, $path, $props) = @_; print STDOUT "\n# $path\n"; my $s = $props->{'svn:externals'} or return; @@ -1262,7 +1271,7 @@ sub cmd_create_ignore { my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); $gs ||= Git::SVN->new; my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); - $gs->prop_walk($gs->{path}, $r, sub { + $gs->prop_walk($gs->path, $r, sub { my ($gs, $path, $props) = @_; # $path is of the form /path/to/dir/ $path = '.' . $path; @@ -1292,31 +1301,6 @@ sub cmd_mkdirs { $gs->mkemptydirs($_revision); } -sub canonicalize_path { - my ($path) = @_; - my $dot_slash_added = 0; - if (substr($path, 0, 1) ne "/") { - $path = "./" . $path; - $dot_slash_added = 1; - } - # File::Spec->canonpath doesn't collapse x/../y into y (for a - # good reason), so let's do this manually. - $path =~ s#/+#/#g; - $path =~ s#/\.(?:/|$)#/#g; - $path =~ s#/[^/]+/\.\.##g; - $path =~ s#/$##g; - $path =~ s#^\./## if $dot_slash_added; - $path =~ s#^/##; - $path =~ s#^\.$##; - return $path; -} - -sub canonicalize_url { - my ($url) = @_; - $url =~ s#^([^:]+://[^/]*/)(.*)$#$1 . canonicalize_path($2)#e; - return $url; -} - # get_svnprops(PATH) # ------------------ # Helper for cmd_propget and cmd_proplist below. @@ -1330,7 +1314,7 @@ sub get_svnprops { $path = $cmd_dir_prefix . $path; fatal("No such file or directory: $path") unless -e $path; my $is_dir = -d $path ? 1 : 0; - $path = $gs->{path} . '/' . $path; + $path = join_paths($gs->{path}, $path); # canonicalize the path (otherwise libsvn will abort or fail to # find the file) @@ -1431,8 +1415,8 @@ sub cmd_commit_diff { fatal("Needed URL or usable git-svn --id in ", "the command-line\n", $usage); } - $url = $gs->{url}; - $svn_path = $gs->{path}; + $url = $gs->url; + $svn_path = $gs->path; } unless (defined $_revision) { fatal("-r|--revision is a required argument\n", $usage); @@ -1466,24 +1450,6 @@ sub cmd_commit_diff { } } -sub escape_uri_only { - my ($uri) = @_; - my @tmp; - foreach (split m{/}, $uri) { - s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; - push @tmp, $_; - } - join('/', @tmp); -} - -sub escape_url { - my ($url) = @_; - if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) { - my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3)); - $url = "$scheme://$domain$uri"; - } - $url; -} sub cmd_info { my $path = canonicalize_path(defined($_[0]) ? $_[0] : "."); @@ -1508,21 +1474,21 @@ sub cmd_info { # canonicalize_path() will return "" to make libsvn 1.5.x happy, $path = "." if $path eq ""; - my $full_url = $url . ($fullpath eq "" ? "" : "/$fullpath"); + my $full_url = canonicalize_url( add_path_to_url( $url, $fullpath ) ); if ($_url) { - print escape_url($full_url), "\n"; + print "$full_url\n"; return; } my $result = "Path: $path\n"; $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir"; - $result .= "URL: " . escape_url($full_url) . "\n"; + $result .= "URL: $full_url\n"; eval { my $repos_root = $gs->repos_root; Git::SVN::remove_username($repos_root); - $result .= "Repository Root: " . escape_url($repos_root) . "\n"; + $result .= "Repository Root: " . canonicalize_url($repos_root) . "\n"; }; if ($@) { $result .= "Repository Root: (offline)\n"; @@ -1669,7 +1635,9 @@ sub post_fetch_checkout { sub complete_svn_url { my ($url, $path) = @_; - $path =~ s#/+$##; + $path = canonicalize_path($path); + + # If the path is not a URL... if ($path !~ m#^[a-z\+]+://#) { if (!defined $url || $url !~ m#^[a-z\+]+://#) { fatal("E: '$path' is not a complete URL ", @@ -1686,7 +1654,7 @@ sub complete_url_ls_init { print STDERR "W: $switch not specified\n"; return; } - $repo_path =~ s#/+$##; + $repo_path = canonicalize_path($repo_path); if ($repo_path =~ m#^[a-z\+]+://#) { $ra = Git::SVN::Ra->new($repo_path); $repo_path = ''; @@ -1697,18 +1665,18 @@ sub complete_url_ls_init { "and a separate URL is not specified"); } } - my $url = $ra->{url}; + my $url = $ra->url; my $gs = Git::SVN->init($url, undef, undef, undef, 1); my $k = "svn-remote.$gs->{repo_id}.url"; my $orig_url = eval { command_oneline(qw/config --get/, $k) }; - if ($orig_url && ($orig_url ne $gs->{url})) { + if ($orig_url && ($orig_url ne $gs->url)) { die "$k already set: $orig_url\n", - "wanted to set to: $gs->{url}\n"; + "wanted to set to: $gs->url\n"; } - command_oneline('config', $k, $gs->{url}) unless $orig_url; - my $remote_path = "$gs->{path}/$repo_path"; + command_oneline('config', $k, $gs->url) unless $orig_url; + + my $remote_path = join_paths( $gs->path, $repo_path ); $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; - $remote_path =~ s#/+#/#g; $remote_path =~ s#^/##g; $remote_path .= "/*" if $remote_path !~ /\*/; my ($n) = ($switch =~ /^--(\w+)/); diff --git a/gitk-git/gitk b/gitk-git/gitk index 22270ce46b..d93bd990a9 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -2038,7 +2038,7 @@ proc makewindow {} { set file { mc "File" cascade { {mc "Update" command updatecommits -accelerator F5} - {mc "Reload" command reloadcommits -accelerator Meta1-F5} + {mc "Reload" command reloadcommits -accelerator Shift-F5} {mc "Reread references" command rereadrefs} {mc "List references" command showrefs -accelerator F2} {xx "" separator} @@ -2495,7 +2495,7 @@ proc makewindow {} { bindkey ? {dofind -1 1} bindkey f nextfile bind . <F5> updatecommits - bind . <$M1B-F5> reloadcommits + bind . <Shift-F5> reloadcommits bind . <F2> showrefs bind . <Shift-F4> {newview 0} catch { bind . <Shift-Key-XF86_Switch_VT_4> {newview 0} } @@ -10599,7 +10599,7 @@ proc movedhead {hid head} { } proc changedrefs {} { - global cached_dheads cached_dtags cached_atags + global cached_dheads cached_dtags cached_atags cached_tagcontent global arctags archeads arcnos arcout idheads idtags foreach id [concat [array names idheads] [array names idtags]] { @@ -10611,6 +10611,7 @@ proc changedrefs {} { } } } + catch {unset cached_tagcontent} catch {unset cached_dtags} catch {unset cached_atags} catch {unset cached_dheads} @@ -10663,7 +10664,7 @@ proc listrefs {id} { } proc showtag {tag isnew} { - global ctext tagcontents tagids linknum tagobjid + global ctext cached_tagcontent tagids linknum tagobjid if {$isnew} { addtohistory [list showtag $tag 0] savectextpos @@ -10672,13 +10673,13 @@ proc showtag {tag isnew} { clear_ctext settabs 0 set linknum 0 - if {![info exists tagcontents($tag)]} { + if {![info exists cached_tagcontent($tag)]} { catch { - set tagcontents($tag) [exec git cat-file tag $tag] + set cached_tagcontent($tag) [exec git cat-file tag $tag] } } - if {[info exists tagcontents($tag)]} { - set text $tagcontents($tag) + if {[info exists cached_tagcontent($tag)]} { + set text $cached_tagcontent($tag) } else { set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)" } @@ -11532,6 +11533,11 @@ if {[catch {package require Tk 8.4} err]} { exit 1 } +# Unset GIT_TRACE var if set +if { [info exists ::env(GIT_TRACE)] } { + unset ::env(GIT_TRACE) +} + # defaults... set wrcomcmd "git diff-tree --stdin -p --pretty" @@ -58,6 +58,14 @@ enum grep_expr_node { GREP_NODE_OR }; +enum grep_pattern_type { + GREP_PATTERN_TYPE_UNSPECIFIED = 0, + GREP_PATTERN_TYPE_BRE, + GREP_PATTERN_TYPE_ERE, + GREP_PATTERN_TYPE_FIXED, + GREP_PATTERN_TYPE_PCRE +}; + struct grep_expr { enum grep_expr_node node; unsigned hit; @@ -103,6 +111,8 @@ struct grep_opt { int max_depth; int funcname; int funcbody; + int extended_regexp_option; + int pattern_type_option; char color_context[COLOR_MAXLEN]; char color_filename[COLOR_MAXLEN]; char color_function[COLOR_MAXLEN]; diff --git a/http-push.c b/http-push.c index a832ca77a3..8701c1215d 100644 --- a/http-push.c +++ b/http-push.c @@ -1610,9 +1610,8 @@ static int verify_merge_base(unsigned char *head_sha1, struct ref *remote) { struct commit *head = lookup_commit_or_die(head_sha1, "HEAD"); struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name); - struct commit_list *merge_bases = get_merge_bases(head, branch, 1); - return (merge_bases && !merge_bases->next && merge_bases->item == branch); + return in_merge_bases(branch, head); } static int delete_remote_branch(const char *pattern, int force) @@ -210,8 +210,10 @@ int split_ident_line(struct ident_split *split, const char *line, int len) split->name_end = cp + 1; break; } - if (!split->name_end) - return status; + if (!split->name_end) { + /* no human readable name */ + split->name_end = split->name_begin; + } for (cp = split->mail_begin; cp < line + len; cp++) if (*cp == '>') { diff --git a/mergetools/codecompare b/mergetools/codecompare new file mode 100644 index 0000000000..3f0486bc80 --- /dev/null +++ b/mergetools/codecompare @@ -0,0 +1,25 @@ +diff_cmd () { + "$merge_tool_path" "$LOCAL" "$REMOTE" +} + +merge_cmd () { + touch "$BACKUP" + if $base_present + then + "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" -BF="$BASE" \ + -RF="$MERGED" + else + "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" \ + -RF="$MERGED" + fi + check_unchanged +} + +translate_merge_tool_path() { + if merge_mode + then + echo CodeMerge + else + echo CodeCompare + fi +} diff --git a/parse-options.h b/parse-options.h index 77a4a8b2e6..71a39c60d9 100644 --- a/parse-options.h +++ b/parse-options.h @@ -238,6 +238,6 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int); #define OPT__COLOR(var, h) \ OPT_COLOR_FLAG(0, "color", (var), (h)) #define OPT_COLUMN(s, l, v, h) \ - { OPTION_CALLBACK, (s), (l), (v), "style", (h), PARSE_OPT_OPTARG, parseopt_column_callback } + { OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback } #endif diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm index 8478d0c952..acb25394f4 100644 --- a/perl/Git/SVN.pm +++ b/perl/Git/SVN.pm @@ -23,7 +23,14 @@ use Git qw( command_output_pipe command_close_pipe ); -use Git::SVN::Utils qw(fatal can_compress); +use Git::SVN::Utils qw( + fatal + can_compress + join_paths + canonicalize_path + canonicalize_url + add_path_to_url +); my $can_use_yaml; BEGIN { @@ -195,9 +202,9 @@ sub read_all_remotes { } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) { $r->{$1}->{svm} = {}; } elsif (m!^(.+)\.url=\s*(.*)\s*$!) { - $r->{$1}->{url} = $2; + $r->{$1}->{url} = canonicalize_url($2); } elsif (m!^(.+)\.pushurl=\s*(.*)\s*$!) { - $r->{$1}->{pushurl} = $2; + $r->{$1}->{pushurl} = canonicalize_url($2); } elsif (m!^(.+)\.ignore-refs=\s*(.*)\s*$!) { $r->{$1}->{ignore_refs_regex} = $2; } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) { @@ -290,7 +297,7 @@ sub find_existing_remote { sub init_remote_config { my ($self, $url, $no_write) = @_; - $url =~ s!/+$!!; # strip trailing slash + $url = canonicalize_url($url); my $r = read_all_remotes(); my $existing = find_existing_remote($url, $r); if ($existing) { @@ -314,12 +321,10 @@ sub init_remote_config { print STDERR "Using higher level of URL: ", "$url => $min_url\n"; } - my $old_path = $self->{path}; - $self->{path} = $url; - $self->{path} =~ s!^\Q$min_url\E(/|$)!!; - if (length $old_path) { - $self->{path} .= "/$old_path"; - } + my $old_path = $self->path; + $url =~ s!^\Q$min_url\E(/|$)!!; + $url = join_paths($url, $old_path); + $self->path($url); $url = $min_url; } } @@ -343,18 +348,22 @@ sub init_remote_config { unless ($no_write) { command_noisy('config', "svn-remote.$self->{repo_id}.url", $url); - $self->{path} =~ s{^/}{}; - $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; + my $path = $self->path; + $path =~ s{^/}{}; + $path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg; + $self->path($path); command_noisy('config', '--add', "svn-remote.$self->{repo_id}.fetch", - "$self->{path}:".$self->refname); + $self->path.":".$self->refname); } - $self->{url} = $url; + $self->url($url); } sub find_by_url { # repos_root and, path are optional my ($class, $full_url, $repos_root, $path) = @_; + $full_url = canonicalize_url($full_url); + return undef unless defined $full_url; remove_username($full_url); remove_username($repos_root) if defined $repos_root; @@ -393,6 +402,11 @@ sub find_by_url { # repos_root and, path are optional } $p =~ s#^\Q$z\E(?:/|$)#$prefix# or next; } + + # remote fetch paths are not URI escaped. Decode ours + # so they match + $p = uri_decode($p); + foreach my $f (keys %$fetch) { next if $f ne $p; return Git::SVN->new($fetch->{$f}, $repo_id, $f); @@ -435,20 +449,25 @@ sub new { } } my $self = _new($class, $repo_id, $ref_id, $path); - if (!defined $self->{path} || !length $self->{path}) { + if (!defined $self->path || !length $self->path) { my $fetch = command_oneline('config', '--get', "svn-remote.$repo_id.fetch", ":$ref_id\$") or die "Failed to read \"svn-remote.$repo_id.fetch\" ", "\":$ref_id\$\" in config\n"; - ($self->{path}, undef) = split(/\s*:\s*/, $fetch); + my($path) = split(/\s*:\s*/, $fetch); + $self->path($path); } - $self->{path} =~ s{/+}{/}g; - $self->{path} =~ s{\A/}{}; - $self->{path} =~ s{/\z}{}; - $self->{url} = command_oneline('config', '--get', - "svn-remote.$repo_id.url") or + { + my $path = $self->path; + $path =~ s{\A/}{}; + $path =~ s{/\z}{}; + $self->path($path); + } + my $url = command_oneline('config', '--get', + "svn-remote.$repo_id.url") or die "Failed to read \"svn-remote.$repo_id.url\" in config\n"; + $self->url($url); $self->{pushurl} = eval { command_oneline('config', '--get', "svn-remote.$repo_id.pushurl") }; $self->rebuild; @@ -552,8 +571,7 @@ sub _set_svm_vars { # username is of no interest $src =~ s{(^[a-z\+]*://)[^/@]*@}{$1}; - my $replace = $ra->{url}; - $replace .= "/$path" if length $path; + my $replace = add_path_to_url($ra->url, $path); my $section = "svn-remote.$self->{repo_id}"; tmp_config("$section.svm-source", $src); @@ -567,20 +585,21 @@ sub _set_svm_vars { } my $r = $ra->get_latest_revnum; - my $path = $self->{path}; + my $path = $self->path; my %tried; while (length $path) { - unless ($tried{"$self->{url}/$path"}) { + my $try = add_path_to_url($self->url, $path); + unless ($tried{$try}) { return $ra if $self->read_svm_props($ra, $path, $r); - $tried{"$self->{url}/$path"} = 1; + $tried{$try} = 1; } $path =~ s#/?[^/]+$##; } die "Path: '$path' should be ''\n" if $path ne ''; return $ra if $self->read_svm_props($ra, $path, $r); - $tried{"$self->{url}/$path"} = 1; + $tried{ add_path_to_url($self->url, $path) } = 1; - if ($ra->{repos_root} eq $self->{url}) { + if ($ra->{repos_root} eq $self->url) { die @err, (map { " $_\n" } keys %tried), "\n"; } @@ -590,20 +609,21 @@ sub _set_svm_vars { $path = $ra->{svn_path}; $ra = Git::SVN::Ra->new($ra->{repos_root}); while (length $path) { - unless ($tried{"$ra->{url}/$path"}) { + my $try = add_path_to_url($ra->url, $path); + unless ($tried{$try}) { $ok = $self->read_svm_props($ra, $path, $r); last if $ok; - $tried{"$ra->{url}/$path"} = 1; + $tried{$try} = 1; } $path =~ s#/?[^/]+$##; } die "Path: '$path' should be ''\n" if $path ne ''; $ok ||= $self->read_svm_props($ra, $path, $r); - $tried{"$ra->{url}/$path"} = 1; + $tried{ add_path_to_url($ra->url, $path) } = 1; if (!$ok) { die @err, (map { " $_\n" } keys %tried), "\n"; } - Git::SVN::Ra->new($self->{url}); + Git::SVN::Ra->new($self->url); } sub svnsync { @@ -670,7 +690,7 @@ sub ra_uuid { if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/i) { $self->{ra_uuid} = $uuid; } else { - die "ra_uuid called without URL\n" unless $self->{url}; + die "ra_uuid called without URL\n" unless $self->url; $self->{ra_uuid} = $self->ra->get_uuid; tmp_config('--add', $key, $self->{ra_uuid}); } @@ -694,7 +714,7 @@ sub repos_root { sub ra { my ($self) = shift; - my $ra = Git::SVN::Ra->new($self->{url}); + my $ra = Git::SVN::Ra->new($self->url); $self->_set_repos_root($ra->{repos_root}); if ($self->use_svm_props && !$self->{svm}) { if ($self->no_metadata) { @@ -728,7 +748,7 @@ sub prop_walk { $path =~ s#^/*#/#g; my $p = $path; # Strip the irrelevant part of the path. - $p =~ s#^/+\Q$self->{path}\E(/|$)#/#; + $p =~ s#^/+\Q@{[$self->path]}\E(/|$)#/#; # Ensure the path is terminated by a `/'. $p =~ s#/*$#/#; @@ -749,7 +769,7 @@ sub prop_walk { foreach (sort keys %$dirent) { next if $dirent->{$_}->{kind} != $SVN::Node::dir; - $self->prop_walk($self->{path} . $p . $_, $rev, $sub); + $self->prop_walk($self->path . $p . $_, $rev, $sub); } } @@ -919,20 +939,19 @@ sub rewrite_uuid { sub metadata_url { my ($self) = @_; - ($self->rewrite_root || $self->{url}) . - (length $self->{path} ? '/' . $self->{path} : ''); + my $url = $self->rewrite_root || $self->url; + return canonicalize_url( add_path_to_url( $url, $self->path ) ); } sub full_url { my ($self) = @_; - $self->{url} . (length $self->{path} ? '/' . $self->{path} : ''); + return canonicalize_url( add_path_to_url( $self->url, $self->path ) ); } sub full_pushurl { my ($self) = @_; if ($self->{pushurl}) { - return $self->{pushurl} . (length $self->{path} ? '/' . - $self->{path} : ''); + return canonicalize_url( add_path_to_url( $self->{pushurl}, $self->path ) ); } else { return $self->full_url; } @@ -1048,20 +1067,20 @@ sub do_git_commit { sub match_paths { my ($self, $paths, $r) = @_; - return 1 if $self->{path} eq ''; - if (my $path = $paths->{"/$self->{path}"}) { + return 1 if $self->path eq ''; + if (my $path = $paths->{"/".$self->path}) { return ($path->{action} eq 'D') ? 0 : 1; } - $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//; + $self->{path_regex} ||= qr{^/\Q@{[$self->path]}\E/}; if (grep /$self->{path_regex}/, keys %$paths) { return 1; } my $c = ''; - foreach (split m#/#, $self->{path}) { + foreach (split m#/#, $self->path) { $c .= "/$_"; next unless ($paths->{$c} && ($paths->{$c}->{action} =~ /^[AR]$/)); - if ($self->ra->check_path($self->{path}, $r) == + if ($self->ra->check_path($self->path, $r) == $SVN::Node::dir) { return 1; } @@ -1075,14 +1094,14 @@ sub find_parent_branch { unless (defined $paths) { my $err_handler = $SVN::Error::handler; $SVN::Error::handler = \&Git::SVN::Ra::skip_unknown_revs; - $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, + $self->ra->get_log([$self->path], $rev, $rev, 0, 1, 1, sub { $paths = $_[0] }); $SVN::Error::handler = $err_handler; } return undef unless defined $paths; # look for a parent from another branch: - my @b_path_components = split m#/#, $self->{path}; + my @b_path_components = split m#/#, $self->path; my @a_path_components; my $i; while (@b_path_components) { @@ -1099,8 +1118,8 @@ sub find_parent_branch { } my $r = $i->{copyfrom_rev}; my $repos_root = $self->ra->{repos_root}; - my $url = $self->ra->{url}; - my $new_url = $url . $branch_from; + my $url = $self->ra->url; + my $new_url = canonicalize_url( add_path_to_url( $url, $branch_from ) ); print STDERR "Found possible branch point: ", "$new_url => ", $self->full_url, ", $r\n" unless $::_q > 1; @@ -1114,7 +1133,7 @@ sub find_parent_branch { ($base, $head) = parse_revision_argument(0, $r); } else { if ($r0 < $r) { - $gs->ra->get_log([$gs->{path}], $r0 + 1, $r, 1, + $gs->ra->get_log([$gs->path], $r0 + 1, $r, 1, 0, 1, sub { $base = $_[1] - 1 }); } } @@ -1136,7 +1155,7 @@ sub find_parent_branch { # at the moment), so we can't rely on it $self->{last_rev} = $r0; $self->{last_commit} = $parent; - $ed = Git::SVN::Fetcher->new($self, $gs->{path}); + $ed = Git::SVN::Fetcher->new($self, $gs->path); $gs->ra->gs_do_switch($r0, $rev, $gs, $self->full_url, $ed) or die "SVN connection failed somewhere...\n"; @@ -1235,7 +1254,7 @@ sub mkemptydirs { close $fh; } - my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/; + my $strip = qr/\A\Q@{[$self->path]}\E(?:\/|$)/; foreach my $d (sort keys %empty_dirs) { $d = uri_decode($d); $d =~ s/$strip//; @@ -1429,12 +1448,11 @@ sub find_extra_svk_parents { for my $ticket ( @tickets ) { my ($uuid, $path, $rev) = split /:/, $ticket; if ( $uuid eq $self->ra_uuid ) { - my $url = $self->{url}; - my $repos_root = $url; + my $repos_root = $self->url; my $branch_from = $path; $branch_from =~ s{^/}{}; - my $gs = $self->other_gs($repos_root."/".$branch_from, - $url, + my $gs = $self->other_gs(add_path_to_url( $repos_root, $branch_from ), + $repos_root, $branch_from, $rev, $self->{ref_id}); @@ -1693,7 +1711,7 @@ sub find_extra_svn_parents { # are now marked as merge, we can add the tip as a parent. my @merges = split "\n", $mergeinfo; my @merge_tips; - my $url = $self->{url}; + my $url = $self->url; my $uuid = $self->ra_uuid; my %ranges; for my $merge ( @merges ) { @@ -1875,8 +1893,9 @@ sub make_log_entry { $email ||= "$author\@$uuid"; $commit_email ||= "$author\@$uuid"; } elsif ($self->use_svnsync_props) { - my $full_url = $self->svnsync->{url}; - $full_url .= "/$self->{path}" if length $self->{path}; + my $full_url = canonicalize_url( + add_path_to_url( $self->svnsync->{url}, $self->path ) + ); remove_username($full_url); my $uuid = $self->svnsync->{uuid}; $log_entry{metadata} = "$full_url\@$rev $uuid"; @@ -1923,7 +1942,7 @@ sub set_tree { tree_b => $tree, editor_cb => sub { $self->set_tree_cb($log_entry, $tree, @_) }, - svn_path => $self->{path} ); + svn_path => $self->path ); if (!Git::SVN::Editor->new(\%ed_opts)->apply_diff) { print "No changes\nr$self->{last_rev} = $tree\n"; } @@ -2299,10 +2318,39 @@ sub _new { $_[3] = $path = '' unless (defined $path); mkpath([$dir]); - bless { + my $obj = bless { ref_id => $ref_id, dir => $dir, index => "$dir/index", - path => $path, config => "$ENV{GIT_DIR}/svn/config", + config => "$ENV{GIT_DIR}/svn/config", map_root => "$dir/.rev_map", repo_id => $repo_id }, $class; + + # Ensure it gets canonicalized + $obj->path($path); + + return $obj; +} + +sub path { + my $self = shift; + + if (@_) { + my $path = shift; + $self->{path} = canonicalize_path($path); + return; + } + + return $self->{path}; +} + +sub url { + my $self = shift; + + if (@_) { + my $url = shift; + $self->{url} = canonicalize_url($url); + return; + } + + return $self->{url}; } # for read-only access of old .rev_db formats diff --git a/perl/Git/SVN/Fetcher.pm b/perl/Git/SVN/Fetcher.pm index 76fae9bce0..046a7a2f31 100644 --- a/perl/Git/SVN/Fetcher.pm +++ b/perl/Git/SVN/Fetcher.pm @@ -83,7 +83,7 @@ sub _mark_empty_symlinks { chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`); my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt); local $/ = "\0"; - my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path}; + my $pfx = defined($switch_path) ? $switch_path : $git_svn->path; $pfx .= '/' if length($pfx); while (<$ls>) { chomp; diff --git a/perl/Git/SVN/Migration.pm b/perl/Git/SVN/Migration.pm index 75d74298ea..30daf35465 100644 --- a/perl/Git/SVN/Migration.pm +++ b/perl/Git/SVN/Migration.pm @@ -177,14 +177,14 @@ sub minimize_connections { my $ra = Git::SVN::Ra->new($url); # skip existing cases where we already connect to the root - if (($ra->{url} eq $ra->{repos_root}) || + if (($ra->url eq $ra->{repos_root}) || ($ra->{repos_root} eq $repo_id)) { - $root_repos->{$ra->{url}} = $repo_id; + $root_repos->{$ra->url} = $repo_id; next; } my $root_ra = Git::SVN::Ra->new($ra->{repos_root}); - my $root_path = $ra->{url}; + my $root_path = $ra->url; $root_path =~ s#^\Q$ra->{repos_root}\E(/|$)##; foreach my $path (keys %$fetch) { my $ref_id = $fetch->{$path}; diff --git a/perl/Git/SVN/Ra.pm b/perl/Git/SVN/Ra.pm index 23ff43e86b..90ec30bfff 100644 --- a/perl/Git/SVN/Ra.pm +++ b/perl/Git/SVN/Ra.pm @@ -3,6 +3,12 @@ use vars qw/@ISA $config_dir $_ignore_refs_regex $_log_window_size/; use strict; use warnings; use SVN::Client; +use Git::SVN::Utils qw( + canonicalize_url + canonicalize_path + add_path_to_url +); + use SVN::Ra; BEGIN { @ISA = qw(SVN::Ra); @@ -62,29 +68,11 @@ sub _auth_providers () { \@rv; } -sub escape_uri_only { - my ($uri) = @_; - my @tmp; - foreach (split m{/}, $uri) { - s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; - push @tmp, $_; - } - join('/', @tmp); -} - -sub escape_url { - my ($url) = @_; - if ($url =~ m#^(https?)://([^/]+)(.*)$#) { - my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3)); - $url = "$scheme://$domain$uri"; - } - $url; -} sub new { my ($class, $url) = @_; - $url =~ s!/+$!!; - return $RA if ($RA && $RA->{url} eq $url); + $url = canonicalize_url($url); + return $RA if ($RA && $RA->url eq $url); ::_req_svn(); @@ -115,17 +103,34 @@ sub new { $Git::SVN::Prompt::_no_auth_cache = 1; } } # no warnings 'once' - my $self = SVN::Ra->new(url => escape_url($url), auth => $baton, + + my $self = SVN::Ra->new(url => $url, auth => $baton, config => $config, pool => SVN::Pool->new, auth_provider_callbacks => $callbacks); - $self->{url} = $url; + $RA = bless $self, $class; + + # Make sure its canonicalized + $self->url($url); $self->{svn_path} = $url; $self->{repos_root} = $self->get_repos_root; $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##; $self->{cache} = { check_path => { r => 0, data => {} }, get_dir => { r => 0, data => {} } }; - $RA = bless $self, $class; + + return $RA; +} + +sub url { + my $self = shift; + + if (@_) { + my $url = shift; + $self->{url} = canonicalize_url($url); + return; + } + + return $self->{url}; } sub check_path { @@ -195,6 +200,7 @@ sub get_log { qw/copyfrom_path copyfrom_rev action/; if ($s{'copyfrom_path'}) { $s{'copyfrom_path'} =~ s/$prefix_regex//; + $s{'copyfrom_path'} = canonicalize_path($s{'copyfrom_path'}); } $_[0]{$p} = \%s; } @@ -246,7 +252,7 @@ sub get_commit_editor { sub gs_do_update { my ($self, $rev_a, $rev_b, $gs, $editor) = @_; my $new = ($rev_a == $rev_b); - my $path = $gs->{path}; + my $path = $gs->path; if ($new && -e $gs->{index}) { unlink $gs->{index} or die @@ -282,30 +288,33 @@ sub gs_do_update { # svn_ra_reparent didn't work before 1.4) sub gs_do_switch { my ($self, $rev_a, $rev_b, $gs, $url_b, $editor) = @_; - my $path = $gs->{path}; + my $path = $gs->path; my $pool = SVN::Pool->new; - my $full_url = $self->{url}; - my $old_url = $full_url; - $full_url .= '/' . $path if length $path; + my $old_url = $self->url; + my $full_url = add_path_to_url( $self->url, $path ); my ($ra, $reparented); if ($old_url =~ m#^svn(\+ssh)?://# || ($full_url =~ m#^https?://# && - escape_url($full_url) ne $full_url)) { + canonicalize_url($full_url) ne $full_url)) { $_[0] = undef; $self = undef; $RA = undef; $ra = Git::SVN::Ra->new($full_url); $ra_invalid = 1; } elsif ($old_url ne $full_url) { - SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool); - $self->{url} = $full_url; + SVN::_Ra::svn_ra_reparent( + $self->{session}, + canonicalize_url($full_url), + $pool + ); + $self->url($full_url); $reparented = 1; } $ra ||= $self; - $url_b = escape_url($url_b); + $url_b = canonicalize_url($url_b); my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool); my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef) : (); $reporter->set_path('', $rev_a, 0, @lock, $pool); @@ -313,7 +322,7 @@ sub gs_do_switch { if ($reparented) { SVN::_Ra::svn_ra_reparent($self->{session}, $old_url, $pool); - $self->{url} = $old_url; + $self->url($old_url); } $pool->clear; @@ -326,7 +335,7 @@ sub longest_common_path { my $common_max = scalar @$gsv; foreach my $gs (@$gsv) { - my @tmp = split m#/#, $gs->{path}; + my @tmp = split m#/#, $gs->path; my $p = ''; foreach (@tmp) { $p .= length($p) ? "/$_" : $_; @@ -362,7 +371,7 @@ sub gs_fetch_loop_common { my $inc = $_log_window_size; my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc); my $longest_path = longest_common_path($gsv, $globs); - my $ra_url = $self->{url}; + my $ra_url = $self->url; my $find_trailing_edge; while (1) { my %revs; @@ -508,7 +517,7 @@ sub match_globs { ($self->check_path($p, $r) != $SVN::Node::dir)); next unless $p =~ /$g->{path}->{regex}/; - $exists->{$p} = Git::SVN->init($self->{url}, $p, undef, + $exists->{$p} = Git::SVN->init($self->url, $p, undef, $g->{ref}->full_path($de), 1); } } @@ -532,7 +541,7 @@ sub match_globs { next if ($self->check_path($pathname, $r) != $SVN::Node::dir); $exists->{$pathname} = Git::SVN->init( - $self->{url}, $pathname, undef, + $self->url, $pathname, undef, $g->{ref}->full_path($p), 1); } my $c = ''; @@ -548,19 +557,20 @@ sub match_globs { sub minimize_url { my ($self) = @_; - return $self->{url} if ($self->{url} eq $self->{repos_root}); + return $self->url if ($self->url eq $self->{repos_root}); my $url = $self->{repos_root}; my @components = split(m!/!, $self->{svn_path}); my $c = ''; do { - $url .= "/$c" if length $c; + $url = add_path_to_url($url, $c); eval { my $ra = (ref $self)->new($url); my $latest = $ra->get_latest_revnum; $ra->get_log("", $latest, 0, 1, 0, 1, sub {}); }; } while ($@ && ($c = shift @components)); - $url; + + return canonicalize_url($url); } sub can_do_switch { @@ -568,7 +578,7 @@ sub can_do_switch { unless (defined $can_do_switch) { my $pool = SVN::Pool->new; my $rep = eval { - $self->do_switch(1, '', 0, $self->{url}, + $self->do_switch(1, '', 0, $self->url, SVN::Delta::Editor->new, $pool); }; if ($@) { diff --git a/perl/Git/SVN/Utils.pm b/perl/Git/SVN/Utils.pm index 496006bc7b..4bb4dde89a 100644 --- a/perl/Git/SVN/Utils.pm +++ b/perl/Git/SVN/Utils.pm @@ -3,9 +3,18 @@ package Git::SVN::Utils; use strict; use warnings; +use SVN::Core; + use base qw(Exporter); -our @EXPORT_OK = qw(fatal can_compress); +our @EXPORT_OK = qw( + fatal + can_compress + canonicalize_path + canonicalize_url + join_paths + add_path_to_url +); =head1 NAME @@ -56,4 +65,169 @@ sub can_compress { } +=head3 canonicalize_path + + my $canoncalized_path = canonicalize_path($path); + +Converts $path into a canonical form which is safe to pass to the SVN +API as a file path. + +=cut + +# Turn foo/../bar into bar +sub _collapse_dotdot { + my $path = shift; + + 1 while $path =~ s{/[^/]+/+\.\.}{}; + 1 while $path =~ s{[^/]+/+\.\./}{}; + 1 while $path =~ s{[^/]+/+\.\.}{}; + + return $path; +} + + +sub canonicalize_path { + my $path = shift; + my $rv; + + # The 1.7 way to do it + if ( defined &SVN::_Core::svn_dirent_canonicalize ) { + $path = _collapse_dotdot($path); + $rv = SVN::_Core::svn_dirent_canonicalize($path); + } + # The 1.6 way to do it + # This can return undef on subversion-perl-1.4.2-2.el5 (CentOS 5.2) + elsif ( defined &SVN::_Core::svn_path_canonicalize ) { + $path = _collapse_dotdot($path); + $rv = SVN::_Core::svn_path_canonicalize($path); + } + + return $rv if defined $rv; + + # No SVN API canonicalization is available, or the SVN API + # didn't return a successful result, do it ourselves + return _canonicalize_path_ourselves($path); +} + + +sub _canonicalize_path_ourselves { + my ($path) = @_; + my $dot_slash_added = 0; + if (substr($path, 0, 1) ne "/") { + $path = "./" . $path; + $dot_slash_added = 1; + } + $path =~ s#/+#/#g; + $path =~ s#/\.(?:/|$)#/#g; + $path = _collapse_dotdot($path); + $path =~ s#/$##g; + $path =~ s#^\./## if $dot_slash_added; + $path =~ s#^/##; + $path =~ s#^\.$##; + return $path; +} + + +=head3 canonicalize_url + + my $canonicalized_url = canonicalize_url($url); + +Converts $url into a canonical form which is safe to pass to the SVN +API as a URL. + +=cut + +sub canonicalize_url { + my $url = shift; + + # The 1.7 way to do it + if ( defined &SVN::_Core::svn_uri_canonicalize ) { + return SVN::_Core::svn_uri_canonicalize($url); + } + # There wasn't a 1.6 way to do it, so we do it ourself. + else { + return _canonicalize_url_ourselves($url); + } +} + + +sub _canonicalize_url_path { + my ($uri_path) = @_; + + my @parts; + foreach my $part (split m{/+}, $uri_path) { + $part =~ s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; + push @parts, $part; + } + + return join('/', @parts); +} + +sub _canonicalize_url_ourselves { + my ($url) = @_; + if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) { + my ($scheme, $domain, $uri) = ($1, $2, _canonicalize_url_path(canonicalize_path($3))); + $url = "$scheme://$domain$uri"; + } + $url; +} + + +=head3 join_paths + + my $new_path = join_paths(@paths); + +Appends @paths together into a single path. Any empty paths are ignored. + +=cut + +sub join_paths { + my @paths = @_; + + @paths = grep { defined $_ && length $_ } @paths; + + return '' unless @paths; + return $paths[0] if @paths == 1; + + my $new_path = shift @paths; + $new_path =~ s{/+$}{}; + + my $last_path = pop @paths; + $last_path =~ s{^/+}{}; + + for my $path (@paths) { + $path =~ s{^/+}{}; + $path =~ s{/+$}{}; + $new_path .= "/$path"; + } + + return $new_path .= "/$last_path"; +} + + +=head3 add_path_to_url + + my $new_url = add_path_to_url($url, $path); + +Appends $path onto the $url. If $path is empty, $url is returned unchanged. + +=cut + +sub add_path_to_url { + my($url, $path) = @_; + + return $url if !defined $path or !length $path; + + # Strip trailing and leading slashes so we don't + # wind up with http://x.com///path + $url =~ s{/+$}{}; + $path =~ s{^/+}{}; + + # If a path has a % in it, URI escape it so it's not + # mistaken for a URI escape later. + $path =~ s{%}{%25}g; + + return join '/', $url, $path; +} + 1; diff --git a/revision.c b/revision.c index cbcae1086b..dc3fecf903 100644 --- a/revision.c +++ b/revision.c @@ -1312,7 +1312,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") || !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") || !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") || - !prefixcmp(arg, "--remotes=")) + !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk=")) { unkv[(*unkc)++] = arg; return 1; @@ -1707,7 +1707,18 @@ static int handle_revision_pseudo_opt(const char *submodule, } else if (!strcmp(arg, "--not")) { *flags ^= UNINTERESTING; } else if (!strcmp(arg, "--no-walk")) { - revs->no_walk = 1; + revs->no_walk = REVISION_WALK_NO_WALK_SORTED; + } else if (!prefixcmp(arg, "--no-walk=")) { + /* + * Detached form ("--no-walk X" as opposed to "--no-walk=X") + * not allowed, since the argument is optional. + */ + if (!strcmp(arg + 10, "sorted")) + revs->no_walk = REVISION_WALK_NO_WALK_SORTED; + else if (!strcmp(arg + 10, "unsorted")) + revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED; + else + return error("invalid argument to --no-walk"); } else if (!strcmp(arg, "--do-walk")) { revs->no_walk = 0; } else { @@ -2129,10 +2140,11 @@ int prepare_revision_walk(struct rev_info *revs) } e++; } - commit_list_sort_by_date(&revs->commits); if (!revs->leak_pending) free(list); + if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED) + commit_list_sort_by_date(&revs->commits); if (revs->no_walk) return 0; if (revs->limited) diff --git a/revision.h b/revision.h index cb5ab3513b..a95bd0b3f3 100644 --- a/revision.h +++ b/revision.h @@ -41,6 +41,10 @@ struct rev_cmdline_info { } *rev; }; +#define REVISION_WALK_WALK 0 +#define REVISION_WALK_NO_WALK_SORTED 1 +#define REVISION_WALK_NO_WALK_UNSORTED 2 + struct rev_info { /* Starting list */ struct commit_list *commits; @@ -62,7 +66,7 @@ struct rev_info { /* Traversal flags */ unsigned int dense:1, prune:1, - no_walk:1, + no_walk:2, show_all:1, remove_empty_trees:1, simplify_history:1, diff --git a/sequencer.c b/sequencer.c index bf078f274b..f86f116a15 100644 --- a/sequencer.c +++ b/sequencer.c @@ -311,6 +311,9 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, if (allow_empty) argv_array_push(&array, "--allow-empty"); + if (opts->allow_empty_message) + argv_array_push(&array, "--allow-empty-message"); + rc = run_command_v_opt(array.argv, RUN_GIT_CMD); argv_array_clear(&array); return rc; @@ -543,7 +546,11 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) static void prepare_revs(struct replay_opts *opts) { - if (opts->action != REPLAY_REVERT) + /* + * picking (but not reverting) ranges (but not individual revisions) + * should be done in reverse + */ + if (opts->action == REPLAY_PICK && !opts->revs->no_walk) opts->revs->reverse ^= 1; if (prepare_revision_walk(opts->revs)) diff --git a/sequencer.h b/sequencer.h index aa5f17cc30..d8494201e0 100644 --- a/sequencer.h +++ b/sequencer.h @@ -30,6 +30,7 @@ struct replay_opts { int allow_ff; int allow_rerere_auto; int allow_empty; + int allow_empty_message; int keep_redundant_commits; int mainline; diff --git a/submodule.c b/submodule.c index 19dc6a6c0d..50f213e926 100644 --- a/submodule.c +++ b/submodule.c @@ -588,13 +588,13 @@ static void calculate_changed_submodule_paths(void) initialized_fetch_ref_tips = 0; } -int fetch_populated_submodules(int num_options, const char **options, +int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, int quiet) { - int i, result = 0, argc = 0, default_argc; + int i, result = 0; struct child_process cp; - const char **argv; + struct argv_array argv = ARGV_ARRAY_INIT; struct string_list_item *name_for_path; const char *work_tree = get_git_work_tree(); if (!work_tree) @@ -604,17 +604,13 @@ int fetch_populated_submodules(int num_options, const char **options, if (read_cache() < 0) die("index file corrupt"); - /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */ - argv = xcalloc(num_options + 6, sizeof(const char *)); - argv[argc++] = "fetch"; - for (i = 0; i < num_options; i++) - argv[argc++] = options[i]; - argv[argc++] = "--recurse-submodules-default"; - default_argc = argc++; - argv[argc++] = "--submodule-prefix"; + argv_array_push(&argv, "fetch"); + for (i = 0; i < options->argc; i++) + argv_array_push(&argv, options->argv[i]); + argv_array_push(&argv, "--recurse-submodules-default"); + /* default value, "--submodule-prefix" and its value are added later */ memset(&cp, 0, sizeof(cp)); - cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; @@ -674,16 +670,21 @@ int fetch_populated_submodules(int num_options, const char **options, if (!quiet) printf("Fetching submodule %s%s\n", prefix, ce->name); cp.dir = submodule_path.buf; - argv[default_argc] = default_argv; - argv[argc] = submodule_prefix.buf; + argv_array_push(&argv, default_argv); + argv_array_push(&argv, "--submodule-prefix"); + argv_array_push(&argv, submodule_prefix.buf); + cp.argv = argv.argv; if (run_command(&cp)) result = 1; + argv_array_pop(&argv); + argv_array_pop(&argv); + argv_array_pop(&argv); } strbuf_release(&submodule_path); strbuf_release(&submodule_git_dir); strbuf_release(&submodule_prefix); } - free(argv); + argv_array_clear(&argv); out: string_list_clear(&changed_submodule_paths, 1); return result; @@ -788,7 +789,7 @@ static int find_first_merges(struct object_array *result, const char *path, die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { struct object *o = &(commit->object); - if (in_merge_bases(b, &commit, 1)) + if (in_merge_bases(b, commit)) add_object_array(o, NULL, &merges); } reset_revision_walk(); @@ -803,7 +804,7 @@ static int find_first_merges(struct object_array *result, const char *path, contains_another = 0; for (j = 0; j < merges.nr; j++) { struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && in_merge_bases(m2, &m1, 1)) { + if (i != j && in_merge_bases(m2, m1)) { contains_another = 1; break; } @@ -865,18 +866,18 @@ int merge_submodule(unsigned char result[20], const char *path, } /* check whether both changes are forward */ - if (!in_merge_bases(commit_base, &commit_a, 1) || - !in_merge_bases(commit_base, &commit_b, 1)) { + if (!in_merge_bases(commit_base, commit_a) || + !in_merge_bases(commit_base, commit_b)) { MERGE_WARNING(path, "commits don't follow merge-base"); return 0; } /* Case #1: a is contained in b or vice versa */ - if (in_merge_bases(commit_a, &commit_b, 1)) { + if (in_merge_bases(commit_a, commit_b)) { hashcpy(result, b); return 1; } - if (in_merge_bases(commit_b, &commit_a, 1)) { + if (in_merge_bases(commit_b, commit_a)) { hashcpy(result, a); return 1; } diff --git a/submodule.h b/submodule.h index e105b0ebe6..594b50d510 100644 --- a/submodule.h +++ b/submodule.h @@ -2,6 +2,7 @@ #define SUBMODULE_H struct diff_options; +struct argv_array; enum { RECURSE_SUBMODULES_ON_DEMAND = -1, @@ -23,7 +24,7 @@ void show_submodule_summary(FILE *f, const char *path, const char *del, const char *add, const char *reset); void set_config_fetch_recurse_submodules(int value); void check_for_new_submodule_commits(unsigned char new_sha1[20]); -int fetch_populated_submodules(int num_options, const char **options, +int fetch_populated_submodules(const struct argv_array *options, const char *prefix, int command_line_option, int quiet); unsigned is_submodule_modified(const char *path, int ignore_untracked); diff --git a/t/Git-SVN/Utils/add_path_to_url.t b/t/Git-SVN/Utils/add_path_to_url.t new file mode 100644 index 0000000000..bfbd87845f --- /dev/null +++ b/t/Git-SVN/Utils/add_path_to_url.t @@ -0,0 +1,27 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils qw( + add_path_to_url +); + +# A reference cannot be a hash key, so we use an array. +my @tests = ( + ["http://x.com", "bar"] => 'http://x.com/bar', + ["http://x.com", ""] => 'http://x.com', + ["http://x.com/foo/", undef] => 'http://x.com/foo/', + ["http://x.com/foo/", "/bar/baz/"] => 'http://x.com/foo/bar/baz/', + ["http://x.com", 'per%cent'] => 'http://x.com/per%25cent', +); + +while(@tests) { + my($have, $want) = splice @tests, 0, 2; + + my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have; + my $name = "add_path_to_url($args) eq $want"; + is add_path_to_url(@$have), $want, $name; +} diff --git a/t/Git-SVN/Utils/canonicalize_url.t b/t/Git-SVN/Utils/canonicalize_url.t new file mode 100644 index 0000000000..05795ab636 --- /dev/null +++ b/t/Git-SVN/Utils/canonicalize_url.t @@ -0,0 +1,26 @@ +#!/usr/bin/env perl + +# Test our own home rolled URL canonicalizer. Test the private one +# directly because we can't predict what the SVN API is doing to do. + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils; +my $canonicalize_url = \&Git::SVN::Utils::_canonicalize_url_ourselves; + +my %tests = ( + "http://x.com" => "http://x.com", + "http://x.com/" => "http://x.com", + "http://x.com/foo/bar" => "http://x.com/foo/bar", + "http://x.com//foo//bar//" => "http://x.com/foo/bar", + "http://x.com/ /%/" => "http://x.com/%20%20/%25", +); + +for my $arg (keys %tests) { + my $want = $tests{$arg}; + + is $canonicalize_url->($arg), $want, "canonicalize_url('$arg') => $want"; +} diff --git a/t/Git-SVN/Utils/collapse_dotdot.t b/t/Git-SVN/Utils/collapse_dotdot.t new file mode 100644 index 0000000000..1da1cce156 --- /dev/null +++ b/t/Git-SVN/Utils/collapse_dotdot.t @@ -0,0 +1,23 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils; +my $collapse_dotdot = \&Git::SVN::Utils::_collapse_dotdot; + +my %tests = ( + "foo/bar/baz" => "foo/bar/baz", + ".." => "..", + "foo/.." => "", + "/foo/bar/../../baz" => "/baz", + "deeply/.././deeply/nested" => "./deeply/nested", +); + +for my $arg (keys %tests) { + my $want = $tests{$arg}; + + is $collapse_dotdot->($arg), $want, "_collapse_dotdot('$arg') => $want"; +} diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t new file mode 100644 index 0000000000..d4488e7162 --- /dev/null +++ b/t/Git-SVN/Utils/join_paths.t @@ -0,0 +1,32 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils qw( + join_paths +); + +# A reference cannot be a hash key, so we use an array. +my @tests = ( + [] => '', + ["/x.com", "bar"] => '/x.com/bar', + ["x.com", ""] => 'x.com', + ["/x.com/foo/", undef, "bar"] => '/x.com/foo/bar', + ["x.com/foo/", "/bar/baz/"] => 'x.com/foo/bar/baz/', + ["foo", "bar"] => 'foo/bar', + ["/foo/bar", "baz", "/biff"] => '/foo/bar/baz/biff', + ["", undef, "."] => '.', + [] => '', + +); + +while(@tests) { + my($have, $want) = splice @tests, 0, 2; + + my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have; + my $name = "join_paths($args) eq '$want'"; + is join_paths(@$have), $want, $name; +} diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 957ae936e8..3c43ff11b3 100755 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -18,6 +18,10 @@ check() { cat stderr && false fi && + if test_have_prereq MINGW + then + dos2unix -q stderr + fi && test_cmp expect-stdout stdout && test_cmp expect-stderr stderr } diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh index 2d753ab7e1..d748c36df5 100644 --- a/t/lib-git-p4.sh +++ b/t/lib-git-p4.sh @@ -115,3 +115,20 @@ marshal_dump() { EOF "$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py" } + +# +# Construct a client with this list of View lines +# +client_view() { + ( + cat <<-EOF && + Client: client + Description: client + Root: $cli + View: + EOF + for arg ; do + printf "\t$arg\n" + done + ) | p4 client -i +} diff --git a/t/t0006-date.sh b/t/t0006-date.sh index 1d29810a7a..e53cf6d36d 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -11,7 +11,7 @@ check_show() { echo "$t -> $2" >expect test_expect_${3:-success} "relative date ($2)" " test-date show $t >actual && - test_cmp expect actual + test_i18ncmp expect actual " } diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index e3f354a45e..244a43c920 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -51,7 +51,7 @@ EOF test_expect_success 'test help' ' test_must_fail test-parse-options -h > output 2> output.err && test ! -s output.err && - test_cmp expect output + test_i18ncmp expect output ' mv expect expect.err @@ -79,6 +79,17 @@ check() { test_cmp expect output } +check_i18n() { + what="$1" && + shift && + expect="$1" && + shift && + sed "s/^$what .*/$what $expect/" <expect.template >expect && + test-parse-options $* >output 2>output.err && + test ! -s output.err && + test_i18ncmp expect output +} + check_unknown() { case "$1" in --*) @@ -92,6 +103,19 @@ check_unknown() { test_cmp expect output.err } +check_unknown_i18n() { + case "$1" in + --*) + echo error: unknown option \`${1#--}\' >expect ;; + -*) + echo error: unknown switch \`${1#-}\' >expect ;; + esac && + cat expect.err >>expect && + test_must_fail test-parse-options $* >output 2>output.err && + test ! -s output && + test_i18ncmp expect output.err +} + test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes' test_expect_success 'OPT_BOOL() #2' 'check boolean: 1 --no-doubt' test_expect_success 'OPT_BOOL() #3' 'check boolean: 1 -D' @@ -104,8 +128,8 @@ test_expect_success 'OPT_BOOL() is idempotent #2' 'check boolean: 1 -DB' test_expect_success 'OPT_BOOL() negation #1' 'check boolean: 0 -D --no-yes' test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt' -test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown --fear' -test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown --no-no-fear' +test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown_i18n --fear' +test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown_i18n --no-no-fear' test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt' @@ -310,8 +334,8 @@ EOF test_expect_success 'OPT_CALLBACK() and callback errors work' ' test_must_fail test-parse-options --no-length > output 2> output.err && - test_cmp expect output && - test_cmp expect.err output.err + test_i18ncmp expect output && + test_i18ncmp expect.err output.err ' cat > expect <<EOF diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh index 9bee8bfd2e..da2c504e53 100755 --- a/t/t0070-fundamental.sh +++ b/t/t0070-fundamental.sh @@ -25,4 +25,9 @@ test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' ' grep "cannotwrite/test" err ' +test_expect_success 'check for a bug in the regex routines' ' + # if this test fails, re-build git with NO_REGEX=1 + test-regex +' + test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index a477453e2e..e127f35db9 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -391,7 +391,7 @@ test_expect_success 'get bool variable with empty value' \ test_expect_success 'no arguments, but no crash' ' test_must_fail git config >output 2>&1 && - grep usage output + test_i18ngrep usage output ' cat > .git/config << EOF diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index 1efd7f76dd..13c88c9aae 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -41,7 +41,7 @@ EOF test_expect_success 'test --parseopt help output' ' test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec && - test_cmp expect output + test_i18ncmp expect output ' cat > expect <<EOF diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh index b8559838b1..57cbdfe9bc 100755 --- a/t/t2006-checkout-index-basic.sh +++ b/t/t2006-checkout-index-basic.sh @@ -7,7 +7,7 @@ test_description='basic checkout-index tests test_expect_success 'checkout-index --gobbledegook' ' test_expect_code 129 git checkout-index --gobbledegook 2>err && - grep "[Uu]sage" err + test_i18ngrep "[Uu]sage" err ' test_expect_success 'checkout-index -h in broken repository' ' @@ -18,7 +18,7 @@ test_expect_success 'checkout-index -h in broken repository' ' >.git/index && test_expect_code 129 git checkout-index -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_done diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 0dbbb00d74..a6405d318d 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -15,7 +15,7 @@ test_expect_success 'update-index --nonsense fails' ' test_expect_success 'update-index --nonsense dumps usage' ' test_expect_code 129 git update-index --nonsense 2>err && - grep "[Uu]sage: git update-index" err + test_i18ngrep "[Uu]sage: git update-index" err ' test_expect_success 'update-index -h with corrupt index' ' @@ -26,7 +26,7 @@ test_expect_success 'update-index -h with corrupt index' ' >.git/index && test_expect_code 129 git update-index -h >usage 2>&1 ) && - grep "[Uu]sage: git update-index" broken/usage + test_i18ngrep "[Uu]sage: git update-index" broken/usage ' test_expect_success '--cacheinfo does not accept blob null sha1' ' diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh index 490e052875..8d9bc3c2af 100755 --- a/t/t3004-ls-files-basic.sh +++ b/t/t3004-ls-files-basic.sh @@ -22,7 +22,7 @@ test_expect_success 'ls-files with nonexistent path' ' test_expect_success 'ls-files with nonsense option' ' test_expect_code 129 git ls-files --nonsense 2>actual && - grep "[Uu]sage: git ls-files" actual + test_i18ngrep "[Uu]sage: git ls-files" actual ' test_expect_success 'ls-files -h in corrupt repository' ' @@ -33,7 +33,7 @@ test_expect_success 'ls-files -h in corrupt repository' ' >.git/index && test_expect_code 129 git ls-files -h >usage 2>&1 ) && - grep "[Uu]sage: git ls-files " broken/usage + test_i18ngrep "[Uu]sage: git ls-files " broken/usage ' test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index a17f8b2a40..79c8d0142e 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -31,7 +31,7 @@ test_expect_success 'branch -h in broken repository' ' >.git/refs/heads/master && test_expect_code 129 git branch -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_expect_success \ @@ -74,7 +74,7 @@ test_expect_success \ test_expect_success \ 'git branch -m dumps usage' \ 'test_expect_code 129 git branch -m 2>err && - grep "[Uu]sage: git branch" err' + test_i18ngrep "[Uu]sage: git branch" err' test_expect_success \ 'git branch -m m m/m should work' \ @@ -369,6 +369,76 @@ test_expect_success \ 'git tag foobar && test_must_fail git branch --track my11 foobar' +test_expect_success 'use --set-upstream-to modify HEAD' \ + 'test_config branch.master.remote foo && + test_config branch.master.merge foo && + git branch my12 + git branch --set-upstream-to my12 && + test "$(git config branch.master.remote)" = "." && + test "$(git config branch.master.merge)" = "refs/heads/my12"' + +test_expect_success 'use --set-upstream-to modify a particular branch' \ + 'git branch my13 + git branch --set-upstream-to master my13 && + test "$(git config branch.my13.remote)" = "." && + test "$(git config branch.my13.merge)" = "refs/heads/master"' + +test_expect_success '--unset-upstream should fail if given a non-existent branch' \ + 'test_must_fail git branch --unset-upstream i-dont-exist' + +test_expect_success 'test --unset-upstream on HEAD' \ + 'git branch my14 + test_config branch.master.remote foo && + test_config branch.master.merge foo && + git branch --set-upstream-to my14 && + git branch --unset-upstream && + test_must_fail git config branch.master.remote && + test_must_fail git config branch.master.merge && + # fail for a branch without upstream set + test_must_fail git branch --unset-upstream +' + +test_expect_success 'test --unset-upstream on a particular branch' \ + 'git branch my15 + git branch --set-upstream-to master my14 && + git branch --unset-upstream my14 && + test_must_fail git config branch.my14.remote && + test_must_fail git config branch.my14.merge' + +test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' \ + 'git update-ref refs/remotes/origin/master HEAD && + git branch --set-upstream origin/master 2>actual && + test_when_finished git update-ref -d refs/remotes/origin/master && + test_when_finished git branch -d origin/master && + cat >expected <<EOF && +The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to + +If you wanted to make '"'master'"' track '"'origin/master'"', do this: + + git branch -d origin/master + git branch --set-upstream-to origin/master +EOF + test_cmp expected actual +' + +test_expect_success '--set-upstream with two args only shows the deprecation message' \ + 'git branch --set-upstream master my13 2>actual && + test_when_finished git branch --unset-upstream master && + cat >expected <<EOF && +The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to +EOF + test_cmp expected actual +' + +test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' \ + 'git branch --set-upstream my13 2>actual && + test_when_finished git branch --unset-upstream my13 && + cat >expected <<EOF && +The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to +EOF + test_cmp expected actual +' + # Keep this test last, as it changes the current branch cat >expect <<EOF $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 595d2ff990..34c86e5de6 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -47,7 +47,7 @@ test_expect_success 'cherry-pick --nonsense' ' git diff --exit-code HEAD && test_must_fail git cherry-pick --nonsense 2>msg && git diff --exit-code HEAD "$pos" && - grep '[Uu]sage:' msg + test_i18ngrep '[Uu]sage:' msg ' test_expect_success 'revert --nonsense' ' @@ -56,7 +56,7 @@ test_expect_success 'revert --nonsense' ' git diff --exit-code HEAD && test_must_fail git revert --nonsense 2>msg && git diff --exit-code HEAD "$pos" && - grep '[Uu]sage:' msg + test_i18ngrep '[Uu]sage:' msg ' test_expect_success 'cherry-pick after renaming branch' ' diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh index 5a1340cee6..a0c6e30d80 100755 --- a/t/t3505-cherry-pick-empty.sh +++ b/t/t3505-cherry-pick-empty.sh @@ -53,6 +53,11 @@ test_expect_success 'index lockfile was removed' ' ' +test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' ' + git checkout -f master && + git cherry-pick --allow-empty-message empty-branch +' + test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' ' git checkout master && echo fourth >>file2 && diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh index 75f7ff4f2f..340afc760d 100755 --- a/t/t3508-cherry-pick-many-commits.sh +++ b/t/t3508-cherry-pick-many-commits.sh @@ -44,6 +44,21 @@ test_expect_success 'cherry-pick first..fourth works' ' check_head_differs_from fourth ' +test_expect_success 'cherry-pick three one two works' ' + git checkout -f first && + test_commit one && + test_commit two && + test_commit three && + git checkout -f master && + git reset --hard first && + git cherry-pick three one two && + git diff --quiet three && + git diff --quiet HEAD three && + test "$(git log --reverse --format=%s first..)" = "three +one +two" +' + test_expect_success 'output to keep user entertained during multi-pick' ' cat <<-\EOF >expected && [master OBJID] second diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index cd042633ba..5dfbda7491 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -610,7 +610,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu git stash apply ) | sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..." - test_cmp expect actual + test_i18ncmp expect actual ' cat > expect << EOF diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index 7a3e1f9a24..3d4b1ba23f 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -36,24 +36,24 @@ test_expect_success '--stat output after text chmod' ' test_chmod -x rezrov && echo " 0 files changed" >expect && git diff HEAD --stat >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_expect_success '--shortstat output after text chmod' ' git diff HEAD --shortstat >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_expect_success '--stat output after binary chmod' ' test_chmod +x binbin && echo " 0 files changed" >expect && git diff HEAD --stat >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_expect_success '--shortstat output after binary chmod' ' git diff HEAD --shortstat >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_done diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index ec4deea192..1215ae544b 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -63,7 +63,7 @@ test_expect_success 'apply --numstat understands diff --binary format' ' # apply needs to be able to skip the binary material correctly # in order to report the line number of a corrupt patch. -test_expect_success 'apply detecting corrupt patch correctly' ' +test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' git diff >output && sed -e "s/-CIT/xCIT/" <output >broken && test_must_fail git apply --stat --summary broken 2>detected && @@ -73,7 +73,7 @@ test_expect_success 'apply detecting corrupt patch correctly' ' test "$detected" = xCIT ' -test_expect_success 'apply detecting corrupt patch correctly' ' +test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' git diff --binary | sed -e "s/-CIT/xCIT/" >broken && test_must_fail git apply --stat --summary broken 2>detected && detected=`cat detected` && diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index a33d510bf6..c5fecdfed4 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -32,7 +32,7 @@ test_expect_success 'apply git diff with -p2' ' test_expect_success 'apply with too large -p' ' cp file1.saved file1 && test_must_fail git apply --stat -p3 patch.file 2>err && - grep "removing 3 leading" err + test_i18ngrep "removing 3 leading" err ' test_expect_success 'apply (-p2) traditional diff with funny filenames' ' @@ -54,7 +54,7 @@ test_expect_success 'apply (-p2) traditional diff with funny filenames' ' test_expect_success 'apply with too large -p and fancy filename' ' cp file1.saved file1 && test_must_fail git apply --stat -p3 patch.escaped 2>err && - grep "removing 3 leading" err + test_i18ngrep "removing 3 leading" err ' test_expect_success 'apply (-p2) diff, mode change only' ' diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh index 94da99075c..2ecb4216b7 100755 --- a/t/t4133-apply-filenames.sh +++ b/t/t4133-apply-filenames.sh @@ -30,9 +30,9 @@ EOF test_expect_success 'apply diff with inconsistent filenames in headers' ' test_must_fail git apply bad1.patch 2>err && - grep "inconsistent new filename" err && + test_i18ngrep "inconsistent new filename" err && test_must_fail git apply bad2.patch 2>err && - grep "inconsistent old filename" err + test_i18ngrep "inconsistent old filename" err ' test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 3ab670d36a..7f6666fcd3 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -382,13 +382,13 @@ test_expect_success 'rerere --no-no-rerere-autoupdate' ' git update-index --index-info <failedmerge && cp file3.conflict file3 && test_must_fail git rerere --no-no-rerere-autoupdate 2>err && - grep [Uu]sage err && + test_i18ngrep [Uu]sage err && test_must_fail git update-index --refresh ' test_expect_success 'rerere -h' ' test_must_fail git rerere -h >help && - grep [Uu]sage help + test_i18ngrep [Uu]sage help ' test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 45058cc8cb..924ba536ca 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -178,11 +178,21 @@ test_expect_success 'git log --no-walk <commits> sorts by commit time' ' test_cmp expect actual ' +test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' ' + git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual && + test_cmp expect actual +' + cat > expect << EOF 5d31159 fourth 804a787 sixth 394ef78 fifth EOF +test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' ' + git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual && + test_cmp expect actual +' + test_expect_success 'git show <commits> leaves list of commits as given' ' git show --oneline -s 5d31159 804a787 394ef78 > actual && test_cmp expect actual @@ -803,7 +813,7 @@ sanitize_output () { test_expect_success 'log --graph with diff and stats' ' git log --graph --pretty=short --stat -p >actual && sanitize_output >actual.sanitized <actual && - test_cmp expect actual.sanitized + test_i18ncmp expect actual.sanitized ' test_expect_success 'dotdot is a parent directory' ' diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 4afd77815f..2c45de7aea 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -88,7 +88,7 @@ test_expect_success 'NUL separation with --stat' ' stat1_part=$(git diff --stat --root HEAD^) && printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n" >expected && git log -z --stat --pretty="format:%s" >actual && - test_cmp expected actual + test_i18ncmp expected actual ' test_expect_failure 'NUL termination with --stat' ' @@ -96,7 +96,7 @@ test_expect_failure 'NUL termination with --stat' ' stat1_part=$(git diff --stat --root HEAD^) && printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n\0" >expected && git log -z --stat --pretty="tformat:%s" >actual && - test_cmp expected actual + test_i18ncmp expected actual ' test_done diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 2e52f8b838..a07c871797 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -416,11 +416,11 @@ test_expect_success \ test_expect_success \ 'make sure index-pack detects the SHA1 collision' \ 'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg && - grep "SHA1 COLLISION FOUND" msg' + test_i18ngrep "SHA1 COLLISION FOUND" msg' test_expect_success \ 'make sure index-pack detects the SHA1 collision (large blobs)' \ 'test_must_fail git -c core.bigfilethreshold=1 index-pack -o bad.idx test-3.pack 2>msg && - grep "SHA1 COLLISION FOUND" msg' + test_i18ngrep "SHA1 COLLISION FOUND" msg' test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index e8af615e6d..ccc55ebf4b 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -52,7 +52,7 @@ test_expect_success setup ' ' -test_expect_success 'remote information for the origin' ' +test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' ' ( cd test && tokens_match origin "$(git remote)" && @@ -66,8 +66,6 @@ test_expect_success 'add another remote' ' cd test && git remote add -f second ../two && tokens_match "origin second" "$(git remote)" && - check_remote_track origin master side && - check_remote_track second master side another && check_tracking_branch second master side another && git for-each-ref "--format=%(refname)" refs/remotes | sed -e "/^refs\/remotes\/origin\//d" \ @@ -77,6 +75,14 @@ test_expect_success 'add another remote' ' ) ' +test_expect_success C_LOCALE_OUTPUT 'check remote tracking' ' +( + cd test && + check_remote_track origin master side && + check_remote_track second master side another +) +' + test_expect_success 'remote forces tracking branches' ' ( cd test && @@ -95,7 +101,7 @@ test_expect_success 'remove remote' ' ) ' -test_expect_success 'remove remote' ' +test_expect_success C_LOCALE_OUTPUT 'remove remote' ' ( cd test && tokens_match origin "$(git remote)" && @@ -125,14 +131,14 @@ EOF } && git tag footag && git config --add remote.oops.fetch "+refs/*:refs/*" && - git remote rm oops 2>actual1 && + git remote remove oops 2>actual1 && git branch foobranch && git config --add remote.oops.fetch "+refs/*:refs/*" && git remote rm oops 2>actual2 && git branch -d foobranch && git tag -d footag && - test_cmp expect1 actual1 && - test_cmp expect2 actual2 + test_i18ncmp expect1 actual1 && + test_i18ncmp expect2 actual2 ) ' @@ -192,7 +198,7 @@ test_expect_success 'show' ' git config --add remote.two.push refs/heads/master:refs/heads/another && git remote show origin two > output && git branch -d rebase octopus && - test_cmp expect output) + test_i18ncmp expect output) ' cat > test/expect << EOF @@ -217,7 +223,7 @@ test_expect_success 'show -n' ' cd test && git remote show -n origin > output && mv ../one.unreachable ../one && - test_cmp expect output) + test_i18ncmp expect output) ' test_expect_success 'prune' ' @@ -255,7 +261,7 @@ EOF test_expect_success 'set-head --auto fails w/multiple HEADs' ' (cd test && test_must_fail git remote set-head --auto two >output 2>&1 && - test_cmp expect output) + test_i18ncmp expect output) ' cat >test/expect <<EOF @@ -285,7 +291,7 @@ test_expect_success 'prune --dry-run' ' test_must_fail git rev-parse refs/remotes/origin/side && (cd ../one && git branch -m side side2) && - test_cmp expect output) + test_i18ncmp expect output) ' test_expect_success 'add --mirror && prune' ' @@ -672,7 +678,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' ' git clone one five && origin_url=$(pwd)/one && (cd five && - git remote rm origin && + git remote remove origin && mkdir -p .git/remotes && cat ../remotes_origin > .git/remotes/origin && git remote rename origin origin && @@ -705,7 +711,7 @@ test_expect_success 'remote prune to cause a dangling symref' ' cd seven && git remote prune origin ) >err 2>&1 && - grep "has become dangling" err && + test_i18ngrep "has become dangling" err && : And the dangling symref will not cause other annoying errors && ( diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index 6b2a5f4a65..c983d3694c 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh @@ -35,8 +35,8 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' ' printf "0032want %s\n00000009done\n0000" \ $(git rev-parse HEAD) >input && test_must_fail git upload-pack . <input >/dev/null 2>output.err && - grep "unable to read" output.err && - grep "pack-objects died" output.err + test_i18ngrep "unable to read" output.err && + test_i18ngrep "pack-objects died" output.err ' test_expect_success 'corrupt repo differently' ' diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index f141f2d1da..01d0d95b4d 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -109,7 +109,7 @@ test_expect_success 'http-push fetches packed objects' ' # By reset, we force git to retrieve the packed object (cd "$ROOT_PATH"/test_repo_clone_packed && git reset --hard HEAD^ && - git remote rm origin && + git remote remove origin && git reflog expire --expire=0 --all && git prune && git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master) diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index ef6d6b6e4e..4b4b4a604f 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -66,7 +66,10 @@ test_expect_success 'no empty path components' ' test_expect_success 'clone remote repository' ' rm -rf test_repo_clone && - git clone $HTTPD_URL/smart/test_repo.git test_repo_clone + git clone $HTTPD_URL/smart/test_repo.git test_repo_clone && + ( + cd test_repo_clone && git config push.default matching + ) ' test_expect_success 'push to remote repository (standard)' ' diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 82f3639937..b1a63655f9 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -11,7 +11,7 @@ test_expect_success 'gc empty repository' ' test_expect_success 'gc --gobbledegook' ' test_expect_code 129 git gc --nonsense 2>err && - grep "[Uu]sage: git gc" err + test_i18ngrep "[Uu]sage: git gc" err ' test_expect_success 'gc -h with invalid configuration' ' @@ -22,7 +22,7 @@ test_expect_success 'gc -h with invalid configuration' ' echo "[gc] pruneexpire = CORRUPT" >>.git/config && test_expect_code 129 git gc -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_done diff --git a/t/t7007-show.sh b/t/t7007-show.sh index a40cd3630c..e41fa00b80 100755 --- a/t/t7007-show.sh +++ b/t/t7007-show.sh @@ -108,4 +108,16 @@ test_expect_success 'showing range' ' test_cmp expect actual.filtered ' +test_expect_success '-s suppresses diff' ' + echo main3 >expect && + git show -s --format=%s main3 >actual && + test_cmp expect actual +' + +test_expect_success '--quiet suppresses diff' ' + echo main3 >expect && + git show --quiet --format=%s main3 >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7508-status.sh b/t/t7508-status.sh index c206f4777a..e313ef196e 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -80,7 +80,7 @@ test_expect_success 'status --column' ' # dir1/untracked dir2/untracked untracked # dir2/modified output EOF - test_cmp expect output + test_i18ncmp expect output ' cat >expect <<\EOF diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 9e27bbf902..5e19598fe7 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -157,7 +157,7 @@ test_expect_success 'merge -h with invalid index' ' >.git/index && test_expect_code 129 git merge -h 2>usage ) && - grep "[Uu]sage: git merge" broken/usage + test_i18ngrep "[Uu]sage: git merge" broken/usage ' test_expect_success 'reject non-strategy with a git-merge-foo name' ' diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 523d04123d..35d357d4c8 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -250,6 +250,84 @@ do git -c grep.extendedRegexp=true grep "a+b*c" ab >actual && test_cmp expected actual ' + + test_expect_success "grep $L with grep.patterntype=basic" ' + echo "ab:a+bc" >expected && + git -c grep.patterntype=basic grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.patterntype=extended" ' + echo "ab:abc" >expected && + git -c grep.patterntype=extended grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.patterntype=fixed" ' + echo "ab:a+b*c" >expected && + git -c grep.patterntype=fixed grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" ' + echo "ab:a+b*c" >expected && + git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" ' + echo "ab:abc" >expected && + git \ + -c grep.patternType=default \ + -c grep.extendedRegexp=true \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" ' + echo "ab:abc" >expected && + git \ + -c grep.extendedRegexp=true \ + -c grep.patternType=default \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.patternType=extended and grep.extendedRegexp=false' ' + echo "ab:abc" >expected && + git \ + -c grep.patternType=extended \ + -c grep.extendedRegexp=false \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.patternType=basic and grep.extendedRegexp=true' ' + echo "ab:a+bc" >expected && + git \ + -c grep.patternType=basic \ + -c grep.extendedRegexp=true \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.extendedRegexp=false and grep.patternType=extended' ' + echo "ab:abc" >expected && + git \ + -c grep.extendedRegexp=false \ + -c grep.patternType=extended \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.extendedRegexp=true and grep.patternType=basic' ' + echo "ab:a+bc" >expected && + git \ + -c grep.extendedRegexp=true \ + -c grep.patternType=basic \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' done cat >expected <<EOF @@ -761,44 +839,147 @@ test_expect_success 'grep -G invalidpattern properly dies ' ' test_must_fail git grep -G "a[" ' +test_expect_success 'grep invalidpattern properly dies with grep.patternType=basic' ' + test_must_fail git -c grep.patterntype=basic grep "a[" +' + test_expect_success 'grep -E invalidpattern properly dies ' ' test_must_fail git grep -E "a[" ' +test_expect_success 'grep invalidpattern properly dies with grep.patternType=extended' ' + test_must_fail git -c grep.patterntype=extended grep "a[" +' + test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' ' test_must_fail git grep -P "a[" ' +test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' ' + test_must_fail git -c grep.patterntype=perl grep "a[" +' + test_expect_success 'grep -G -E -F pattern' ' echo "ab:a+b*c" >expected && git grep -G -E -F "a+b*c" ab >actual && test_cmp expected actual ' +test_expect_success 'grep pattern with grep.patternType=basic, =extended, =fixed' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=basic \ + -c grep.patterntype=extended \ + -c grep.patterntype=fixed \ + grep "a+b*c" ab >actual && + test_cmp expected actual +' + test_expect_success 'grep -E -F -G pattern' ' echo "ab:a+bc" >expected && git grep -E -F -G "a+b*c" ab >actual && test_cmp expected actual ' +test_expect_success 'grep pattern with grep.patternType=extended, =fixed, =basic' ' + echo "ab:a+bc" >expected && + git \ + -c grep.patterntype=extended \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + grep "a+b*c" ab >actual && + test_cmp expected actual +' + test_expect_success 'grep -F -G -E pattern' ' echo "ab:abc" >expected && git grep -F -G -E "a+b*c" ab >actual && test_cmp expected actual ' +test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =extended' ' + echo "ab:abc" >expected && + git \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + -c grep.patterntype=extended \ + grep "a+b*c" ab >actual && + test_cmp expected actual +' + test_expect_success 'grep -G -F -P -E pattern' ' >empty && test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual && test_cmp empty actual ' +test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =extended' ' + >empty && + test_must_fail git \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + -c grep.patterntype=perl \ + -c grep.patterntype=extended \ + grep "a\x{2b}b\x{2a}c" ab >actual && + test_cmp empty actual +' + test_expect_success LIBPCRE 'grep -G -F -E -P pattern' ' echo "ab:a+b*c" >expected && git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual && test_cmp expected actual ' +test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + -c grep.patterntype=extended \ + -c grep.patterntype=perl \ + grep "a\x{2b}b\x{2a}c" ab >actual && + test_cmp expected actual +' + +test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=fixed \ + grep -P "a\x{2b}b\x{2a}c" ab >actual && + test_cmp expected actual +' + +test_expect_success 'grep -F pattern with grep.patternType=basic' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=basic \ + grep -F "*c" ab >actual && + test_cmp expected actual +' + +test_expect_success 'grep -G pattern with grep.patternType=fixed' ' + { + echo "ab:a+b*c" + echo "ab:a+bc" + } >expected && + git \ + -c grep.patterntype=fixed \ + grep -G "a+b" ab >actual && + test_cmp expected actual +' + +test_expect_success 'grep -E pattern with grep.patternType=fixed' ' + { + echo "ab:a+b*c" + echo "ab:a+bc" + echo "ab:abc" + } >expected && + git \ + -c grep.patterntype=fixed \ + grep -E "a+" ab >actual && + test_cmp expected actual +' + test_config() { git config "$1" "$2" && test_when_finished "git config --unset $1" diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index 289fc313fb..ee73013eed 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -27,15 +27,17 @@ test_expect_success 'setup old-looking metadata' ' head=`git rev-parse --verify refs/heads/git-svn-HEAD^0` test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'" +svnrepo_escaped=`echo $svnrepo | sed 's/ /%20/'` + test_expect_success 'initialize old-style (v0) git svn layout' ' mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info && echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url && echo "$svnrepo" > "$GIT_DIR"/svn/info/url && git svn migrate && - ! test -d "$GIT_DIR"/git svn && + ! test -d "$GIT_DIR"/git-svn && git rev-parse --verify refs/${remotes_git_svn}^0 && git rev-parse --verify refs/remotes/svn^0 && - test "$(git config --get svn-remote.svn.url)" = "$svnrepo" && + test "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" && test `git config --get svn-remote.svn.fetch` = \ ":refs/${remotes_git_svn}" ' diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh index 63fc982c8c..193d3cabdd 100755 --- a/t/t9118-git-svn-funky-branch-names.sh +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -32,6 +32,11 @@ test_expect_success 'setup svnrepo' ' start_httpd ' +# SVN 1.7 will truncate "not-a%40{0]" to just "not-a". +# Look at what SVN wound up naming the branch and use that. +# Be sure to escape the @ if it shows up. +non_reflog=`svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/'` + test_expect_success 'test clone with funky branch names' ' git svn clone -s "$svnrepo/pr ject" project && ( @@ -42,7 +47,7 @@ test_expect_success 'test clone with funky branch names' ' git rev-parse "refs/remotes/%2Eleading_dot" && git rev-parse "refs/remotes/trailing_dot%2E" && git rev-parse "refs/remotes/trailing_dotlock%2Elock" && - git rev-parse "refs/remotes/not-a%40{0}reflog" + git rev-parse "refs/remotes/$non_reflog" ) ' diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index 99fe16b72d..9730821c30 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -410,6 +410,83 @@ test_expect_failure 'git p4 clone file subset branch' ' test_path_is_missing file3 ) ' + +# From a report in http://stackoverflow.com/questions/11893688 +# where --use-client-spec caused branch prefixes not to be removed; +# every file in git appeared into a subdirectory of the branch name. +test_expect_success 'use-client-spec detect-branches setup' ' + rm -rf "$cli" && + mkdir "$cli" && + ( + cd "$cli" && + client_view "//depot/usecs/... //client/..." && + mkdir b1 && + echo b1/b1-file1 >b1/b1-file1 && + p4 add b1/b1-file1 && + p4 submit -d "b1/b1-file1" && + + p4 integrate //depot/usecs/b1/... //depot/usecs/b2/... && + p4 submit -d "b1 -> b2" && + p4 branch -i <<-EOF && + Branch: b2 + View: //depot/usecs/b1/... //depot/usecs/b2/... + EOF + + echo b2/b2-file2 >b2/b2-file2 && + p4 add b2/b2-file2 && + p4 submit -d "b2/b2-file2" + ) +' + +test_expect_success 'use-client-spec detect-branches files in top-level' ' + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git p4 sync --detect-branches --use-client-spec //depot/usecs@all && + git checkout -b master p4/usecs/b1 && + test_path_is_file b1-file1 && + test_path_is_missing b2-file2 && + test_path_is_missing b1 && + test_path_is_missing b2 && + + git checkout -b b2 p4/usecs/b2 && + test_path_is_file b1-file1 && + test_path_is_file b2-file2 && + test_path_is_missing b1 && + test_path_is_missing b2 + ) +' + +test_expect_success 'use-client-spec detect-branches skips branches setup' ' + ( + cd "$cli" && + + p4 integrate //depot/usecs/b1/... //depot/usecs/b3/... && + p4 submit -d "b1 -> b3" && + p4 branch -i <<-EOF && + Branch: b3 + View: //depot/usecs/b1/... //depot/usecs/b3/... + EOF + + echo b3/b3-file3 >b3/b3-file3 && + p4 add b3/b3-file3 && + p4 submit -d "b3/b3-file3" + ) +' + +test_expect_success 'use-client-spec detect-branches skips branches' ' + client_view "//depot/usecs/... //client/..." \ + "-//depot/usecs/b3/... //client/b3/..." && + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git p4 sync --detect-branches --use-client-spec //depot/usecs@all && + test_must_fail git rev-parse refs/remotes/p4/usecs/b3 + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh index 7d993ef80a..281be29174 100755 --- a/t/t9809-git-p4-client-view.sh +++ b/t/t9809-git-p4-client-view.sh @@ -9,23 +9,6 @@ test_expect_success 'start p4d' ' ' # -# Construct a client with this list of View lines -# -client_view() { - ( - cat <<-EOF && - Client: client - Description: client - Root: $cli - View: - EOF - for arg ; do - printf "\t$arg\n" - done - ) | p4 client -i -} - -# # Verify these files exist, exactly. Caller creates # a list of files in file "files". # diff --git a/test-regex.c b/test-regex.c new file mode 100644 index 0000000000..b5bfd54139 --- /dev/null +++ b/test-regex.c @@ -0,0 +1,20 @@ +#include <git-compat-util.h> + +int main(int argc, char **argv) +{ + char *pat = "[^={} \t]+"; + char *str = "={}\nfred"; + regex_t r; + regmatch_t m[1]; + + if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE)) + die("failed regcomp() for pattern '%s'", pat); + if (regexec(&r, str, 1, m, 0)) + die("no match of pattern '%s' to string '%s'", pat, str); + + /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957 */ + if (m[0].rm_so == 3) /* matches '\n' when it should not */ + die("regex bug confirmed: re-build git with NO_REGEX=1"); + + exit(0); +} @@ -403,6 +403,19 @@ int remove_or_warn(unsigned int mode, const char *file) return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file); } +void warn_on_inaccessible(const char *path) +{ + warning(_("unable to access '%s': %s"), path, strerror(errno)); +} + +int access_or_warn(const char *path, int mode) +{ + int ret = access(path, mode); + if (ret && errno != ENOENT) + warn_on_inaccessible(path); + return ret; +} + struct passwd *xgetpwuid_self(void) { struct passwd *pw; |