diff options
158 files changed, 6089 insertions, 1950 deletions
diff --git a/.gitignore b/.gitignore index 726db73450..aa258a6bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /GIT-BUILD-OPTIONS /GIT-CFLAGS /GIT-LDFLAGS -/GIT-GUI-VARS /GIT-PREFIX /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES @@ -125,7 +124,7 @@ /git-remote-ftps /git-remote-fd /git-remote-ext -/git-remote-testgit +/git-remote-testpy /git-remote-testsvn /git-repack /git-replace @@ -172,7 +171,6 @@ /git-whatchanged /git-write-tree /git-core-*/?* -/gitk-git/gitk-wish /gitweb/GITWEB-BUILD-OPTIONS /gitweb/gitweb.cgi /gitweb/static/gitweb.js @@ -199,6 +197,7 @@ /test-string-list /test-subprocess /test-svn-fe +/test-wildmatch /common-cmds.h *.tar.gz *.dsc diff --git a/Documentation/RelNotes/1.8.1.2.txt b/Documentation/RelNotes/1.8.1.2.txt new file mode 100644 index 0000000000..76ad0b3d01 --- /dev/null +++ b/Documentation/RelNotes/1.8.1.2.txt @@ -0,0 +1,13 @@ +Git 1.8.1.2 Release Notes +========================= + +Fixes since v1.8.1.1 +-------------------- + + * "git archive" did not record uncompressed size in the header when + streaming a zip archive, which confused some implementations of unzip. + + * When users spelled "cc:" in lowercase in the fake "header" in the + trailer part, "git send-email" failed to pick up the addresses from + there. As e-mail headers field names are case insensitive, this + script should follow suit and treat "cc:" and "Cc:" the same way. diff --git a/Documentation/RelNotes/1.8.2.txt b/Documentation/RelNotes/1.8.2.txt new file mode 100644 index 0000000000..f6d15059dc --- /dev/null +++ b/Documentation/RelNotes/1.8.2.txt @@ -0,0 +1,261 @@ +Git v1.8.2 Release Notes +======================== + +Backward compatibility notes +---------------------------- + +In the upcoming major release (tentatively called 1.8.2), we will +change the behavior of the "git push" command. + +When "git push [$there]" does not say what to push, we have used the +traditional "matching" semantics so far (all your branches were sent +to the remote as long as there already are branches of the same name +over there). 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. + + +Updates since v1.8.1 +-------------------- + +UI, Workflows & Features + + * Initial ports to QNX and z/OS UNIX System Services have started. + + * Output from the tests is coloured using "green is okay, yellow is + questionable, red is bad and blue is informative" scheme. + + * In bare repositories, "git shortlog" and other commands now read + mailmap files from the tip of the history, to help running these + tools in server settings. + + * Color specifiers, e.g. "%C(blue)Hello%C(reset)", used in the + "--format=" option of "git log" and friends can be disabled when + the output is not sent to a terminal by prefixing them with + "auto,", e.g. "%C(auto,blue)Hello%C(auto,reset)". + + * Scripts can ask Git that wildcard patterns in pathspecs they give do + not have any significance, i.e. take them as literal strings. + + * The pathspec code learned to grok "foo/**/bar" as a pattern that + matches "bar" in 0-or-more levels of subdirectory in "foo". + + * "git blame" (and "git diff") learned the "--no-follow" option. + + * "git cherry-pick" can be used to replay a root commit to an unborn + branch. + + * "git commit" can be told to use --cleanup=whitespace by setting the + configuration variable commit.cleanup to 'whitespace'. + + * "git fetch --mirror" and fetch that uses other forms of refspec + with wildcard used to attempt to update a symbolic ref that match + the wildcard on the receiving end, which made little sense (the + real ref that is pointed at by the symbolic ref would be updated + anyway). Symbolic refs no longer are affected by such a fetch. + + * "git format-patch" now detects more cases in which a whole branch + is being exported, and uses the description for the branch, when + asked to write a cover letter for the series. + + * "git format-patch" learned "-v $count" option, and prepends a + string "v$count-" to the names of its output files, and also + automatically sets the subject prefix to "PATCH v$count". This + allows patches from rerolled series to be stored under different + names and makes it easier to reuse cover letter messsages. + + * "git log" and friends can be told with --use-mailmap option to + rewrite the names and email addresses of people using the mailmap + mechanism. + + * "git push" now requires "-f" to update a tag, even if it is a + fast-forward, as tags are meant to be fixed points. + + * When "git rebase" fails to generate patches to be applied (e.g. due + to oom), it failed to detect the failure and instead behaved as if + there were nothing to do. A workaround to use a temporary file has + been applied, but we probably would want to revisit this later, as + it hurts the common case of not failing at all. + + * "git submodule" started learning a new mode to integrate with the + tip of the remote branch (as opposed to integrating with the commit + recorded in the superproject's gitlink). + + +Foreign Interface + + * "git fast-export" has been updated for its use in the context of + the remote helper interface. + + * A new remote helper to interact with bzr has been added to contrib/. + + +Performance, Internal Implementation, etc. + + * "git fsck" has been taught to be pickier about entries in tree + objects that should not be there, e.g. ".", ".git", and "..". + + * Matching paths with common forms of pathspecs that contain wildcard + characters has been optimized further. + + * The implementation of "imap-send" has been updated to reuse xml + quoting code from http-push codepath. + + * There is a simple-minded checker for the test scripts in t/ + directory to catch most common mistakes (it is not enabled by + default). + + +Also contains minor documentation updates and code clean-ups. + + +Fixes since v1.8.1 +------------------ + +Unless otherwise noted, all the fixes since v1.8.1 in the maintenance +track are contained in this release (see release notes to them for +details). + + * An element on GIT_CEILING_DIRECTORIES list that does not name the + real path to a directory (i.e. a symbolic link) could have caused + the GIT_DIR discovery logic to escape the ceiling. + (merge 059b379 mh/ceiling later to maint). + + * When attempting to read the XDG-style $HOME/.config/git/config and + finding that $HOME/.config/git is a file, we gave a wrong error + message, instead of treating the case as "a custom config file does + not exist there" and moving on. + (merge 8f2bbe4 jn/warn-on-inaccessible-loosen later to maint). + + * The behaviour visible to the end users was confusing, when they + attempt to kill a process spawned in the editor that was in turn + launched by Git with SIGINT (or SIGQUIT), as Git would catch that + signal and die. We ignore these signals now. + (merge 1250857 pf/editor-ignore-sigint later to maint). + + * A child process that was killed by a signal (e.g. SIGINT) was + reported in an inconsistent way depending on how the process was + spawned by us, with or without a shell in between. + (merge 709ca73 jk/unify-exit-code-by-receiving-signal later to maint). + + * After failing to create a temporary file using mkstemp(), failing + pathname was not reported correctly on some platforms. + (merge f7be59b jc/mkstemp-more-careful-error-reporting later to maint). + + * The attribute mechanism didn't allow limiting attributes to be + applied to only a single directory itself with "path/" like the + exclude mechanism does. + (merge 94bc671 ja/directory-attrs later to maint). + + * "git apply" misbehaved when fixing whitespace breakages by removing + excess trailing blank lines. + (merge 5de7166 jc/apply-trailing-blank-removal later to maint). + + * A tar archive created by "git archive" recorded a directory in a + way that made NetBSD's implementation of "tar" sometimes unhappy. + (merge 22f0dcd rs/leave-base-name-in-name-field-of-tar later to maint). + + * "git archive" did not record uncompressed size in the header when + streaming a zip archive, which confused some implementations of unzip. + (merge 5ea2c84 rs/zip-with-uncompressed-size-in-the-header later to maint). + + * When "git clone --separate-git-dir=$over_there" is interrupted, it + failed to remove the real location of the $GIT_DIR it created. + This was most visible when interrupting a submodule update. + (merge 9be1980 jl/interrupt-clone-remove-separate-git-dir later to maint). + + * The way "git svn" asked for password using SSH_ASKPASS and + GIT_ASKPASS was not in line with the rest of the system. + (merge e9263e4 ss/svn-prompt later to maint). + + * The --graph code fell into infinite loop when asked to do what the + code did not expect. + (merge 656197a mk/maint-graph-infinity-loop later to maint). + + * http transport was wrong to ask for the username when the + authentication is done by certificate identity. + (merge 75e9a40 rb/http-cert-cred-no-username-prompt later to maint). + + * "git pack-refs" that ran in parallel to another process that + created new refs had a nasty race. + (merge b3f1280 jk/repack-ref-racefix later to maint). + + * After "git add -N" and then writing a tree object out of the + index, the cache-tree data structure got corrupted. + (merge eec3e7e nd/invalidate-i-t-a-cache-tree later to maint). + + * "git merge --no-edit" computed who were involved in the work done + on the side branch, even though that information is to be discarded + without getting seen in the editor. + (merge 9bcbb1c jc/maint-fmt-merge-msg-no-edit-lose-credit later to maint). + + * "git merge" started calling prepare-commit-msg hook like "git + commit" does some time ago, but forgot to pay attention to the exit + status of the hook. + (merge 3e4141d ap/merge-stop-at-prepare-commit-msg-failure later to maint). + + * When users spell "cc:" in lowercase in the fake "header" in the + trailer part, "git send-email" failed to pick up the addresses from + there. As e-mail headers field names are case insensitive, this + script should follow suit and treat "cc:" and "Cc:" the same way. + (merge 6310071 nz/send-email-headers-are-case-insensitive later to maint). + + * Output from "git status --ignored" showed an unexpected interaction + with "--untracked". + (merge a45fb69 ap/status-ignored-in-ignored-directory later to maint). + + * "gitweb", when sorting by age to show repositories with new + activities first, used to sort repositories with absolutely + nothing in it early, which was not very useful. + (merge 28dae18 md/gitweb-sort-by-age later to maint). + + * "gitweb"'s code to sanitize control characters before passing it to + "highlight" filter lost known-to-be-safe control characters by + mistake. + (merge 0e901d2 os/gitweb-highlight-uncaptured later to maint). + + * When a line to be wrapped has a solid run of non space characters + whose length exactly is the wrap width, "git shortlog -w" failed + to add a newline after such a line. + (merge e0db176 sp/shortlog-missing-lf later to maint). + + * Command line completion leaked an unnecessary error message while + looking for possible matches with paths in <tree-ish>. + (merge ca87dd6 ds/completion-silence-in-tree-path-probe later to maint). + + * Command line completion for "tcsh" emitted an unwanted space + after completing a single directory name. + (merge 92f1c04 mk/complete-tcsh later to maint). + + * Some shells do not behave correctly when IFS is unset; work it + around by explicitly setting it to the default value. + (merge 393050c jc/maint-fbsd-sh-ifs-workaround later to maint). + + * Some scripted programs written in Python did not get updated when + PYTHON_PATH changed. + (cherry-pick 96a4647fca54031974cd6ad1 later to maint). + + * When autoconf is used, any build on a different commit always ran + "config.status --recheck" even when unnecessary. + (merge 1226504 jn/less-reconfigure later to maint). + + * We have been carrying a translated and long-unmaintained copy of an + old version of the tutorial; removed. + (merge 0a85441 ta/remove-stale-translated-tut later to maint). + + * t4014, t9502 and t0200 tests had various portability issues that + broke on OpenBSD. + (merge 27f6342 jc/maint-test-portability later to maint). + + * t9020 and t3600 tests had various portability issues. + (merge 5a02966 jc/test-portability later to maint). + + * t9200 runs "cvs init" on a directory that already exists, but a + platform can configure this fail for the current user (e.g. you + need to be in the cvsadmin group on NetBSD 6.0). + (merge 8666df0 jc/test-cvs-no-init-in-existing-dir later to maint). + + * t9020 and t9810 had a few non-portable shell script construct. + (merge 2797914 tb/test-t9020-no-which later to maint). + (merge 6f4e505 tb/test-t9810-no-sed-i later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index bf8f911e1f..b87f744643 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -140,10 +140,11 @@ advice.*:: can tell Git that you do not need help by setting these to 'false': + -- - pushNonFastForward:: + pushUpdateRejected:: Set this variable to 'false' if you want to disable - 'pushNonFFCurrent', 'pushNonFFDefault', and - 'pushNonFFMatching' simultaneously. + 'pushNonFFCurrent', 'pushNonFFDefault', + 'pushNonFFMatching', and 'pushAlreadyExists' + simultaneously. pushNonFFCurrent:: Advice shown when linkgit:git-push[1] fails due to a non-fast-forward update to the current branch. @@ -158,6 +159,9 @@ advice.*:: 'matching refs' explicitly (i.e. you used ':', or specified a refspec that isn't your current branch) and it resulted in a non-fast-forward error. + pushAlreadyExists:: + Shown when linkgit:git-push[1] rejects an update that + does not qualify for fast-forwarding (e.g., a tag.) statusHints:: Show directions on how to proceed from the current state in the output of linkgit:git-status[1], in @@ -735,6 +739,12 @@ branch.<name>.rebase:: it unless you understand the implications (see linkgit:git-rebase[1] for details). +branch.<name>.description:: + Branch description, can be edited with + `git branch --edit-description`. Branch description is + automatically added in the format-patch cover letter or + request-pull summary. + browser.<tool>.cmd:: Specify the command to invoke the specified browser. The specified command is evaluated in shell with the URLs passed @@ -913,6 +923,15 @@ column.tag:: Specify whether to output tag listing in `git tag` in columns. See `column.ui` for details. +commit.cleanup:: + This setting overrides the default of the `--cleanup` option in + `git commit`. See linkgit:git-commit[1] for details. Changing the + default can be useful when you always want to keep lines that begin + with comment character `#` in your log message, in which case you + would do `git config commit.cleanup whitespace` (note that you will + have to remove the help lines that begin with `#` in the commit log + template yourself, if you do this). + commit.status:: A boolean to enable/disable inclusion of status information in the commit message template when using an editor to prepare the commit @@ -1351,6 +1370,12 @@ help.autocorrect:: value is 0 - the command will be just shown but not executed. This is the default. +help.htmlpath:: + Specify the path where the HTML documentation resides. File system paths + and URLs are supported. HTML pages will be prefixed with this path when + help is displayed in the 'web' format. This defaults to the documentation + path of your Git installation. + http.proxy:: Override the HTTP proxy, normally configured using the 'http_proxy', 'https_proxy', and 'all_proxy' environment variables (see @@ -1509,6 +1534,10 @@ log.showroot:: Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which normally hide the root commit will now show it. True by default. +log.mailmap:: + If true, makes linkgit:git-log[1], linkgit:git-show[1], and + linkgit:git-whatchanged[1] assume `--use-mailmap`. + mailmap.file:: The location of an augmenting mailmap file. The default mailmap, located in the root of the repository, is loaded @@ -1517,6 +1546,14 @@ mailmap.file:: subdirectory, or somewhere outside of the repository itself. See linkgit:git-shortlog[1] and linkgit:git-blame[1]. +mailmap.blob:: + Like `mailmap.file`, but consider the value as a reference to a + blob in the repository. If both `mailmap.file` and + `mailmap.blob` are given, both are parsed, with entries from + `mailmap.file` taking precedence. In a bare repository, this + defaults to `HEAD:.mailmap`. In a non-bare repository, it + defaults to empty. + man.viewer:: Specify the programs that may be used to display help in the 'man' format. See linkgit:git-help[1]. @@ -1995,6 +2032,12 @@ submodule.<name>.update:: URL and other values found in the `.gitmodules` file. See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details. +submodule.<name>.branch:: + The remote branch name for a submodule, used by `git submodule + update --remote`. Set this option to override the value found in + the `.gitmodules` file. See linkgit:git-submodule[1] and + linkgit:gitmodules[5] for details. + submodule.<name>.fetchRecurseSubmodules:: This option can be used to control recursive fetching of this submodule. It can be overridden by using the --[no-]recurse-submodules diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt index 6d5a04c83b..a221169515 100644 --- a/Documentation/git-commit-tree.txt +++ b/Documentation/git-commit-tree.txt @@ -72,13 +72,13 @@ if set: GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE - EMAIL (nb "<", ">" and "\n"s are stripped) In case (some of) these environment variables are not set, the information is taken from the configuration items user.name and user.email, or, if not -present, system user name and the hostname used for outgoing mail (taken +present, the environment variable EMAIL, or, if that is not set, +system user name and the hostname used for outgoing mail (taken from `/etc/mailname` and falling back to the fully qualified hostname when that file does not exist). diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 7bdb039d5e..41b27da325 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -179,7 +179,9 @@ OPTIONS only if the message is to be edited. Otherwise only whitespace removed. The 'verbatim' mode does not change message at all, 'whitespace' removes just leading/trailing whitespace lines - and 'strip' removes both whitespace and commentary. + and 'strip' removes both whitespace and commentary. The default + can be changed by the 'commit.cleanup' configuration variable + (see linkgit:git-config[1]). -e:: --edit:: diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 98d9881d7e..9d5353e8be 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -213,11 +213,9 @@ Problems related to tags: * Multiple tags on the same revision are not imported. If you suspect that any of these issues may apply to the repository you -want to import consider using these alternative tools which proved to be -more stable in practice: +want to imort, consider using cvs2git: -* cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org` -* parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs` +* cvs2git (part of cvs2svn), `http://subversion.apache.org/` GIT --- diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index db55a4e0bb..f2e08d11c1 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -117,7 +117,7 @@ returns an empty string instead. As a special case for the date-type fields, you may specify a format for the date by adding one of `:default`, `:relative`, `:short`, `:local`, -`:iso8601` or `:rfc2822` to the end of the fieldname; e.g. +`:iso8601`, `:rfc2822` or `:raw` to the end of the fieldname; e.g. `%(taggerdate:relative)`. diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 259dce4994..9a914d0159 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -18,7 +18,7 @@ SYNOPSIS [--start-number <n>] [--numbered-files] [--in-reply-to=Message-Id] [--suffix=.<sfx>] [--ignore-if-in-upstream] - [--subject-prefix=Subject-Prefix] + [--subject-prefix=Subject-Prefix] [(--reroll-count|-v) <n>] [--to=<email>] [--cc=<email>] [--cover-letter] [--quiet] [--notes[=<ref>]] [<common diff options>] @@ -166,6 +166,15 @@ will want to ensure that threading is disabled for `git send-email`. allows for useful naming of a patch series, and can be combined with the `--numbered` option. +-v <n>:: +--reroll-count=<n>:: + Mark the series as the <n>-th iteration of the topic. The + output filenames have `v<n>` pretended to them, and the + subject prefix ("PATCH" by default, but configurable via the + `--subject-prefix` option) has ` v<n>` appended to it. E.g. + `--reroll-count=4` may produce `v4-0001-add-makefile.patch` + file that has "Subject: [PATCH v4 1/20] Add makefile" in it. + --to=<email>:: Add a `To:` header to the email headers. This is in addition to any configured headers, and may be used multiple times. diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 585dac40ba..22c0d6e4b1 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -47,6 +47,11 @@ OPTIONS Print out the ref name given on the command line by which each commit was reached. +--use-mailmap:: + Use mailmap file to map author and committer names and email + to canonical real names and email addresses. See + linkgit:git-shortlog[1]. + --full-diff:: Without this flag, "git log -p <path>..." shows commits that touch the specified paths, and diffs about the same specified @@ -167,7 +172,7 @@ log.showroot:: `git log -p` output would be shown without a diff attached. The default is `true`. -mailmap.file:: +mailmap.*:: See linkgit:git-shortlog[1]. notes.displayRef:: diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 8b637d339f..c964b796be 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -51,10 +51,11 @@ be named. If `:`<dst> is omitted, the same ref as <src> will be updated. + The object referenced by <src> is used to update the <dst> reference -on the remote side, but by default this is only allowed if the -update can fast-forward <dst>. By having the optional leading `+`, -you can tell git to update the <dst> ref even when the update is not a -fast-forward. This does *not* attempt to merge <src> into <dst>. See +on the remote side. By default this is only allowed if <dst> is not +a tag (annotated or lightweight), and then only if it can fast-forward +<dst>. By having the optional leading `+`, you can tell git to update +the <dst> ref even if it is not allowed by default (e.g., it is not a +fast-forward.) This does *not* attempt to merge <src> into <dst>. See EXAMPLES below for details. + `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`. diff --git a/Documentation/git-remote-testgit.txt b/Documentation/git-remote-testgit.txt index 2a67d456a3..612a625ced 100644 --- a/Documentation/git-remote-testgit.txt +++ b/Documentation/git-remote-testgit.txt @@ -19,7 +19,7 @@ testcase for the remote-helper functionality, and as an example to show remote-helper authors one possible implementation. The best way to learn more is to read the comments and source code in -'git-remote-testgit.py'. +'git-remote-testgit'. SEE ALSO -------- diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index b1de3bade7..b1996f1a63 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -13,7 +13,7 @@ SYNOPSIS [--reference <repository>] [--] <repository> [<path>] 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...] 'git submodule' [--quiet] init [--] [<path>...] -'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] +'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>] [commit] [--] [<path>...] @@ -208,6 +208,8 @@ OPTIONS -b:: --branch:: Branch of repository to add as submodule. + The name of the branch is recorded as `submodule.<path>.branch` in + `.gitmodules` for `update --remote`. -f:: --force:: @@ -236,6 +238,27 @@ OPTIONS (the default). This limit only applies to modified submodules. The size is always limited to 1 for added/deleted/typechanged submodules. +--remote:: + This option is only valid for the update command. Instead of using + the superproject's recorded SHA-1 to update the submodule, use the + status of the submodule's remote tracking branch. The remote used + is branch's remote (`branch.<name>.remote`), defaulting to `origin`. + The remote branch used defaults to `master`, but the branch name may + be overridden by setting the `submodule.<name>.branch` option in + either `.gitmodules` or `.git/config` (with `.git/config` taking + precedence). ++ +This works for any of the supported update procedures (`--checkout`, +`--rebase`, etc.). The only change is the source of the target SHA-1. +For example, `submodule update --remote --merge` will merge upstream +submodule changes into the submodules, while `submodule update +--merge` will merge superproject gitlink changes into the submodules. ++ +In order to ensure a current tracking branch state, `update --remote` +fetches the submodule's remote repository before calculating the +SHA-1. If you don't want to fetch, you should use `submodule update +--remote --no-fetch`. + -N:: --no-fetch:: This option is only valid for the update command. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 69decb13b0..34d438b0ab 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -346,6 +346,16 @@ Any other arguments are passed directly to 'git log' corresponding git commit hash (this can optionally be followed by a tree-ish to specify which branch should be searched). When given a tree-ish, returns the corresponding SVN revision number. ++ +--before;; + Don't require an exact match if given an SVN revision, instead find + the commit corresponding to the state of the SVN repository (on the + current branch) at the specified revision. ++ +--after;; + Don't require an exact match if given an SVN revision; if there is + not an exact match return the closest match searching forward in the + history. 'set-tree':: You should consider using 'dcommit' instead of this command. diff --git a/Documentation/git.txt b/Documentation/git.txt index 276491223a..555250dfa0 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -429,6 +429,11 @@ help ...`. Do not use replacement refs to replace git objects. See linkgit:git-replace[1] for more information. +--literal-pathspecs:: + Treat pathspecs literally, rather than as glob patterns. This is + equivalent to setting the `GIT_LITERAL_PATHSPECS` environment + variable to `1`. + GIT COMMANDS ------------ @@ -805,6 +810,16 @@ for further details. as a file path and will try to write the trace messages into it. +GIT_LITERAL_PATHSPECS:: + Setting this variable to `1` will cause git to treat all + pathspecs literally, rather than as glob patterns. For example, + running `GIT_LITERAL_PATHSPECS=1 git log -- '*.c'` will search + for commits that touch the path `*.c`, not any paths that the + glob `*.c` matches. You might want this if you are feeding + literal paths to git (e.g., paths previously given to you by + `git ls-tree`, `--raw` diff output, etc). + + Discussion[[Discussion]] ------------------------ diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 1b82fe1969..91a6438031 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -108,6 +108,25 @@ PATTERN FORMAT For example, "/{asterisk}.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". +Two consecutive asterisks ("`**`") in patterns matched against +full pathname may have special meaning: + + - A leading "`**`" followed by a slash means match in all + directories. For example, "`**/foo`" matches file or directory + "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar" + matches file or directory "`bar`" anywhere that is directly + under directory "`foo`". + + - A trailing "/**" matches everything inside. For example, + "abc/**" matches all files inside directory "abc", relative + to the location of the `.gitignore` file, with infinite depth. + + - A slash followed by two consecutive asterisks then a slash + matches zero or more directories. For example, "`a/**/b`" + matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on. + + - Other consecutive asterisks are considered invalid. + NOTES ----- diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index ab3e91c054..52d7ae4313 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -49,6 +49,11 @@ submodule.<name>.update:: This config option is overridden if 'git submodule update' is given the '--merge', '--rebase' or '--checkout' options. +submodule.<name>.branch:: + A remote branch name for tracking updates in the upstream submodule. + If the option is not specified, it defaults to 'master'. See the + `--remote` documentation in linkgit:git-submodule[1] for details. + submodule.<name>.fetchRecurseSubmodules:: This option can be used to control recursive fetching of this submodule. If this option is also present in the submodules entry in diff --git a/Documentation/mailmap.txt b/Documentation/mailmap.txt index dd89fca3f8..4a8c276529 100644 --- a/Documentation/mailmap.txt +++ b/Documentation/mailmap.txt @@ -1,5 +1,6 @@ If the file `.mailmap` exists at the toplevel of the repository, or at -the location pointed to by the mailmap.file configuration option, it +the location pointed to by the mailmap.file or mailmap.blob +configuration options, it is used to map author and committer names and email addresses to canonical real names and email addresses. diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index d9eddedc72..105f18a6f9 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -144,7 +144,11 @@ The placeholders are: - '%Cgreen': switch color to green - '%Cblue': switch color to blue - '%Creset': reset color -- '%C(...)': color specification, as described in color.branch.* config option +- '%C(...)': color specification, as described in color.branch.* config option; + adding `auto,` at the beginning will emit color only when colors are + enabled for log output (by `color.diff`, `color.ui`, or `--color`, and + respecting the `auto` settings of the former if we are going to a + terminal) - '%m': left, right or boundary mark - '%n': newline - '%%': a raw '%' diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt index add6f435b5..944fc39fac 100644 --- a/Documentation/technical/api-directory-listing.txt +++ b/Documentation/technical/api-directory-listing.txt @@ -9,37 +9,40 @@ Data structure -------------- `struct dir_struct` structure is used to pass directory traversal -options to the library and to record the paths discovered. The notable -options are: +options to the library and to record the paths discovered. A single +`struct dir_struct` is used regardless of whether or not the traversal +recursively descends into subdirectories. + +The notable options are: `exclude_per_dir`:: The name of the file to be read in each directory for excluded files (typically `.gitignore`). -`collect_ignored`:: +`flags`:: - Include paths that are to be excluded in the result. + A bit-field of options: -`show_ignored`:: +`DIR_SHOW_IGNORED`::: The traversal is for finding just ignored files, not unignored files. -`show_other_directories`:: +`DIR_SHOW_OTHER_DIRECTORIES`::: Include a directory that is not tracked. -`hide_empty_directories`:: +`DIR_HIDE_EMPTY_DIRECTORIES`::: Do not include a directory that is not tracked and is empty. -`no_gitlinks`:: +`DIR_NO_GITLINKS`::: If set, recurse into a directory that looks like a git directory. Otherwise it is shown as a directory. -The result of the enumeration is left in these fields:: +The result of the enumeration is left in these fields: `entries[]`:: diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt index 7386bcab3e..20be348834 100644 --- a/Documentation/technical/api-string-list.txt +++ b/Documentation/technical/api-string-list.txt @@ -82,14 +82,6 @@ Functions call free() on the util members of any items that have to be deleted. Preserve the order of the items that are retained. -`string_list_longest_prefix`:: - - Return the longest string within a string_list that is a - prefix (in the sense of prefixcmp()) of the specified string, - or NULL if no such prefix exists. This function does not - require the string_list to be sorted (it does a linear - search). - `print_string_list`:: Dump a string_list to stdout, useful mainly for debugging purposes. It diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index c572e8da13..e9f7abca91 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.8.1.1 +DEF_VER=v1.8.1.GIT LF=' ' @@ -74,10 +74,14 @@ all:: # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks # d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7). # +# Define HAVE_STRINGS_H if you have strings.h and need it for strcasecmp. +# # Define NO_STRCASESTR if you don't have strcasestr. # # Define NO_MEMMEM if you don't have memmem. # +# Define NO_GETPAGESIZE if you don't have getpagesize. +# # Define NO_STRLCPY if you don't have strlcpy. # # Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the @@ -165,6 +169,10 @@ all:: # Define NO_POLL if you do not have or don't want to use poll(). # This also implies NO_SYS_POLL_H. # +# Define NEEDS_SYS_PARAM_H if you need to include sys/param.h to compile, +# *PLEASE* REPORT to git@vger.kernel.org if your platform needs this; +# we want to know more about the issue. +# # Define NO_PTHREADS if you do not have or do not want to use Pthreads. # # Define NO_PREAD if you have a problem with pread() system call (e.g. @@ -330,19 +338,6 @@ GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN -include GIT-VERSION-FILE -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') -uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') -uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') -uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') -uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') -uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') - -ifdef MSVC - # avoid the MingW and Cygwin configuration sections - uname_S := Windows - uname_O := Windows -endif - # CFLAGS and LDFLAGS are for the users to override from the command line. CFLAGS = -g -O2 -Wall @@ -474,7 +469,7 @@ SCRIPT_PERL += git-relink.perl SCRIPT_PERL += git-send-email.perl SCRIPT_PERL += git-svn.perl -SCRIPT_PYTHON += git-remote-testgit.py +SCRIPT_PYTHON += git-remote-testpy.py SCRIPT_PYTHON += git-p4.py SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ @@ -528,6 +523,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-string-list TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe +TEST_PROGRAMS_NEED_X += test-wildmatch TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) @@ -700,6 +696,7 @@ LIB_H += userdiff.h LIB_H += utf8.h LIB_H += varint.h LIB_H += walker.h +LIB_H += wildmatch.h LIB_H += wt-status.h LIB_H += xdiff-interface.h LIB_H += xdiff/xdiff.h @@ -769,7 +766,7 @@ LIB_OBJS += log-tree.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += merge.o -LIB_OBJS += merge-file.o +LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o LIB_OBJS += mergesort.o LIB_OBJS += name-hash.o @@ -834,6 +831,7 @@ LIB_OBJS += utf8.o LIB_OBJS += varint.o LIB_OBJS += version.o LIB_OBJS += walker.o +LIB_OBJS += wildmatch.o LIB_OBJS += wrapper.o LIB_OBJS += write_or_die.o LIB_OBJS += ws.o @@ -941,518 +939,7 @@ EXTLIBS = GIT_USER_AGENT = git/$(GIT_VERSION) -# -# Platform specific tweaks -# - -# We choose to avoid "if .. else if .. else .. endif endif" -# because maintaining the nesting to match is a pain. If -# we had "elif" things would have been much nicer... - -ifeq ($(uname_M),x86_64) - XDL_FAST_HASH = YesPlease -endif -ifeq ($(uname_S),OSF1) - # Need this for u_short definitions et al - BASIC_CFLAGS += -D_OSF_SOURCE - SOCKLEN_T = int - NO_STRTOULL = YesPlease - NO_NSEC = YesPlease -endif -ifeq ($(uname_S),Linux) - NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease - HAVE_PATHS_H = YesPlease - LIBC_CONTAINS_LIBINTL = YesPlease - HAVE_DEV_TTY = YesPlease -endif -ifeq ($(uname_S),GNU/kFreeBSD) - NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease - HAVE_PATHS_H = YesPlease - DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease - LIBC_CONTAINS_LIBINTL = YesPlease -endif -ifeq ($(uname_S),UnixWare) - CC = cc - NEEDS_SOCKET = YesPlease - NEEDS_NSL = YesPlease - NEEDS_SSL_WITH_CRYPTO = YesPlease - NEEDS_LIBICONV = YesPlease - SHELL_PATH = /usr/local/bin/bash - NO_IPV6 = YesPlease - NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease - BASIC_CFLAGS += -Kthread - BASIC_CFLAGS += -I/usr/local/include - BASIC_LDFLAGS += -L/usr/local/lib - INSTALL = ginstall - TAR = gtar - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease -endif -ifeq ($(uname_S),SCO_SV) - ifeq ($(uname_R),3.2) - CFLAGS = -O2 - endif - ifeq ($(uname_R),5) - CC = cc - BASIC_CFLAGS += -Kthread - endif - NEEDS_SOCKET = YesPlease - NEEDS_NSL = YesPlease - NEEDS_SSL_WITH_CRYPTO = YesPlease - NEEDS_LIBICONV = YesPlease - SHELL_PATH = /usr/bin/bash - NO_IPV6 = YesPlease - NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease - BASIC_CFLAGS += -I/usr/local/include - BASIC_LDFLAGS += -L/usr/local/lib - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - INSTALL = ginstall - TAR = gtar -endif -ifeq ($(uname_S),Darwin) - NEEDS_CRYPTO_WITH_SSL = YesPlease - NEEDS_SSL_WITH_CRYPTO = YesPlease - NEEDS_LIBICONV = YesPlease - ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2) - OLD_ICONV = UnfortunatelyYes - endif - ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2) - NO_STRLCPY = YesPlease - endif - NO_MEMMEM = YesPlease - USE_ST_TIMESPEC = YesPlease - HAVE_DEV_TTY = YesPlease - COMPAT_OBJS += compat/precompose_utf8.o - BASIC_CFLAGS += -DPRECOMPOSE_UNICODE -endif -ifeq ($(uname_S),SunOS) - NEEDS_SOCKET = YesPlease - NEEDS_NSL = YesPlease - SHELL_PATH = /bin/bash - SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease - NO_REGEX = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - NO_MSGFMT_EXTENDED_OPTIONS = YesPlease - HAVE_DEV_TTY = YesPlease - ifeq ($(uname_R),5.6) - SOCKLEN_T = int - NO_HSTRERROR = YesPlease - NO_IPV6 = YesPlease - NO_SOCKADDR_STORAGE = YesPlease - NO_UNSETENV = YesPlease - NO_SETENV = YesPlease - NO_STRLCPY = YesPlease - NO_STRTOUMAX = YesPlease - GIT_TEST_CMP = cmp - endif - ifeq ($(uname_R),5.7) - NEEDS_RESOLV = YesPlease - NO_IPV6 = YesPlease - NO_SOCKADDR_STORAGE = YesPlease - NO_UNSETENV = YesPlease - NO_SETENV = YesPlease - NO_STRLCPY = YesPlease - NO_STRTOUMAX = YesPlease - GIT_TEST_CMP = cmp - endif - ifeq ($(uname_R),5.8) - NO_UNSETENV = YesPlease - NO_SETENV = YesPlease - NO_STRTOUMAX = YesPlease - GIT_TEST_CMP = cmp - endif - ifeq ($(uname_R),5.9) - NO_UNSETENV = YesPlease - NO_SETENV = YesPlease - NO_STRTOUMAX = YesPlease - GIT_TEST_CMP = cmp - endif - INSTALL = /usr/ucb/install - TAR = gtar - BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H -endif -ifeq ($(uname_O),Cygwin) - ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4) - NO_D_TYPE_IN_DIRENT = YesPlease - NO_D_INO_IN_DIRENT = YesPlease - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease - NO_SYMLINK_HEAD = YesPlease - NO_IPV6 = YesPlease - OLD_ICONV = UnfortunatelyYes - CYGWIN_V15_WIN32API = YesPlease - endif - NO_THREAD_SAFE_PREAD = YesPlease - NEEDS_LIBICONV = YesPlease - NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes - NO_TRUSTABLE_FILEMODE = UnfortunatelyYes - NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease - # There are conflicting reports about this. - # On some boxes NO_MMAP is needed, and not so elsewhere. - # Try commenting this out if you suspect MMAP is more efficient - NO_MMAP = YesPlease - X = .exe - COMPAT_OBJS += compat/cygwin.o - UNRELIABLE_FSTAT = UnfortunatelyYes - SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield -endif -ifeq ($(uname_S),FreeBSD) - NEEDS_LIBICONV = YesPlease - OLD_ICONV = YesPlease - NO_MEMMEM = YesPlease - BASIC_CFLAGS += -I/usr/local/include - BASIC_LDFLAGS += -L/usr/local/lib - DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease - USE_ST_TIMESPEC = YesPlease - ifeq ($(shell expr "$(uname_R)" : '4\.'),2) - PTHREAD_LIBS = -pthread - NO_UINTMAX_T = YesPlease - NO_STRTOUMAX = YesPlease - endif - PYTHON_PATH = /usr/local/bin/python - HAVE_PATHS_H = YesPlease -endif -ifeq ($(uname_S),OpenBSD) - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - USE_ST_TIMESPEC = YesPlease - NEEDS_LIBICONV = YesPlease - BASIC_CFLAGS += -I/usr/local/include - BASIC_LDFLAGS += -L/usr/local/lib - HAVE_PATHS_H = YesPlease -endif -ifeq ($(uname_S),NetBSD) - ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) - NEEDS_LIBICONV = YesPlease - endif - BASIC_CFLAGS += -I/usr/pkg/include - BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib - USE_ST_TIMESPEC = YesPlease - NO_MKSTEMPS = YesPlease - HAVE_PATHS_H = YesPlease -endif -ifeq ($(uname_S),AIX) - DEFAULT_PAGER = more - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease - NO_STRLCPY = YesPlease - NO_NSEC = YesPlease - FREAD_READS_DIRECTORIES = UnfortunatelyYes - INTERNAL_QSORT = UnfortunatelyYes - NEEDS_LIBICONV = YesPlease - BASIC_CFLAGS += -D_LARGE_FILES - ifeq ($(shell expr "$(uname_V)" : '[1234]'),1) - NO_PTHREADS = YesPlease - else - PTHREAD_LIBS = -lpthread - endif - ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3) - INLINE = '' - endif - GIT_TEST_CMP = cmp -endif -ifeq ($(uname_S),GNU) - # GNU/Hurd - NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease - HAVE_PATHS_H = YesPlease - LIBC_CONTAINS_LIBINTL = YesPlease -endif -ifeq ($(uname_S),IRIX) - NO_SETENV = YesPlease - NO_UNSETENV = YesPlease - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease - NO_MKDTEMP = YesPlease - # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads - # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), - # git dies with a segmentation fault when trying to access the first - # entry of a reflog. The conservative choice is made to always set - # NO_MMAP. If you suspect that your compiler is not affected by this - # issue, comment out the NO_MMAP statement. - NO_MMAP = YesPlease - NO_REGEX = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - SNPRINTF_RETURNS_BOGUS = YesPlease - SHELL_PATH = /usr/gnu/bin/bash - NEEDS_LIBGEN = YesPlease -endif -ifeq ($(uname_S),IRIX64) - NO_SETENV = YesPlease - NO_UNSETENV = YesPlease - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease - NO_MKDTEMP = YesPlease - # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads - # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), - # git dies with a segmentation fault when trying to access the first - # entry of a reflog. The conservative choice is made to always set - # NO_MMAP. If you suspect that your compiler is not affected by this - # issue, comment out the NO_MMAP statement. - NO_MMAP = YesPlease - NO_REGEX = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - SNPRINTF_RETURNS_BOGUS = YesPlease - SHELL_PATH = /usr/gnu/bin/bash - NEEDS_LIBGEN = YesPlease -endif -ifeq ($(uname_S),HP-UX) - INLINE = __inline - NO_IPV6 = YesPlease - NO_SETENV = YesPlease - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease - NO_STRLCPY = YesPlease - NO_MKDTEMP = YesPlease - NO_UNSETENV = YesPlease - NO_HSTRERROR = YesPlease - NO_SYS_SELECT_H = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - SNPRINTF_RETURNS_BOGUS = YesPlease - NO_NSEC = YesPlease - ifeq ($(uname_R),B.11.00) - NO_INET_NTOP = YesPlease - NO_INET_PTON = YesPlease - endif - ifeq ($(uname_R),B.10.20) - # Override HP-UX 11.x setting: - INLINE = - SOCKLEN_T = size_t - NO_PREAD = YesPlease - NO_INET_NTOP = YesPlease - NO_INET_PTON = YesPlease - endif - GIT_TEST_CMP = cmp -endif -ifeq ($(uname_S),Windows) - GIT_VERSION := $(GIT_VERSION).MSVC - pathsep = ; - NO_PREAD = YesPlease - NEEDS_CRYPTO_WITH_SSL = YesPlease - NO_LIBGEN_H = YesPlease - NO_POLL = YesPlease - NO_SYMLINK_HEAD = YesPlease - NO_IPV6 = YesPlease - NO_UNIX_SOCKETS = YesPlease - NO_SETENV = YesPlease - NO_UNSETENV = YesPlease - NO_STRCASESTR = YesPlease - NO_STRLCPY = YesPlease - NO_STRTOK_R = YesPlease - NO_FNMATCH = YesPlease - NO_MEMMEM = YesPlease - # NEEDS_LIBICONV = YesPlease - NO_ICONV = YesPlease - NO_STRTOUMAX = YesPlease - NO_STRTOULL = YesPlease - NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease - SNPRINTF_RETURNS_BOGUS = YesPlease - NO_SVN_TESTS = YesPlease - NO_PERL_MAKEMAKER = YesPlease - RUNTIME_PREFIX = YesPlease - NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease - NO_NSEC = YesPlease - USE_WIN32_MMAP = YesPlease - # USE_NED_ALLOCATOR = YesPlease - UNRELIABLE_FSTAT = UnfortunatelyYes - OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo - NO_REGEX = YesPlease - NO_CURL = YesPlease - NO_PYTHON = YesPlease - BLK_SHA1 = YesPlease - NO_POSIX_GOODIES = UnfortunatelyYes - NATIVE_CRLF = YesPlease - DEFAULT_HELP_FORMAT = html - - CC = compat/vcbuild/scripts/clink.pl - AR = compat/vcbuild/scripts/lib.pl - CFLAGS = - BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE - COMPAT_OBJS = compat/msvc.o compat/winansi.o \ - compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/dirent.o - COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" - BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib - EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib - PTHREAD_LIBS = - lib = -ifndef DEBUG - BASIC_CFLAGS += -GL -Os -MT - BASIC_LDFLAGS += -LTCG - AR += -LTCG -else - BASIC_CFLAGS += -Zi -MTd -endif - X = .exe -endif -ifeq ($(uname_S),Interix) - NO_INITGROUPS = YesPlease - NO_IPV6 = YesPlease - NO_MEMMEM = YesPlease - NO_MKDTEMP = YesPlease - NO_STRTOUMAX = YesPlease - NO_NSEC = YesPlease - NO_MKSTEMPS = YesPlease - ifeq ($(uname_R),3.5) - NO_INET_NTOP = YesPlease - NO_INET_PTON = YesPlease - NO_SOCKADDR_STORAGE = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - endif - ifeq ($(uname_R),5.2) - NO_INET_NTOP = YesPlease - NO_INET_PTON = YesPlease - NO_SOCKADDR_STORAGE = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - endif -endif -ifeq ($(uname_S),Minix) - NO_IPV6 = YesPlease - NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease - NO_NSEC = YesPlease - NEEDS_LIBGEN = - NEEDS_CRYPTO_WITH_SSL = YesPlease - NEEDS_IDN_WITH_CURL = YesPlease - NEEDS_SSL_WITH_CURL = YesPlease - NEEDS_RESOLV = - NO_HSTRERROR = YesPlease - NO_MMAP = YesPlease - NO_CURL = - NO_EXPAT = -endif -ifeq ($(uname_S),NONSTOP_KERNEL) - # Needs some C99 features, "inline" is just one of them. - # INLINE='' would just replace one set of warnings with another and - # still not compile in c89 mode, due to non-const array initializations. - CC = cc -c99 - # Disable all optimization, seems to result in bad code, with -O or -O2 - # or even -O1 (default), /usr/local/libexec/git-core/git-pack-objects - # abends on "git push". Needs more investigation. - CFLAGS = -g -O0 - # We'd want it to be here. - prefix = /usr/local - # Our's are in ${prefix}/bin (perl might also be in /usr/bin/perl). - PERL_PATH = ${prefix}/bin/perl - PYTHON_PATH = ${prefix}/bin/python - - # As detected by './configure'. - # Missdetected, hence commented out, see below. - #NO_CURL = YesPlease - # Added manually, see above. - NEEDS_SSL_WITH_CURL = YesPlease - HAVE_LIBCHARSET_H = YesPlease - NEEDS_LIBICONV = YesPlease - NEEDS_LIBINTL_BEFORE_LIBICONV = YesPlease - NO_SYS_SELECT_H = UnfortunatelyYes - NO_D_TYPE_IN_DIRENT = YesPlease - NO_HSTRERROR = YesPlease - NO_STRCASESTR = YesPlease - NO_FNMATCH_CASEFOLD = YesPlease - NO_MEMMEM = YesPlease - NO_STRLCPY = YesPlease - NO_SETENV = YesPlease - NO_UNSETENV = YesPlease - NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease - # Currently libiconv-1.9.1. - OLD_ICONV = UnfortunatelyYes - NO_REGEX = YesPlease - NO_PTHREADS = UnfortunatelyYes - - # Not detected (nor checked for) by './configure'. - # We don't have SA_RESTART on NonStop, unfortunalety. - COMPAT_CFLAGS += -DSA_RESTART=0 - # Apparently needed in compat/fnmatch/fnmatch.c. - COMPAT_CFLAGS += -DHAVE_STRING_H=1 - NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease - NO_NSEC = YesPlease - NO_PREAD = YesPlease - NO_MMAP = YesPlease - NO_POLL = YesPlease - NO_INTPTR_T = UnfortunatelyYes - # Bug report 10-120822-4477 submitted to HP NonStop development. - MKDIR_WO_TRAILING_SLASH = YesPlease - # RFE 10-120912-4693 submitted to HP NonStop development. - NO_SETITIMER = UnfortunatelyYes - SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin - SHELL_PATH = /usr/local/bin/bash - # as of H06.25/J06.14, we might better use this - #SHELL_PATH = /usr/coreutils/bin/bash -endif -ifneq (,$(findstring MINGW,$(uname_S))) - pathsep = ; - NO_PREAD = YesPlease - NEEDS_CRYPTO_WITH_SSL = YesPlease - NO_LIBGEN_H = YesPlease - NO_POLL = YesPlease - NO_SYMLINK_HEAD = YesPlease - NO_UNIX_SOCKETS = YesPlease - NO_SETENV = YesPlease - NO_UNSETENV = YesPlease - NO_STRCASESTR = YesPlease - NO_STRLCPY = YesPlease - NO_STRTOK_R = YesPlease - NO_FNMATCH = YesPlease - NO_MEMMEM = YesPlease - NEEDS_LIBICONV = YesPlease - OLD_ICONV = YesPlease - NO_STRTOUMAX = YesPlease - NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease - NO_SVN_TESTS = YesPlease - NO_PERL_MAKEMAKER = YesPlease - RUNTIME_PREFIX = YesPlease - NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease - NO_NSEC = YesPlease - USE_WIN32_MMAP = YesPlease - USE_NED_ALLOCATOR = YesPlease - UNRELIABLE_FSTAT = UnfortunatelyYes - OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo - NO_REGEX = YesPlease - NO_PYTHON = YesPlease - BLK_SHA1 = YesPlease - ETAGS_TARGET = ETAGS - NO_INET_PTON = YesPlease - NO_INET_NTOP = YesPlease - NO_POSIX_GOODIES = UnfortunatelyYes - COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32 - COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" - COMPAT_OBJS += compat/mingw.o compat/winansi.o \ - compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/dirent.o - EXTLIBS += -lws2_32 - PTHREAD_LIBS = - X = .exe - SPARSE_FLAGS = -Wno-one-bit-signed-bitfield -ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) - htmldir = doc/git/html/ - prefix = - INSTALL = /bin/install - EXTLIBS += /mingw/lib/libz.a - NO_R_TO_GCC_LINKER = YesPlease - INTERNAL_QSORT = YesPlease - HAVE_LIBCHARSET_H = YesPlease -else - NO_CURL = YesPlease -endif -endif - +include config.mak.uname -include config.mak.autogen -include config.mak @@ -1659,6 +1146,9 @@ endif ifdef NO_D_INO_IN_DIRENT BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT endif +ifdef NO_GECOS_IN_PWENT + BASIC_CFLAGS += -DNO_GECOS_IN_PWENT +endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif @@ -1752,6 +1242,9 @@ endif ifdef NO_SYS_POLL_H BASIC_CFLAGS += -DNO_SYS_POLL_H endif +ifdef NEEDS_SYS_PARAM_H + BASIC_CFLAGS += -DNEEDS_SYS_PARAM_H +endif ifdef NO_INTTYPES_H BASIC_CFLAGS += -DNO_INTTYPES_H endif @@ -1863,6 +1356,9 @@ ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM COMPAT_OBJS += compat/memmem.o endif +ifdef NO_GETPAGESIZE + COMPAT_CFLAGS += -DNO_GETPAGESIZE +endif ifdef INTERNAL_QSORT COMPAT_CFLAGS += -DINTERNAL_QSORT COMPAT_OBJS += compat/qsort.o @@ -1888,6 +1384,10 @@ ifdef HAVE_LIBCHARSET_H EXTLIBS += $(CHARSET_LIB) endif +ifdef HAVE_STRINGS_H + BASIC_CFLAGS += -DHAVE_STRINGS_H +endif + ifdef HAVE_DEV_TTY BASIC_CFLAGS += -DHAVE_DEV_TTY endif @@ -2187,7 +1687,7 @@ endef GIT-SCRIPT-DEFINES: FORCE @FLAGS='$(SCRIPT_DEFINES)'; \ if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \ - echo 1>&2 " * new script parameters"; \ + echo >&2 " * new script parameters"; \ echo "$$FLAGS" >$@; \ fi @@ -2574,7 +2074,7 @@ TRACK_PREFIX = $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\ GIT-PREFIX: FORCE @FLAGS='$(TRACK_PREFIX)'; \ if test x"$$FLAGS" != x"`cat GIT-PREFIX 2>/dev/null`" ; then \ - echo 1>&2 " * new prefix flags"; \ + echo >&2 " * new prefix flags"; \ echo "$$FLAGS" >GIT-PREFIX; \ fi @@ -2583,7 +2083,7 @@ TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):$(USE_GETTEXT_SCHEME) GIT-CFLAGS: FORCE @FLAGS='$(TRACK_CFLAGS)'; \ if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new build flags"; \ + echo >&2 " * new build flags"; \ echo "$$FLAGS" >GIT-CFLAGS; \ fi @@ -2592,7 +2092,7 @@ TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS)) GIT-LDFLAGS: FORCE @FLAGS='$(TRACK_LDFLAGS)'; \ if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new link flags"; \ + echo >&2 " * new link flags"; \ echo "$$FLAGS" >GIT-LDFLAGS; \ fi @@ -2634,18 +2134,6 @@ ifdef GIT_PERF_MAKE_OPTS @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@ endif -### Detect Tck/Tk interpreter path changes -ifndef NO_TCLTK -TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)') - -GIT-GUI-VARS: FORCE - @VARS='$(TRACK_VARS)'; \ - if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \ - echo 1>&2 " * new Tcl/Tk interpreter location"; \ - echo "$$VARS" >$@; \ - fi -endif - ### Detect Python interpreter path changes ifndef NO_PYTHON TRACK_PYTHON = $(subst ','\'',-DPYTHON_PATH='$(PYTHON_PATH_SQ)') @@ -2653,7 +2141,7 @@ TRACK_PYTHON = $(subst ','\'',-DPYTHON_PATH='$(PYTHON_PATH_SQ)') GIT-PYTHON-VARS: FORCE @VARS='$(TRACK_PYTHON)'; \ if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \ - echo 1>&2 " * new Python interpreter location"; \ + echo >&2 " * new Python interpreter location"; \ echo "$$VARS" >$@; \ fi endif @@ -2932,7 +2420,7 @@ ifndef NO_TCLTK $(MAKE) -C gitk-git clean $(MAKE) -C git-gui clean endif - $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS + $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES GIT-PYTHON-VARS .PHONY: all install profile-clean clean strip @@ -1 +1 @@ -Documentation/RelNotes/1.8.1.1.txt
\ No newline at end of file +Documentation/RelNotes/1.8.2.txt
\ No newline at end of file @@ -15,16 +15,34 @@ int is_directory(const char *path) #define MAXDEPTH 5 /* - * Use this to get the real path, i.e. resolve links. If you want an - * absolute path but don't mind links, use absolute_path. + * Return the real path (i.e., absolute path, with symlinks resolved + * and extra slashes removed) equivalent to the specified path. (If + * you want an absolute path but don't mind links, use + * absolute_path().) The return value is a pointer to a static + * buffer. + * + * The input and all intermediate paths must be shorter than MAX_PATH. + * The directory part of path (i.e., everything up to the last + * dir_sep) must denote a valid, existing directory, but the last + * component need not exist. If die_on_error is set, then die with an + * informative error message if there is a problem. Otherwise, return + * NULL on errors (without generating any output). * * If path is our buffer, then return path, as it's already what the * user wants. */ -const char *real_path(const char *path) +static const char *real_path_internal(const char *path, int die_on_error) { static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; + char *retval = NULL; + + /* + * If we have to temporarily chdir(), store the original CWD + * here so that we can chdir() back to it at the end of the + * function: + */ char cwd[1024] = ""; + int buf_index = 1; int depth = MAXDEPTH; @@ -35,11 +53,19 @@ const char *real_path(const char *path) if (path == buf || path == next_buf) return path; - if (!*path) - die("The empty string is not a valid path"); + if (!*path) { + if (die_on_error) + die("The empty string is not a valid path"); + else + goto error_out; + } - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) { + if (die_on_error) + die("Too long path: %.*s", 60, path); + else + goto error_out; + } while (depth--) { if (!is_directory(buf)) { @@ -54,20 +80,36 @@ const char *real_path(const char *path) } if (*buf) { - if (!*cwd && !getcwd(cwd, sizeof(cwd))) - die_errno ("Could not get current working directory"); + if (!*cwd && !getcwd(cwd, sizeof(cwd))) { + if (die_on_error) + die_errno("Could not get current working directory"); + else + goto error_out; + } - if (chdir(buf)) - die_errno ("Could not switch to '%s'", buf); + if (chdir(buf)) { + if (die_on_error) + die_errno("Could not switch to '%s'", buf); + else + goto error_out; + } + } + if (!getcwd(buf, PATH_MAX)) { + if (die_on_error) + die_errno("Could not get current working directory"); + else + goto error_out; } - if (!getcwd(buf, PATH_MAX)) - die_errno ("Could not get current working directory"); if (last_elem) { size_t len = strlen(buf); - if (len + strlen(last_elem) + 2 > PATH_MAX) - die ("Too long path name: '%s/%s'", - buf, last_elem); + if (len + strlen(last_elem) + 2 > PATH_MAX) { + if (die_on_error) + die("Too long path name: '%s/%s'", + buf, last_elem); + else + goto error_out; + } if (len && !is_dir_sep(buf[len-1])) buf[len++] = '/'; strcpy(buf + len, last_elem); @@ -77,10 +119,18 @@ const char *real_path(const char *path) if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { ssize_t len = readlink(buf, next_buf, PATH_MAX); - if (len < 0) - die_errno ("Invalid symlink '%s'", buf); - if (PATH_MAX <= len) - die("symbolic link too long: %s", buf); + if (len < 0) { + if (die_on_error) + die_errno("Invalid symlink '%s'", buf); + else + goto error_out; + } + if (PATH_MAX <= len) { + if (die_on_error) + die("symbolic link too long: %s", buf); + else + goto error_out; + } next_buf[len] = '\0'; buf = next_buf; buf_index = 1 - buf_index; @@ -89,10 +139,23 @@ const char *real_path(const char *path) break; } + retval = buf; +error_out: + free(last_elem); if (*cwd && chdir(cwd)) die_errno ("Could not change back to '%s'", cwd); - return buf; + return retval; +} + +const char *real_path(const char *path) +{ + return real_path_internal(path, 1); +} + +const char *real_path_if_valid(const char *path) +{ + return real_path_internal(path, 0); } static const char *get_pwd_cwd(void) @@ -1,9 +1,10 @@ #include "cache.h" -int advice_push_nonfastforward = 1; +int advice_push_update_rejected = 1; int advice_push_non_ff_current = 1; int advice_push_non_ff_default = 1; int advice_push_non_ff_matching = 1; +int advice_push_already_exists = 1; int advice_status_hints = 1; int advice_commit_before_merge = 1; int advice_resolve_conflict = 1; @@ -14,15 +15,19 @@ static struct { const char *name; int *preference; } advice_config[] = { - { "pushnonfastforward", &advice_push_nonfastforward }, + { "pushupdaterejected", &advice_push_update_rejected }, { "pushnonffcurrent", &advice_push_non_ff_current }, { "pushnonffdefault", &advice_push_non_ff_default }, { "pushnonffmatching", &advice_push_non_ff_matching }, + { "pushalreadyexists", &advice_push_already_exists }, { "statushints", &advice_status_hints }, { "commitbeforemerge", &advice_commit_before_merge }, { "resolveconflict", &advice_resolve_conflict }, { "implicitidentity", &advice_implicit_identity }, { "detachedhead", &advice_detached_head }, + + /* make this an alias for backward compatibility */ + { "pushnonfastforward", &advice_push_update_rejected } }; void advise(const char *advice, ...) @@ -3,10 +3,11 @@ #include "git-compat-util.h" -extern int advice_push_nonfastforward; +extern int advice_push_update_rejected; extern int advice_push_non_ff_current; extern int advice_push_non_ff_default; extern int advice_push_non_ff_matching; +extern int advice_push_already_exists; extern int advice_status_hints; extern int advice_commit_before_merge; extern int advice_resolve_conflict; diff --git a/archive-zip.c b/archive-zip.c index 55f66b4060..d3aef532b7 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -240,7 +240,7 @@ static int write_zip_entry(struct archiver_args *args, (mode & 0111) ? ((mode) << 16) : 0; if (S_ISREG(mode) && args->compression_level != 0 && size > 0) method = 8; - compressed_size = size; + compressed_size = (method == 0) ? size : 0; if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert && size > big_file_threshold) { @@ -313,10 +313,7 @@ static int write_zip_entry(struct archiver_args *args, copy_le16(header.compression_method, method); copy_le16(header.mtime, zip_time); copy_le16(header.mdate, zip_date); - if (flags & ZIP_STREAM) - set_zip_header_data_desc(&header, 0, 0, 0); - else - set_zip_header_data_desc(&header, size, compressed_size, crc); + set_zip_header_data_desc(&header, size, compressed_size, crc); copy_le16(header.filename_length, pathlen); copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE); write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE); @@ -284,7 +284,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, * (reading the file from top to bottom), .gitattribute of the root * directory (again, reading the file from top to bottom) down to the * current directory, and then scan the list backwards to find the first match. - * This is exactly the same as what excluded() does in dir.c to deal with + * This is exactly the same as what is_excluded() does in dir.c to deal with * .gitignore */ @@ -704,7 +704,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem) if (*n == ATTR__UNKNOWN) { debug_set(what, - a->is_macro ? a->u.attr->name : a->u.pattern, + a->is_macro ? a->u.attr->name : a->u.pat.pattern, attr, v); *n = v; rem--; diff --git a/builtin/add.c b/builtin/add.c index e664100c71..075312afcd 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -454,7 +454,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) && !file_exists(pathspec[i])) { if (ignore_missing) { int dtype = DT_UNKNOWN; - if (path_excluded(&check, pathspec[i], -1, &dtype)) + if (is_path_excluded(&check, pathspec[i], -1, &dtype)) dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); } else die(_("pathspec '%s' did not match any files"), diff --git a/builtin/blame.c b/builtin/blame.c index cfae569905..b431ba3209 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -42,6 +42,7 @@ static int blank_boundary; static int incremental; static int xdl_opts; static int abbrev = -1; +static int no_whole_file_rename; static enum date_mode blame_date_mode = DATE_ISO8601; static size_t blame_date_width; @@ -1226,7 +1227,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * The first pass looks for unrenamed path to optimize for * common cases, then we look for renames in the second pass. */ - for (pass = 0; pass < 2; pass++) { + for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { struct origin *(*find)(struct scoreboard *, struct commit *, struct origin *); find = pass ? find_rename : find_origin; @@ -1321,30 +1322,31 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) * Information on commits, used for output. */ struct commit_info { - const char *author; - const char *author_mail; + struct strbuf author; + struct strbuf author_mail; unsigned long author_time; - const char *author_tz; + struct strbuf author_tz; /* filled only when asked for details */ - const char *committer; - const char *committer_mail; + struct strbuf committer; + struct strbuf committer_mail; unsigned long committer_time; - const char *committer_tz; + struct strbuf committer_tz; - const char *summary; + struct strbuf summary; }; /* * Parse author/committer line in the commit object buffer */ static void get_ac_line(const char *inbuf, const char *what, - int person_len, char *person, - int mail_len, char *mail, - unsigned long *time, const char **tz) + struct strbuf *name, struct strbuf *mail, + unsigned long *time, struct strbuf *tz) { - int len, tzlen, maillen; - char *tmp, *endp, *timepos, *mailpos; + struct ident_split ident; + size_t len, maillen, namelen; + char *tmp, *endp; + const char *namebuf, *mailbuf; tmp = strstr(inbuf, what); if (!tmp) @@ -1355,69 +1357,61 @@ static void get_ac_line(const char *inbuf, const char *what, len = strlen(tmp); else len = endp - tmp; - if (person_len <= len) { + + if (split_ident_line(&ident, tmp, len)) { error_out: /* Ugh */ - *tz = "(unknown)"; - strcpy(person, *tz); - strcpy(mail, *tz); + tmp = "(unknown)"; + strbuf_addstr(name, tmp); + strbuf_addstr(mail, tmp); + strbuf_addstr(tz, tmp); *time = 0; return; } - memcpy(person, tmp, len); - tmp = person; - tmp += len; - *tmp = 0; - while (person < tmp && *tmp != ' ') - tmp--; - if (tmp <= person) - goto error_out; - *tz = tmp+1; - tzlen = (person+len)-(tmp+1); + namelen = ident.name_end - ident.name_begin; + namebuf = ident.name_begin; - *tmp = 0; - while (person < tmp && *tmp != ' ') - tmp--; - if (tmp <= person) - goto error_out; - *time = strtoul(tmp, NULL, 10); - timepos = tmp; + maillen = ident.mail_end - ident.mail_begin; + mailbuf = ident.mail_begin; - *tmp = 0; - while (person < tmp && !(*tmp == ' ' && tmp[1] == '<')) - tmp--; - if (tmp <= person) - return; - mailpos = tmp + 1; - *tmp = 0; - maillen = timepos - tmp; - memcpy(mail, mailpos, maillen); + *time = strtoul(ident.date_begin, NULL, 10); - if (!mailmap.nr) - return; - - /* - * mailmap expansion may make the name longer. - * make room by pushing stuff down. - */ - tmp = person + person_len - (tzlen + 1); - memmove(tmp, *tz, tzlen); - tmp[tzlen] = 0; - *tz = tmp; + len = ident.tz_end - ident.tz_begin; + strbuf_add(tz, ident.tz_begin, len); /* * Now, convert both name and e-mail using mailmap */ - if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) { - /* Add a trailing '>' to email, since map_user returns plain emails - Note: It already has '<', since we replace from mail+1 */ - mailpos = memchr(mail, '\0', mail_len); - if (mailpos && mailpos-mail < mail_len - 1) { - *mailpos = '>'; - *(mailpos+1) = '\0'; - } - } + map_user(&mailmap, &mailbuf, &maillen, + &namebuf, &namelen); + + strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf); + strbuf_add(name, namebuf, namelen); +} + +static void commit_info_init(struct commit_info *ci) +{ + + strbuf_init(&ci->author, 0); + strbuf_init(&ci->author_mail, 0); + strbuf_init(&ci->author_tz, 0); + strbuf_init(&ci->committer, 0); + strbuf_init(&ci->committer_mail, 0); + strbuf_init(&ci->committer_tz, 0); + strbuf_init(&ci->summary, 0); +} + +static void commit_info_destroy(struct commit_info *ci) +{ + + strbuf_release(&ci->author); + strbuf_release(&ci->author_mail); + strbuf_release(&ci->author_tz); + strbuf_release(&ci->committer); + strbuf_release(&ci->committer_mail); + strbuf_release(&ci->committer_tz); + strbuf_release(&ci->summary); } static void get_commit_info(struct commit *commit, @@ -1427,11 +1421,8 @@ static void get_commit_info(struct commit *commit, int len; const char *subject, *encoding; char *reencoded, *message; - static char author_name[1024]; - static char author_mail[1024]; - static char committer_name[1024]; - static char committer_mail[1024]; - static char summary_buf[1024]; + + commit_info_init(ret); /* * We've operated without save_commit_buffer, so @@ -1449,11 +1440,8 @@ static void get_commit_info(struct commit *commit, encoding = get_log_output_encoding(); reencoded = logmsg_reencode(commit, encoding); message = reencoded ? reencoded : commit->buffer; - ret->author = author_name; - ret->author_mail = author_mail; get_ac_line(message, "\nauthor ", - sizeof(author_name), author_name, - sizeof(author_mail), author_mail, + &ret->author, &ret->author_mail, &ret->author_time, &ret->author_tz); if (!detailed) { @@ -1461,21 +1449,16 @@ static void get_commit_info(struct commit *commit, return; } - ret->committer = committer_name; - ret->committer_mail = committer_mail; get_ac_line(message, "\ncommitter ", - sizeof(committer_name), committer_name, - sizeof(committer_mail), committer_mail, + &ret->committer, &ret->committer_mail, &ret->committer_time, &ret->committer_tz); - ret->summary = summary_buf; len = find_commit_subject(message, &subject); - if (len && len < sizeof(summary_buf)) { - memcpy(summary_buf, subject, len); - summary_buf[len] = 0; - } else { - sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1)); - } + if (len) + strbuf_add(&ret->summary, subject, len); + else + strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1)); + free(reencoded); } @@ -1504,15 +1487,15 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat) suspect->commit->object.flags |= METAINFO_SHOWN; get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author); - printf("author-mail %s\n", ci.author_mail); + printf("author %s\n", ci.author.buf); + printf("author-mail %s\n", ci.author_mail.buf); printf("author-time %lu\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz); - printf("committer %s\n", ci.committer); - printf("committer-mail %s\n", ci.committer_mail); + printf("author-tz %s\n", ci.author_tz.buf); + printf("committer %s\n", ci.committer.buf); + printf("committer-mail %s\n", ci.committer_mail.buf); printf("committer-time %lu\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz); - printf("summary %s\n", ci.summary); + printf("committer-tz %s\n", ci.committer_tz.buf); + printf("summary %s\n", ci.summary.buf); if (suspect->commit->object.flags & UNINTERESTING) printf("boundary\n"); if (suspect->previous) { @@ -1520,6 +1503,9 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat) printf("previous %s ", sha1_to_hex(prev->commit->object.sha1)); write_name_quoted(prev->path, stdout, '\n'); } + + commit_info_destroy(&ci); + return 1; } @@ -1706,11 +1692,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) if (opt & OUTPUT_ANNOTATE_COMPAT) { const char *name; if (opt & OUTPUT_SHOW_EMAIL) - name = ci.author_mail; + name = ci.author_mail.buf; else - name = ci.author; + name = ci.author.buf; printf("\t(%10s\t%10s\t%d)", name, - format_time(ci.author_time, ci.author_tz, + format_time(ci.author_time, ci.author_tz.buf, show_raw_time), ent->lno + 1 + cnt); } else { @@ -1729,14 +1715,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) const char *name; int pad; if (opt & OUTPUT_SHOW_EMAIL) - name = ci.author_mail; + name = ci.author_mail.buf; else - name = ci.author; + name = ci.author.buf; pad = longest_author - utf8_strwidth(name); printf(" (%s%*s %10s", name, pad, "", format_time(ci.author_time, - ci.author_tz, + ci.author_tz.buf, show_raw_time)); } printf(" %*d) ", @@ -1751,6 +1737,8 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) if (sb->final_buf_size && cp[-1] != '\n') putchar('\n'); + + commit_info_destroy(&ci); } static void output(struct scoreboard *sb, int option) @@ -1875,9 +1863,9 @@ static void find_alignment(struct scoreboard *sb, int *option) suspect->commit->object.flags |= METAINFO_SHOWN; get_commit_info(suspect->commit, &ci, 1); if (*option & OUTPUT_SHOW_EMAIL) - num = utf8_strwidth(ci.author_mail); + num = utf8_strwidth(ci.author_mail.buf); else - num = utf8_strwidth(ci.author); + num = utf8_strwidth(ci.author.buf); if (longest_author < num) longest_author = num; } @@ -1889,6 +1877,8 @@ static void find_alignment(struct scoreboard *sb, int *option) longest_dst_lines = num; if (largest_score < ent_score(sb, e)) largest_score = ent_score(sb, e); + + commit_info_destroy(&ci); } max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); @@ -2403,6 +2393,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); + DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES); save_commit_buffer = 0; dashdash_pos = 0; @@ -2426,6 +2417,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_revision_opt(&revs, &ctx, options, blame_opt_usage); } parse_done: + no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES); + DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES); argc = parse_options_end(&ctx); if (0 < abbrev) diff --git a/builtin/branch.c b/builtin/branch.c index 1ec9c02612..873f624d1c 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -725,7 +725,7 @@ static int edit_branch_description(const char *branch_name) stripspace(&buf, 1); strbuf_addf(&name, "branch.%s.description", branch_name); - status = git_config_set(name.buf, buf.buf); + status = git_config_set(name.buf, buf.len ? buf.buf : NULL); strbuf_release(&name); strbuf_release(&buf); diff --git a/builtin/clean.c b/builtin/clean.c index 69c1cda906..f4b760bf3d 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -10,6 +10,7 @@ #include "cache.h" #include "dir.h" #include "parse-options.h" +#include "refs.h" #include "string-list.h" #include "quote.h" @@ -20,6 +21,12 @@ static const char *const builtin_clean_usage[] = { NULL }; +static const char *msg_remove = N_("Removing %s\n"); +static const char *msg_would_remove = N_("Would remove %s\n"); +static const char *msg_skip_git_dir = N_("Skipping repository %s\n"); +static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n"); +static const char *msg_warn_remove_failed = N_("failed to remove %s"); + static int git_clean_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "clean.requireforce")) @@ -34,11 +41,112 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset) return 0; } +static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, + int dry_run, int quiet, int *dir_gone) +{ + DIR *dir; + struct strbuf quoted = STRBUF_INIT; + struct dirent *e; + int res = 0, ret = 0, gone = 1, original_len = path->len, len, i; + unsigned char submodule_head[20]; + struct string_list dels = STRING_LIST_INIT_DUP; + + *dir_gone = 1; + + if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && + !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) { + if (!quiet) { + quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir), + quoted.buf); + } + + *dir_gone = 0; + return 0; + } + + dir = opendir(path->buf); + if (!dir) { + /* an empty dir could be removed even if it is unreadble */ + res = dry_run ? 0 : rmdir(path->buf); + if (res) { + quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + warning(_(msg_warn_remove_failed), quoted.buf); + *dir_gone = 0; + } + return res; + } + + if (path->buf[original_len - 1] != '/') + strbuf_addch(path, '/'); + + len = path->len; + while ((e = readdir(dir)) != NULL) { + struct stat st; + if (is_dot_or_dotdot(e->d_name)) + continue; + + strbuf_setlen(path, len); + strbuf_addstr(path, e->d_name); + if (lstat(path->buf, &st)) + ; /* fall thru */ + else if (S_ISDIR(st.st_mode)) { + if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone)) + ret = 1; + if (gone) { + quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + string_list_append(&dels, quoted.buf); + } else + *dir_gone = 0; + continue; + } else { + res = dry_run ? 0 : unlink(path->buf); + if (!res) { + quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + string_list_append(&dels, quoted.buf); + } else { + quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + warning(_(msg_warn_remove_failed), quoted.buf); + *dir_gone = 0; + ret = 1; + } + continue; + } + + /* path too long, stat fails, or non-directory still exists */ + *dir_gone = 0; + ret = 1; + break; + } + closedir(dir); + + strbuf_setlen(path, original_len); + + if (*dir_gone) { + res = dry_run ? 0 : rmdir(path->buf); + if (!res) + *dir_gone = 1; + else { + quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + warning(_(msg_warn_remove_failed), quoted.buf); + *dir_gone = 0; + ret = 1; + } + } + + if (!*dir_gone && !quiet) { + for (i = 0; i < dels.nr; i++) + printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string); + } + string_list_clear(&dels, 0); + return ret; +} + int cmd_clean(int argc, const char **argv, const char *prefix) { - int i; - int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; - int ignored_only = 0, config_set = 0, errors = 0; + int i, res; + int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0; + int ignored_only = 0, config_set = 0, errors = 0, gone = 1; int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf directory = STRBUF_INIT; struct dir_struct dir; @@ -49,7 +157,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) char *seen = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("do not print names of files removed")), - OPT__DRY_RUN(&show_only, N_("dry run")), + OPT__DRY_RUN(&dry_run, N_("dry run")), OPT__FORCE(&force, N_("force")), OPT_BOOLEAN('d', NULL, &remove_directories, N_("remove whole directories")), @@ -77,7 +185,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (ignored && ignored_only) die(_("-x and -X cannot be used together")); - if (!show_only && !force) { + if (!dry_run && !force) { if (config_set) die(_("clean.requireForce set to true and neither -n nor -f given; " "refusing to clean")); @@ -149,38 +257,26 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (S_ISDIR(st.st_mode)) { strbuf_addstr(&directory, ent->name); - qname = quote_path_relative(directory.buf, directory.len, &buf, prefix); - if (show_only && (remove_directories || - (matches == MATCHED_EXACTLY))) { - printf(_("Would remove %s\n"), qname); - } else if (remove_directories || - (matches == MATCHED_EXACTLY)) { - if (!quiet) - printf(_("Removing %s\n"), qname); - if (remove_dir_recursively(&directory, - rm_flags) != 0) { - warning(_("failed to remove %s"), qname); + if (remove_directories || (matches == MATCHED_EXACTLY)) { + if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone)) errors++; + if (gone && !quiet) { + qname = quote_path_relative(directory.buf, directory.len, &buf, prefix); + printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); } - } else if (show_only) { - printf(_("Would not remove %s\n"), qname); - } else { - printf(_("Not removing %s\n"), qname); } strbuf_reset(&directory); } else { if (pathspec && !matches) continue; - qname = quote_path_relative(ent->name, -1, &buf, prefix); - if (show_only) { - printf(_("Would remove %s\n"), qname); - continue; - } else if (!quiet) { - printf(_("Removing %s\n"), qname); - } - if (unlink(ent->name) != 0) { - warning(_("failed to remove %s"), qname); + res = dry_run ? 0 : unlink(ent->name); + if (res) { + qname = quote_path_relative(ent->name, -1, &buf, prefix); + warning(_(msg_warn_remove_failed), qname); errors++; + } else if (!quiet) { + qname = quote_path_relative(ent->name, -1, &buf, prefix); + printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); } } } diff --git a/builtin/clone.c b/builtin/clone.c index 8d23a62e8a..36ec99db3f 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -704,6 +704,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_origin) die(_("--bare and --origin %s options are incompatible."), option_origin); + if (real_git_dir) + die(_("--bare and --separate-git-dir are incompatible.")); option_no_checkout = 1; } diff --git a/builtin/commit.c b/builtin/commit.c index d6dd3df8b1..7c2a3d48b4 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -103,7 +103,7 @@ static enum { CLEANUP_NONE, CLEANUP_ALL } cleanup_mode; -static char *cleanup_arg; +static const char *cleanup_arg; static enum commit_whence whence; static int use_editor = 1, include_status = 1; @@ -1320,6 +1320,8 @@ static int git_commit_config(const char *k, const char *v, void *cb) include_status = git_config_bool(k, v); return 0; } + if (!strcmp(k, "commit.cleanup")) + return git_config_string(&cleanup_arg, k, v); status = git_gpg_config(k, v, NULL); if (status) diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 12220ad8da..77dffd1ce3 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -474,18 +474,21 @@ static void handle_tag(const char *name, struct tag *tag) (int)message_size, (int)message_size, message ? message : ""); } -static void get_tags_and_duplicates(struct object_array *pending, +static void get_tags_and_duplicates(struct rev_cmdline_info *info, struct string_list *extra_refs) { struct tag *tag; int i; - for (i = 0; i < pending->nr; i++) { - struct object_array_entry *e = pending->objects + i; + for (i = 0; i < info->nr; i++) { + struct rev_cmdline_entry *e = info->rev + i; unsigned char sha1[20]; - struct commit *commit = commit; + struct commit *commit; char *full_name; + if (e->flags & UNINTERESTING) + continue; + if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1) continue; @@ -523,10 +526,14 @@ static void get_tags_and_duplicates(struct object_array *pending, typename(e->item->type)); continue; } - if (commit->util) - /* more than one name for the same object */ + + /* + * This ref will not be updated through a commit, lets make + * sure it gets properly updated eventually. + */ + if (commit->util || commit->object.flags & SHOWN) string_list_append(extra_refs, full_name)->util = commit; - else + if (!commit->util) commit->util = full_name; } } @@ -614,6 +621,10 @@ static void import_marks(char *input_file) if (object->flags & SHOWN) error("Object %s already has a mark", sha1_to_hex(sha1)); + if (object->type != OBJ_COMMIT) + /* only commits */ + continue; + mark_object(object, mark); if (last_idnum < mark) last_idnum = mark; @@ -677,7 +688,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (import_filename && revs.prune_data.nr) full_tree = 1; - get_tags_and_duplicates(&revs.pending, &extra_refs); + get_tags_and_duplicates(&revs.cmdline, &extra_refs); if (prepare_revision_walk(&revs)) die("revision walk setup failed"); diff --git a/builtin/log.c b/builtin/log.c index e7b7db1cac..8f0b2e84fe 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -22,6 +22,7 @@ #include "branch.h" #include "streaming.h" #include "version.h" +#include "mailmap.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@ -30,6 +31,7 @@ static int default_abbrev_commit; static int default_show_root = 1; static int decoration_style; static int decoration_given; +static int use_mailmap_config; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; @@ -94,16 +96,18 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, struct rev_info *rev, struct setup_revision_opt *opt) { struct userformat_want w; - int quiet = 0, source = 0; + int quiet = 0, source = 0, mailmap = 0; const struct option builtin_log_options[] = { OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")), OPT_BOOLEAN(0, "source", &source, N_("show source")), + OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")), { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"), PARSE_OPT_OPTARG, decorate_callback}, OPT_END() }; + mailmap = use_mailmap_config; argc = parse_options(argc, argv, prefix, builtin_log_options, builtin_log_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | @@ -136,6 +140,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, if (source) rev->show_source = 1; + if (mailmap) { + rev->mailmap = xcalloc(1, sizeof(struct string_list)); + read_mailmap(rev->mailmap, NULL); + } + if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) { /* * "log --pretty=raw" is special; ignore UI oriented @@ -351,6 +360,11 @@ static int git_log_config(const char *var, const char *value, void *cb) } if (!prefixcmp(var, "color.decorate.")) return parse_decorate_color_config(var, 15, value); + if (!strcmp(var, "log.mailmap")) { + use_mailmap_config = git_config_bool(var, value); + return 0; + } + if (grep_config(var, value, cb) < 0) return -1; return git_diff_ui_config(var, value, cb); @@ -678,7 +692,7 @@ static int reopen_stdout(struct commit *commit, const char *subject, struct rev_info *rev, int quiet) { struct strbuf filename = STRBUF_INIT; - int suffix_len = strlen(fmt_patch_suffix) + 1; + int suffix_len = strlen(rev->patch_suffix) + 1; if (output_directory) { strbuf_addstr(&filename, output_directory); @@ -689,7 +703,12 @@ static int reopen_stdout(struct commit *commit, const char *subject, strbuf_addch(&filename, '/'); } - get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename); + if (rev->numbered_files) + strbuf_addf(&filename, "%d", rev->nr); + else if (commit) + fmt_output_commit(&filename, commit, rev); + else + fmt_output_subject(&filename, subject, rev); if (!quiet) fprintf(realstdout, "%s\n", filename.buf + outdir_offset); @@ -773,7 +792,6 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name) } static void make_cover_letter(struct rev_info *rev, int use_stdout, - int numbered, int numbered_files, struct commit *origin, int nr, struct commit **list, struct commit *head, const char *branch_name, @@ -796,7 +814,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, committer = git_committer_info(0); if (!use_stdout && - reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet)) + reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) return; log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, @@ -1016,8 +1034,9 @@ static char *find_branch_name(struct rev_info *rev) { int i, positive = -1; unsigned char branch_sha1[20]; - struct strbuf buf = STRBUF_INIT; - const char *branch; + const unsigned char *tip_sha1; + const char *ref; + char *full_ref, *branch = NULL; for (i = 0; i < rev->cmdline.nr; i++) { if (rev->cmdline.rev[i].flags & UNINTERESTING) @@ -1027,18 +1046,27 @@ static char *find_branch_name(struct rev_info *rev) else return NULL; } - if (positive < 0) + if (0 <= positive) { + ref = rev->cmdline.rev[positive].name; + tip_sha1 = rev->cmdline.rev[positive].item->sha1; + } else if (!rev->cmdline.nr && rev->pending.nr == 1 && + !strcmp(rev->pending.objects[0].name, "HEAD")) { + /* + * No actual ref from command line, but "HEAD" from + * rev->def was added in setup_revisions() + * e.g. format-patch --cover-letter -12 + */ + ref = "HEAD"; + tip_sha1 = rev->pending.objects[0].item->sha1; + } else { return NULL; - strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name); - branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL); - if (!branch || - prefixcmp(branch, "refs/heads/") || - hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1)) - branch = NULL; - strbuf_release(&buf); - if (branch) - return xstrdup(rev->cmdline.rev[positive].name); - return NULL; + } + if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) && + !prefixcmp(full_ref, "refs/heads/") && + !hashcmp(tip_sha1, branch_sha1)) + branch = xstrdup(full_ref + strlen("refs/heads/")); + free(full_ref); + return branch; } int cmd_format_patch(int argc, const char **argv, const char *prefix) @@ -1050,7 +1078,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int nr = 0, total, i; int use_stdout = 0; int start_number = -1; - int numbered_files = 0; /* _just_ numbers */ + int just_numbers = 0; int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; @@ -1062,6 +1090,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; int quiet = 0; + int reroll_count = -1; char *branch_name = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, @@ -1075,12 +1104,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("print patches to standard out")), OPT_BOOLEAN(0, "cover-letter", &cover_letter, N_("generate a cover letter")), - OPT_BOOLEAN(0, "numbered-files", &numbered_files, + OPT_BOOLEAN(0, "numbered-files", &just_numbers, 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, N_("start numbering patches at <n> instead of 1")), + OPT_INTEGER('v', "reroll-count", &reroll_count, + N_("mark the series as Nth re-roll")), { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"), N_("Use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback }, @@ -1154,6 +1185,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); + if (0 < reroll_count) { + struct strbuf sprefix = STRBUF_INIT; + strbuf_addf(&sprefix, "%s v%d", + rev.subject_prefix, reroll_count); + rev.reroll_count = reroll_count; + rev.subject_prefix = strbuf_detach(&sprefix, NULL); + } + if (do_signoff) { const char *committer; const char *endpos; @@ -1344,12 +1383,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) const char *msgid = clean_message_id(in_reply_to); string_list_append(rev.ref_message_ids, msgid); } - rev.numbered_files = numbered_files; + rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); - make_cover_letter(&rev, use_stdout, numbered, numbered_files, + make_cover_letter(&rev, use_stdout, origin, nr, list, head, branch_name, quiet); total++; start_number--; @@ -1396,7 +1435,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } if (!use_stdout && - reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet)) + reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b5434af0c8..373c573449 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -203,7 +203,7 @@ static void show_ru_info(void) static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce) { int dtype = ce_to_dtype(ce); - return path_excluded(check, ce->name, ce_namelen(ce), &dtype); + return is_path_excluded(check, ce->name, ce_namelen(ce), &dtype); } static void show_files(struct dir_struct *dir) @@ -337,7 +337,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) matchbuf[0] = prefix; matchbuf[1] = NULL; init_pathspec(&pathspec, matchbuf); - pathspec.items[0].use_wildcard = 0; + pathspec.items[0].nowildcard_len = pathspec.items[0].len; } else init_pathspec(&pathspec, NULL); if (read_tree(tree, 1, &pathspec)) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 235c17cc01..fb76e38d84 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -168,7 +168,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) init_pathspec(&pathspec, get_pathspec(prefix, argv + 1)); for (i = 0; i < pathspec.nr; i++) - pathspec.items[i].use_wildcard = 0; + pathspec.items[i].nowildcard_len = pathspec.items[i].len; pathspec.has_wildcard = 0; tree = parse_tree_indirect(sha1); if (!tree) diff --git a/builtin/merge-index.c b/builtin/merge-index.c index 2338832587..be5e514324 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -42,7 +42,7 @@ static int merge_entry(int pos, const char *path) return found; } -static void merge_file(const char *path) +static void merge_one_path(const char *path) { int pos = cache_name_pos(path, strlen(path)); @@ -102,7 +102,7 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix) } die("git merge-index: unknown option %s", arg); } - merge_file(arg); + merge_one_path(arg); } if (err && !quiet) die("merge program failed"); diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 897a563bc6..e0d0b7d28b 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -3,17 +3,15 @@ #include "xdiff-interface.h" #include "blob.h" #include "exec_cmd.h" -#include "merge-file.h" +#include "merge-blobs.h" static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>"; -static int resolve_directories = 1; struct merge_list { struct merge_list *next; struct merge_list *link; /* other stages for this object */ - unsigned int stage : 2, - flags : 30; + unsigned int stage : 2; unsigned int mode; const char *path; struct blob *blob; @@ -27,7 +25,7 @@ static void add_merge_entry(struct merge_list *entry) merge_result_end = &entry->next; } -static void merge_trees(struct tree_desc t[3], const char *base); +static void merge_trees_recursive(struct tree_desc t[3], const char *base, int df_conflict); static const char *explanation(struct merge_list *entry) { @@ -76,7 +74,7 @@ static void *result(struct merge_list *entry, unsigned long *size) their = NULL; if (entry) their = entry->blob; - return merge_file(path, base, our, their, size); + return merge_blobs(path, base, our, their, size); } static void *origin(struct merge_list *entry, unsigned long *size) @@ -174,17 +172,17 @@ static char *traverse_path(const struct traverse_info *info, const struct name_e return make_traverse_path(path, info, n); } -static void resolve(const struct traverse_info *info, struct name_entry *branch1, struct name_entry *result) +static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result) { struct merge_list *orig, *final; const char *path; - /* If it's already branch1, don't bother showing it */ - if (!branch1) + /* If it's already ours, don't bother showing it */ + if (!ours) return; path = traverse_path(info, result); - orig = create_entry(2, branch1->mode, branch1->sha1, path); + orig = create_entry(2, ours->mode, ours->sha1, path); final = create_entry(0, result->mode, result->sha1, path); final->link = orig; @@ -192,34 +190,35 @@ static void resolve(const struct traverse_info *info, struct name_entry *branch1 add_merge_entry(final); } -static int unresolved_directory(const struct traverse_info *info, struct name_entry n[3]) +static void unresolved_directory(const struct traverse_info *info, struct name_entry n[3], + int df_conflict) { char *newbase; struct name_entry *p; struct tree_desc t[3]; void *buf0, *buf1, *buf2; - if (!resolve_directories) - return 0; - p = n; - if (!p->mode) { - p++; - if (!p->mode) - p++; + for (p = n; p < n + 3; p++) { + if (p->mode && S_ISDIR(p->mode)) + break; } - if (!S_ISDIR(p->mode)) - return 0; + if (n + 3 <= p) + return; /* there is no tree here */ + newbase = traverse_path(info, p); - buf0 = fill_tree_descriptor(t+0, n[0].sha1); - buf1 = fill_tree_descriptor(t+1, n[1].sha1); - buf2 = fill_tree_descriptor(t+2, n[2].sha1); - merge_trees(t, newbase); + +#define ENTRY_SHA1(e) (((e)->mode && S_ISDIR((e)->mode)) ? (e)->sha1 : NULL) + buf0 = fill_tree_descriptor(t+0, ENTRY_SHA1(n + 0)); + buf1 = fill_tree_descriptor(t+1, ENTRY_SHA1(n + 1)); + buf2 = fill_tree_descriptor(t+2, ENTRY_SHA1(n + 2)); +#undef ENTRY_SHA1 + + merge_trees_recursive(t, newbase, df_conflict); free(buf0); free(buf1); free(buf2); free(newbase); - return 1; } @@ -242,18 +241,26 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info static void unresolved(const struct traverse_info *info, struct name_entry n[3]) { struct merge_list *entry = NULL; + int i; + unsigned dirmask = 0, mask = 0; + + for (i = 0; i < 3; i++) { + mask |= (1 << 1); + if (n[i].mode && S_ISDIR(n[i].mode)) + dirmask |= (1 << i); + } - if (unresolved_directory(info, n)) + unresolved_directory(info, n, dirmask && (dirmask != mask)); + + if (dirmask == mask) return; - /* - * Do them in reverse order so that the resulting link - * list has the stages in order - link_entry adds new - * links at the front. - */ - entry = link_entry(3, info, n + 2, entry); - entry = link_entry(2, info, n + 1, entry); - entry = link_entry(1, info, n + 0, entry); + if (n[2].mode && !S_ISDIR(n[2].mode)) + entry = link_entry(3, info, n + 2, entry); + if (n[1].mode && !S_ISDIR(n[1].mode)) + entry = link_entry(2, info, n + 1, entry); + if (n[0].mode && !S_ISDIR(n[0].mode)) + entry = link_entry(1, info, n + 0, entry); add_merge_entry(entry); } @@ -292,20 +299,29 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s /* Same in both? */ if (same_entry(entry+1, entry+2)) { if (entry[0].sha1) { + /* Modified identically */ resolve(info, NULL, entry+1); return mask; } + /* "Both added the same" is left unresolved */ } if (same_entry(entry+0, entry+1)) { if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) { + /* We did not touch, they modified -- take theirs */ resolve(info, entry+1, entry+2); return mask; } + /* + * If we did not touch a directory but they made it + * into a file, we fall through and unresolved() + * recurses down. Likewise for the opposite case. + */ } if (same_entry(entry+0, entry+2)) { if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) { + /* We modified, they did not touch -- take ours */ resolve(info, NULL, entry+1); return mask; } @@ -315,15 +331,21 @@ static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, s return mask; } -static void merge_trees(struct tree_desc t[3], const char *base) +static void merge_trees_recursive(struct tree_desc t[3], const char *base, int df_conflict) { struct traverse_info info; setup_traverse_info(&info, base); + info.data = &df_conflict; info.fn = threeway_callback; traverse_trees(3, t, &info); } +static void merge_trees(struct tree_desc t[3], const char *base) +{ + merge_trees_recursive(t, base, 0); +} + static void *get_tree_descriptor(struct tree_desc *desc, const char *rev) { unsigned char sha1[20]; diff --git a/builtin/push.c b/builtin/push.c index db9ba30b08..8491e431e4 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -220,31 +220,42 @@ static const char message_advice_checkout_pull_push[] = "(e.g. 'git pull') before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); +static const char message_advice_ref_already_exists[] = + N_("Updates were rejected because the destination reference already exists\n" + "in the remote."); + static void advise_pull_before_push(void) { - if (!advice_push_non_ff_current || !advice_push_nonfastforward) + if (!advice_push_non_ff_current || !advice_push_update_rejected) return; advise(_(message_advice_pull_before_push)); } static void advise_use_upstream(void) { - if (!advice_push_non_ff_default || !advice_push_nonfastforward) + if (!advice_push_non_ff_default || !advice_push_update_rejected) return; advise(_(message_advice_use_upstream)); } static void advise_checkout_pull_push(void) { - if (!advice_push_non_ff_matching || !advice_push_nonfastforward) + if (!advice_push_non_ff_matching || !advice_push_update_rejected) return; advise(_(message_advice_checkout_pull_push)); } +static void advise_ref_already_exists(void) +{ + if (!advice_push_already_exists || !advice_push_update_rejected) + return; + advise(_(message_advice_ref_already_exists)); +} + static int push_with_options(struct transport *transport, int flags) { int err; - int nonfastforward; + unsigned int reject_reasons; transport_set_verbosity(transport, verbosity, progress); @@ -257,7 +268,7 @@ static int push_with_options(struct transport *transport, int flags) if (verbosity > 0) fprintf(stderr, _("Pushing to %s\n"), transport->url); err = transport_push(transport, refspec_nr, refspec, flags, - &nonfastforward); + &reject_reasons); if (err != 0) error(_("failed to push some refs to '%s'"), transport->url); @@ -265,18 +276,15 @@ static int push_with_options(struct transport *transport, int flags) if (!err) return 0; - switch (nonfastforward) { - default: - break; - case NON_FF_HEAD: + if (reject_reasons & REJECT_NON_FF_HEAD) { advise_pull_before_push(); - break; - case NON_FF_OTHER: + } else if (reject_reasons & REJECT_NON_FF_OTHER) { if (default_matching_used) advise_use_upstream(); else advise_checkout_pull_push(); - break; + } else if (reject_reasons & REJECT_ALREADY_EXISTS) { + advise_ref_already_exists(); } return 1; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index d34201372d..f849e0a4a0 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -44,6 +44,11 @@ static void print_helper_status(struct ref *ref) msg = "non-fast forward"; break; + case REF_STATUS_REJECT_ALREADY_EXISTS: + res = "error"; + msg = "already exists"; + break; + case REF_STATUS_REJECT_NODELETE: case REF_STATUS_REMOTE_REJECT: res = "error"; @@ -85,7 +90,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) int send_all = 0; const char *receivepack = "git-receive-pack"; int flags; - int nonfastforward = 0; + unsigned int reject_reasons; int progress = -1; argv++; @@ -223,7 +228,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) ret |= finish_connect(conn); if (!helper_status) - transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward); + transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_reasons); if (!args.dry_run && remote) { struct ref *ref; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 83605143ac..240bff3efa 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -36,52 +36,28 @@ static void insert_one_record(struct shortlog *log, const char *dot3 = log->common_repo_prefix; char *buffer, *p; struct string_list_item *item; - char namebuf[1024]; - char emailbuf[1024]; - size_t len; + const char *mailbuf, *namebuf; + size_t namelen, maillen; const char *eol; - const char *boemail, *eoemail; struct strbuf subject = STRBUF_INIT; + struct strbuf namemailbuf = STRBUF_INIT; + struct ident_split ident; - boemail = strchr(author, '<'); - if (!boemail) - return; - eoemail = strchr(boemail, '>'); - if (!eoemail) + if (split_ident_line(&ident, author, strlen(author))) return; - /* copy author name to namebuf, to support matching on both name and email */ - memcpy(namebuf, author, boemail - author); - len = boemail - author; - while (len > 0 && isspace(namebuf[len-1])) - len--; - namebuf[len] = 0; - - /* copy email name to emailbuf, to allow email replacement as well */ - memcpy(emailbuf, boemail+1, eoemail - boemail); - emailbuf[eoemail - boemail - 1] = 0; - - if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) { - while (author < boemail && isspace(*author)) - author++; - for (len = 0; - len < sizeof(namebuf) - 1 && author + len < boemail; - len++) - namebuf[len] = author[len]; - while (0 < len && isspace(namebuf[len-1])) - len--; - namebuf[len] = '\0'; - } - else - len = strlen(namebuf); + namebuf = ident.name_begin; + mailbuf = ident.mail_begin; + namelen = ident.name_end - ident.name_begin; + maillen = ident.mail_end - ident.mail_begin; - if (log->email) { - size_t room = sizeof(namebuf) - len - 1; - int maillen = strlen(emailbuf); - snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf); - } + map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen); + strbuf_add(&namemailbuf, namebuf, namelen); + + if (log->email) + strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf); - item = string_list_insert(&log->list, namebuf); + item = string_list_insert(&log->list, namemailbuf.buf); if (item->util == NULL) item->util = xcalloc(1, sizeof(struct string_list)); @@ -362,6 +362,7 @@ static inline enum object_type object_type(unsigned int mode) #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF" #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF" #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE" +#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS" /* * Repository-local GIT_* environment variables @@ -473,6 +474,8 @@ extern int index_name_is_other(const struct index_state *, const char *, int); extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); +#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ + struct pathspec { const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ int nr; @@ -482,7 +485,8 @@ struct pathspec { struct pathspec_item { const char *match; int len; - unsigned int use_wildcard:1; + int nowildcard_len; + int flags; } *items; }; @@ -490,6 +494,8 @@ extern int init_pathspec(struct pathspec *, const char **); extern void free_pathspec(struct pathspec *); extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); +extern int limit_pathspec_to_literal(void); + #define HASH_WRITE_OBJECT 1 #define HASH_FORMAT_CHECK 2 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); @@ -714,10 +720,11 @@ static inline int is_absolute_path(const char *path) } int is_directory(const char *); const char *real_path(const char *path); +const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); const char *relative_path(const char *abs, const char *base); int normalize_path_copy(char *dst, const char *src); -int longest_ancestor_length(const char *path, const char *prefix_list); +int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); int offset_1st_component(const char *path); @@ -999,14 +1006,19 @@ struct ref { unsigned char old_sha1[20]; unsigned char new_sha1[20]; char *symref; - unsigned int force:1, + unsigned int + force:1, + requires_force:1, merge:1, nonfastforward:1, + not_forwardable:1, + update:1, deletion:1; enum { REF_STATUS_NONE = 0, REF_STATUS_OK, REF_STATUS_REJECT_NONFASTFORWARD, + REF_STATUS_REJECT_ALREADY_EXISTS, REF_STATUS_REJECT_NODELETE, REF_STATUS_UPTODATE, REF_STATUS_REMOTE_REJECT, @@ -1136,6 +1148,9 @@ extern int check_repository_format_version(const char *var, const char *value, v extern int git_env_bool(const char *, int); extern int git_config_system(void); extern int config_error_nonbool(const char *); +#ifdef __GNUC__ +#define config_error_nonbool(s) (config_error_nonbool(s), -1) +#endif extern const char *get_log_output_encoding(void); extern const char *get_commit_output_encoding(void); @@ -1155,6 +1170,7 @@ extern int author_ident_sufficiently_given(void); extern const char *git_commit_encoding; extern const char *git_log_output_encoding; extern const char *git_mailmap_file; +extern const char *git_mailmap_blob; /* IO helper functions */ extern void maybe_flush_or_die(FILE *, const char *); @@ -89,6 +89,8 @@ struct pretty_print_context { char *notes_message; struct reflog_walk_info *reflog_info; const char *output_encoding; + struct string_list *mailmap; + int color; }; struct userformat_want { diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c index b8b7dc2543..5ef0685135 100644 --- a/compat/fnmatch/fnmatch.c +++ b/compat/fnmatch/fnmatch.c @@ -55,7 +55,8 @@ program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ -#if defined _LIBC || !defined __GNU_LIBRARY__ +#if defined NO_FNMATCH || defined NO_FNMATCH_CASEFOLD || \ + defined _LIBC || !defined __GNU_LIBRARY__ # if defined STDC_HEADERS || !defined isascii @@ -839,6 +839,8 @@ static int git_default_mailmap_config(const char *var, const char *value) { if (!strcmp(var, "mailmap.file")) return git_config_string(&git_mailmap_file, var, value); + if (!strcmp(var, "mailmap.blob")) + return git_config_string(&git_mailmap_blob, var, value); /* Add other config variables here and to Documentation/config.txt. */ return 0; @@ -1660,6 +1662,7 @@ int git_config_rename_section(const char *old_name, const char *new_name) * Call this to report error for your variable that should not * get a boolean value (i.e. "[my] var" means "true"). */ +#undef config_error_nonbool int config_error_nonbool(const char *var) { return error("Missing value for '%s'", var); diff --git a/config.mak.uname b/config.mak.uname new file mode 100644 index 0000000000..bea34f0511 --- /dev/null +++ b/config.mak.uname @@ -0,0 +1,539 @@ +# Platform specific Makefile tweaks based on uname detection + +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') +uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') +uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') +uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') +uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') +uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') + +ifdef MSVC + # avoid the MingW and Cygwin configuration sections + uname_S := Windows + uname_O := Windows +endif + +# We choose to avoid "if .. else if .. else .. endif endif" +# because maintaining the nesting to match is a pain. If +# we had "elif" things would have been much nicer... + +ifeq ($(uname_M),x86_64) + XDL_FAST_HASH = YesPlease +endif +ifeq ($(uname_S),OSF1) + # Need this for u_short definitions et al + BASIC_CFLAGS += -D_OSF_SOURCE + SOCKLEN_T = int + NO_STRTOULL = YesPlease + NO_NSEC = YesPlease +endif +ifeq ($(uname_S),Linux) + NO_STRLCPY = YesPlease + NO_MKSTEMPS = YesPlease + HAVE_PATHS_H = YesPlease + LIBC_CONTAINS_LIBINTL = YesPlease + HAVE_DEV_TTY = YesPlease +endif +ifeq ($(uname_S),GNU/kFreeBSD) + NO_STRLCPY = YesPlease + NO_MKSTEMPS = YesPlease + HAVE_PATHS_H = YesPlease + DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease + LIBC_CONTAINS_LIBINTL = YesPlease +endif +ifeq ($(uname_S),UnixWare) + CC = cc + NEEDS_SOCKET = YesPlease + NEEDS_NSL = YesPlease + NEEDS_SSL_WITH_CRYPTO = YesPlease + NEEDS_LIBICONV = YesPlease + SHELL_PATH = /usr/local/bin/bash + NO_IPV6 = YesPlease + NO_HSTRERROR = YesPlease + NO_MKSTEMPS = YesPlease + BASIC_CFLAGS += -Kthread + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib + INSTALL = ginstall + TAR = gtar + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease +endif +ifeq ($(uname_S),SCO_SV) + ifeq ($(uname_R),3.2) + CFLAGS = -O2 + endif + ifeq ($(uname_R),5) + CC = cc + BASIC_CFLAGS += -Kthread + endif + NEEDS_SOCKET = YesPlease + NEEDS_NSL = YesPlease + NEEDS_SSL_WITH_CRYPTO = YesPlease + NEEDS_LIBICONV = YesPlease + SHELL_PATH = /usr/bin/bash + NO_IPV6 = YesPlease + NO_HSTRERROR = YesPlease + NO_MKSTEMPS = YesPlease + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + INSTALL = ginstall + TAR = gtar +endif +ifeq ($(uname_S),Darwin) + NEEDS_CRYPTO_WITH_SSL = YesPlease + NEEDS_SSL_WITH_CRYPTO = YesPlease + NEEDS_LIBICONV = YesPlease + ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2) + OLD_ICONV = UnfortunatelyYes + endif + ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2) + NO_STRLCPY = YesPlease + endif + NO_MEMMEM = YesPlease + USE_ST_TIMESPEC = YesPlease + HAVE_DEV_TTY = YesPlease + COMPAT_OBJS += compat/precompose_utf8.o + BASIC_CFLAGS += -DPRECOMPOSE_UNICODE +endif +ifeq ($(uname_S),SunOS) + NEEDS_SOCKET = YesPlease + NEEDS_NSL = YesPlease + SHELL_PATH = /bin/bash + SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + NO_REGEX = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + NO_MSGFMT_EXTENDED_OPTIONS = YesPlease + HAVE_DEV_TTY = YesPlease + ifeq ($(uname_R),5.6) + SOCKLEN_T = int + NO_HSTRERROR = YesPlease + NO_IPV6 = YesPlease + NO_SOCKADDR_STORAGE = YesPlease + NO_UNSETENV = YesPlease + NO_SETENV = YesPlease + NO_STRLCPY = YesPlease + NO_STRTOUMAX = YesPlease + GIT_TEST_CMP = cmp + endif + ifeq ($(uname_R),5.7) + NEEDS_RESOLV = YesPlease + NO_IPV6 = YesPlease + NO_SOCKADDR_STORAGE = YesPlease + NO_UNSETENV = YesPlease + NO_SETENV = YesPlease + NO_STRLCPY = YesPlease + NO_STRTOUMAX = YesPlease + GIT_TEST_CMP = cmp + endif + ifeq ($(uname_R),5.8) + NO_UNSETENV = YesPlease + NO_SETENV = YesPlease + NO_STRTOUMAX = YesPlease + GIT_TEST_CMP = cmp + endif + ifeq ($(uname_R),5.9) + NO_UNSETENV = YesPlease + NO_SETENV = YesPlease + NO_STRTOUMAX = YesPlease + GIT_TEST_CMP = cmp + endif + INSTALL = /usr/ucb/install + TAR = gtar + BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H +endif +ifeq ($(uname_O),Cygwin) + ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4) + NO_D_TYPE_IN_DIRENT = YesPlease + NO_D_INO_IN_DIRENT = YesPlease + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease + NO_SYMLINK_HEAD = YesPlease + NO_IPV6 = YesPlease + OLD_ICONV = UnfortunatelyYes + CYGWIN_V15_WIN32API = YesPlease + endif + NO_THREAD_SAFE_PREAD = YesPlease + NEEDS_LIBICONV = YesPlease + NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes + NO_TRUSTABLE_FILEMODE = UnfortunatelyYes + NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + # There are conflicting reports about this. + # On some boxes NO_MMAP is needed, and not so elsewhere. + # Try commenting this out if you suspect MMAP is more efficient + NO_MMAP = YesPlease + X = .exe + COMPAT_OBJS += compat/cygwin.o + UNRELIABLE_FSTAT = UnfortunatelyYes + SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield +endif +ifeq ($(uname_S),FreeBSD) + NEEDS_LIBICONV = YesPlease + OLD_ICONV = YesPlease + NO_MEMMEM = YesPlease + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib + DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease + USE_ST_TIMESPEC = YesPlease + ifeq ($(shell expr "$(uname_R)" : '4\.'),2) + PTHREAD_LIBS = -pthread + NO_UINTMAX_T = YesPlease + NO_STRTOUMAX = YesPlease + endif + PYTHON_PATH = /usr/local/bin/python + HAVE_PATHS_H = YesPlease +endif +ifeq ($(uname_S),OpenBSD) + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + USE_ST_TIMESPEC = YesPlease + NEEDS_LIBICONV = YesPlease + BASIC_CFLAGS += -I/usr/local/include + BASIC_LDFLAGS += -L/usr/local/lib + HAVE_PATHS_H = YesPlease +endif +ifeq ($(uname_S),NetBSD) + ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) + NEEDS_LIBICONV = YesPlease + endif + BASIC_CFLAGS += -I/usr/pkg/include + BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib + USE_ST_TIMESPEC = YesPlease + NO_MKSTEMPS = YesPlease + HAVE_PATHS_H = YesPlease +endif +ifeq ($(uname_S),AIX) + DEFAULT_PAGER = more + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + NO_STRLCPY = YesPlease + NO_NSEC = YesPlease + FREAD_READS_DIRECTORIES = UnfortunatelyYes + INTERNAL_QSORT = UnfortunatelyYes + NEEDS_LIBICONV = YesPlease + BASIC_CFLAGS += -D_LARGE_FILES + ifeq ($(shell expr "$(uname_V)" : '[1234]'),1) + NO_PTHREADS = YesPlease + else + PTHREAD_LIBS = -lpthread + endif + ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3) + INLINE = '' + endif + GIT_TEST_CMP = cmp +endif +ifeq ($(uname_S),GNU) + # GNU/Hurd + NO_STRLCPY = YesPlease + NO_MKSTEMPS = YesPlease + HAVE_PATHS_H = YesPlease + LIBC_CONTAINS_LIBINTL = YesPlease +endif +ifeq ($(uname_S),IRIX) + NO_SETENV = YesPlease + NO_UNSETENV = YesPlease + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease + NO_MKDTEMP = YesPlease + # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads + # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), + # git dies with a segmentation fault when trying to access the first + # entry of a reflog. The conservative choice is made to always set + # NO_MMAP. If you suspect that your compiler is not affected by this + # issue, comment out the NO_MMAP statement. + NO_MMAP = YesPlease + NO_REGEX = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + SNPRINTF_RETURNS_BOGUS = YesPlease + SHELL_PATH = /usr/gnu/bin/bash + NEEDS_LIBGEN = YesPlease +endif +ifeq ($(uname_S),IRIX64) + NO_SETENV = YesPlease + NO_UNSETENV = YesPlease + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease + NO_MKDTEMP = YesPlease + # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads + # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), + # git dies with a segmentation fault when trying to access the first + # entry of a reflog. The conservative choice is made to always set + # NO_MMAP. If you suspect that your compiler is not affected by this + # issue, comment out the NO_MMAP statement. + NO_MMAP = YesPlease + NO_REGEX = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + SNPRINTF_RETURNS_BOGUS = YesPlease + SHELL_PATH = /usr/gnu/bin/bash + NEEDS_LIBGEN = YesPlease +endif +ifeq ($(uname_S),HP-UX) + INLINE = __inline + NO_IPV6 = YesPlease + NO_SETENV = YesPlease + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease + NO_STRLCPY = YesPlease + NO_MKDTEMP = YesPlease + NO_UNSETENV = YesPlease + NO_HSTRERROR = YesPlease + NO_SYS_SELECT_H = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + SNPRINTF_RETURNS_BOGUS = YesPlease + NO_NSEC = YesPlease + ifeq ($(uname_R),B.11.00) + NO_INET_NTOP = YesPlease + NO_INET_PTON = YesPlease + endif + ifeq ($(uname_R),B.10.20) + # Override HP-UX 11.x setting: + INLINE = + SOCKLEN_T = size_t + NO_PREAD = YesPlease + NO_INET_NTOP = YesPlease + NO_INET_PTON = YesPlease + endif + GIT_TEST_CMP = cmp +endif +ifeq ($(uname_S),Windows) + GIT_VERSION := $(GIT_VERSION).MSVC + pathsep = ; + NO_PREAD = YesPlease + NEEDS_CRYPTO_WITH_SSL = YesPlease + NO_LIBGEN_H = YesPlease + NO_POLL = YesPlease + NO_SYMLINK_HEAD = YesPlease + NO_IPV6 = YesPlease + NO_UNIX_SOCKETS = YesPlease + NO_SETENV = YesPlease + NO_UNSETENV = YesPlease + NO_STRCASESTR = YesPlease + NO_STRLCPY = YesPlease + NO_STRTOK_R = YesPlease + NO_FNMATCH = YesPlease + NO_MEMMEM = YesPlease + # NEEDS_LIBICONV = YesPlease + NO_ICONV = YesPlease + NO_STRTOUMAX = YesPlease + NO_STRTOULL = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + SNPRINTF_RETURNS_BOGUS = YesPlease + NO_SVN_TESTS = YesPlease + NO_PERL_MAKEMAKER = YesPlease + RUNTIME_PREFIX = YesPlease + NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease + USE_WIN32_MMAP = YesPlease + # USE_NED_ALLOCATOR = YesPlease + UNRELIABLE_FSTAT = UnfortunatelyYes + OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo + NO_REGEX = YesPlease + NO_CURL = YesPlease + NO_PYTHON = YesPlease + BLK_SHA1 = YesPlease + NO_POSIX_GOODIES = UnfortunatelyYes + NATIVE_CRLF = YesPlease + DEFAULT_HELP_FORMAT = html + + CC = compat/vcbuild/scripts/clink.pl + AR = compat/vcbuild/scripts/lib.pl + CFLAGS = + BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE + COMPAT_OBJS = compat/msvc.o compat/winansi.o \ + compat/win32/pthread.o compat/win32/syslog.o \ + compat/win32/dirent.o + COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" + BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib + EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib + PTHREAD_LIBS = + lib = +ifndef DEBUG + BASIC_CFLAGS += -GL -Os -MT + BASIC_LDFLAGS += -LTCG + AR += -LTCG +else + BASIC_CFLAGS += -Zi -MTd +endif + X = .exe +endif +ifeq ($(uname_S),Interix) + NO_INITGROUPS = YesPlease + NO_IPV6 = YesPlease + NO_MEMMEM = YesPlease + NO_MKDTEMP = YesPlease + NO_STRTOUMAX = YesPlease + NO_NSEC = YesPlease + NO_MKSTEMPS = YesPlease + ifeq ($(uname_R),3.5) + NO_INET_NTOP = YesPlease + NO_INET_PTON = YesPlease + NO_SOCKADDR_STORAGE = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + endif + ifeq ($(uname_R),5.2) + NO_INET_NTOP = YesPlease + NO_INET_PTON = YesPlease + NO_SOCKADDR_STORAGE = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + endif +endif +ifeq ($(uname_S),Minix) + NO_IPV6 = YesPlease + NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease + NEEDS_LIBGEN = + NEEDS_CRYPTO_WITH_SSL = YesPlease + NEEDS_IDN_WITH_CURL = YesPlease + NEEDS_SSL_WITH_CURL = YesPlease + NEEDS_RESOLV = + NO_HSTRERROR = YesPlease + NO_MMAP = YesPlease + NO_CURL = + NO_EXPAT = +endif +ifeq ($(uname_S),NONSTOP_KERNEL) + # Needs some C99 features, "inline" is just one of them. + # INLINE='' would just replace one set of warnings with another and + # still not compile in c89 mode, due to non-const array initializations. + CC = cc -c99 + # Disable all optimization, seems to result in bad code, with -O or -O2 + # or even -O1 (default), /usr/local/libexec/git-core/git-pack-objects + # abends on "git push". Needs more investigation. + CFLAGS = -g -O0 + # We'd want it to be here. + prefix = /usr/local + # Our's are in ${prefix}/bin (perl might also be in /usr/bin/perl). + PERL_PATH = ${prefix}/bin/perl + PYTHON_PATH = ${prefix}/bin/python + + # As detected by './configure'. + # Missdetected, hence commented out, see below. + #NO_CURL = YesPlease + # Added manually, see above. + NEEDS_SSL_WITH_CURL = YesPlease + HAVE_LIBCHARSET_H = YesPlease + HAVE_STRINGS_H = YesPlease + NEEDS_LIBICONV = YesPlease + NEEDS_LIBINTL_BEFORE_LIBICONV = YesPlease + NO_SYS_SELECT_H = UnfortunatelyYes + NO_D_TYPE_IN_DIRENT = YesPlease + NO_HSTRERROR = YesPlease + NO_STRCASESTR = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + NO_MEMMEM = YesPlease + NO_STRLCPY = YesPlease + NO_SETENV = YesPlease + NO_UNSETENV = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + # Currently libiconv-1.9.1. + OLD_ICONV = UnfortunatelyYes + NO_REGEX = YesPlease + NO_PTHREADS = UnfortunatelyYes + + # Not detected (nor checked for) by './configure'. + # We don't have SA_RESTART on NonStop, unfortunalety. + COMPAT_CFLAGS += -DSA_RESTART=0 + # Apparently needed in compat/fnmatch/fnmatch.c. + COMPAT_CFLAGS += -DHAVE_STRING_H=1 + NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease + NO_PREAD = YesPlease + NO_MMAP = YesPlease + NO_POLL = YesPlease + NO_INTPTR_T = UnfortunatelyYes + # Bug report 10-120822-4477 submitted to HP NonStop development. + MKDIR_WO_TRAILING_SLASH = YesPlease + # RFE 10-120912-4693 submitted to HP NonStop development. + NO_SETITIMER = UnfortunatelyYes + SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin + SHELL_PATH = /usr/local/bin/bash + # as of H06.25/J06.14, we might better use this + #SHELL_PATH = /usr/coreutils/bin/bash +endif +ifneq (,$(findstring MINGW,$(uname_S))) + pathsep = ; + NO_PREAD = YesPlease + NEEDS_CRYPTO_WITH_SSL = YesPlease + NO_LIBGEN_H = YesPlease + NO_POLL = YesPlease + NO_SYMLINK_HEAD = YesPlease + NO_UNIX_SOCKETS = YesPlease + NO_SETENV = YesPlease + NO_UNSETENV = YesPlease + NO_STRCASESTR = YesPlease + NO_STRLCPY = YesPlease + NO_STRTOK_R = YesPlease + NO_FNMATCH = YesPlease + NO_MEMMEM = YesPlease + NEEDS_LIBICONV = YesPlease + OLD_ICONV = YesPlease + NO_STRTOUMAX = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + NO_SVN_TESTS = YesPlease + NO_PERL_MAKEMAKER = YesPlease + RUNTIME_PREFIX = YesPlease + NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease + USE_WIN32_MMAP = YesPlease + USE_NED_ALLOCATOR = YesPlease + UNRELIABLE_FSTAT = UnfortunatelyYes + OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo + NO_REGEX = YesPlease + NO_PYTHON = YesPlease + BLK_SHA1 = YesPlease + ETAGS_TARGET = ETAGS + NO_INET_PTON = YesPlease + NO_INET_NTOP = YesPlease + NO_POSIX_GOODIES = UnfortunatelyYes + COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32 + COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" + COMPAT_OBJS += compat/mingw.o compat/winansi.o \ + compat/win32/pthread.o compat/win32/syslog.o \ + compat/win32/dirent.o + EXTLIBS += -lws2_32 + PTHREAD_LIBS = + X = .exe + SPARSE_FLAGS = -Wno-one-bit-signed-bitfield +ifneq (,$(wildcard ../THIS_IS_MSYSGIT)) + htmldir = doc/git/html/ + prefix = + INSTALL = /bin/install + EXTLIBS += /mingw/lib/libz.a + NO_R_TO_GCC_LINKER = YesPlease + INTERNAL_QSORT = YesPlease + HAVE_LIBCHARSET_H = YesPlease +else + NO_CURL = YesPlease +endif +endif +ifeq ($(uname_S),QNX) + COMPAT_CFLAGS += -DSA_RESTART=0 + HAVE_STRINGS_H = YesPlease + NEEDS_SOCKET = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + NO_GETPAGESIZE = YesPlease + NO_ICONV = YesPlease + NO_MEMMEM = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + NO_NSEC = YesPlease + NO_PTHREADS = YesPlease + NO_R_TO_GCC_LINKER = YesPlease + NO_STRCASESTR = YesPlease + NO_STRLCPY = YesPlease +endif diff --git a/configure.ac b/configure.ac index 41ac9a5e2d..1991258ae0 100644 --- a/configure.ac +++ b/configure.ac @@ -753,6 +753,14 @@ AC_CHECK_MEMBER(struct dirent.d_type, [#include <dirent.h>]) GIT_CONF_SUBST([NO_D_TYPE_IN_DIRENT]) # +# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd +# in the C library. +AC_CHECK_MEMBER(struct passwd.pw_gecos, +[NO_GECOS_IN_PWENT=], +[NO_GECOS_IN_PWENT=YesPlease], +[#include <pwd.h>]) +GIT_CONF_SUBST([NO_GECOS_IN_PWENT]) +# # Define NO_SOCKADDR_STORAGE if your platform does not have struct # sockaddr_storage. AC_CHECK_TYPE(struct sockaddr_storage, @@ -872,6 +880,12 @@ AC_CHECK_HEADER([libcharset.h], [HAVE_LIBCHARSET_H=YesPlease], [HAVE_LIBCHARSET_H=]) GIT_CONF_SUBST([HAVE_LIBCHARSET_H]) +# +# Define HAVE_STRINGS_H if you have strings.h +AC_CHECK_HEADER([strings.h], +[HAVE_STRINGS_H=YesPlease], +[HAVE_STRINGS_H=]) +GIT_CONF_SUBST([HAVE_STRINGS_H]) # Define CHARSET_LIB if libiconv does not export the locale_charset symbol # and libcharset does CHARSET_LIB= diff --git a/contrib/ciabot/ciabot.py b/contrib/ciabot/ciabot.py index bd24395d4c..36b5665ff8 100755 --- a/contrib/ciabot/ciabot.py +++ b/contrib/ciabot/ciabot.py @@ -47,7 +47,13 @@ # we default to that. # -import os, sys, commands, socket, urllib +import sys +if sys.hexversion < 0x02000000: + # The limiter is the xml.sax module + sys.stderr.write("ciabot.py: requires Python 2.0.0 or later.\n") + sys.exit(1) + +import os, commands, socket, urllib from xml.sax.saxutils import escape # Changeset URL prefix for your repo: when the commit ID is appended diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a4c48e179e..14dd5e7ca2 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -397,7 +397,7 @@ __git_complete_revlist_file () *) pfx="$ref:$pfx" ;; esac - __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ + __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \ | sed '/^100... blob /{ s,^.* ,, s,$, , diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh index 8aafb63315..3e3889f2b4 100644 --- a/contrib/completion/git-completion.tcsh +++ b/contrib/completion/git-completion.tcsh @@ -13,6 +13,7 @@ # # To use this completion script: # +# 0) You need tcsh 6.16.00 or newer. # 1) Copy both this file and the bash completion script to ${HOME}. # You _must_ use the name ${HOME}/.git-completion.bash for the # bash script. @@ -24,6 +25,15 @@ # set autolist=ambiguous # It will tell tcsh to list the possible completion choices. +set __git_tcsh_completion_version = `\echo ${tcsh} | \sed 's/\./ /g'` +if ( ${__git_tcsh_completion_version[1]} < 6 || \ + ( ${__git_tcsh_completion_version[1]} == 6 && \ + ${__git_tcsh_completion_version[2]} < 16 ) ) then + echo "git-completion.tcsh: Your version of tcsh is too old, you need version 6.16.00 or newer. Git completion will not work." + exit +endif +unset __git_tcsh_completion_version + set __git_tcsh_completion_original_script = ${HOME}/.git-completion.bash set __git_tcsh_completion_script = ${HOME}/.git-completion.tcsh.bash @@ -64,9 +74,7 @@ fi _\${1} IFS=\$'\n' -if [ \${#COMPREPLY[*]} -gt 0 ]; then - echo "\${COMPREPLY[*]}" | sort | uniq -else +if [ \${#COMPREPLY[*]} -eq 0 ]; then # No completions suggested. In this case, we want tcsh to perform # standard file completion. However, there does not seem to be way # to tell tcsh to do that. To help the user, we try to simulate @@ -85,19 +93,20 @@ else # We don't support ~ expansion: too tricky. if [ "\${TO_COMPLETE:0:1}" != "~" ]; then # Use ls so as to add the '/' at the end of directories. - RESULT=(\`ls -dp \${TO_COMPLETE}* 2> /dev/null\`) - echo \${RESULT[*]} - - # If there is a single completion and it is a directory, - # we output it a second time to trick tcsh into not adding a space - # after it. - if [ \${#RESULT[*]} -eq 1 ] && [ "\${RESULT[0]: -1}" == "/" ]; then - echo \${RESULT[*]} - fi + COMPREPLY=(\`ls -dp \${TO_COMPLETE}* 2> /dev/null\`) fi fi fi +# tcsh does not automatically remove duplicates, so we do it ourselves +echo "\${COMPREPLY[*]}" | sort | uniq + +# If there is a single completion and it is a directory, we output it +# a second time to trick tcsh into not adding a space after it. +if [ \${#COMPREPLY[*]} -eq 1 ] && [ "\${COMPREPLY[0]: -1}" == "/" ]; then + echo "\${COMPREPLY[*]}" +fi + EOF # Don't need this variable anymore, so don't pollute the users environment diff --git a/contrib/fast-import/import-zips.py b/contrib/fast-import/import-zips.py index 82f5ed3ddc..5cec9b0129 100755 --- a/contrib/fast-import/import-zips.py +++ b/contrib/fast-import/import-zips.py @@ -9,10 +9,15 @@ ## git log --stat import-zips from os import popen, path -from sys import argv, exit +from sys import argv, exit, hexversion, stderr from time import mktime from zipfile import ZipFile +if hexversion < 0x01060000: + # The limiter is the zipfile module + sys.stderr.write("import-zips.py: requires Python 1.6.0 or later.\n") + sys.exit(1) + if len(argv) < 2: print 'Usage:', argv[0], '<zipfile>...' exit(1) diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 046cb2b268..232625a7b7 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -23,6 +23,11 @@ import os, os.path, sys import tempfile, pickle, getopt import re +if sys.hexversion < 0x02030000: + # The behavior of the pickle module changed significantly in 2.3 + sys.stderr.write("hg-to-git.py: requires Python 2.3 or later.\n") + sys.exit(1) + # Maps hg version -> git version hgvers = {} # List of children for each hg revision diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py index b6e534b65b..593d6a0682 100644 --- a/contrib/p4import/git-p4import.py +++ b/contrib/p4import/git-p4import.py @@ -14,6 +14,11 @@ import sys import time import getopt +if sys.hexversion < 0x02020000: + # The behavior of the marshal module changed significantly in 2.2 + sys.stderr.write("git-p4import.py: requires Python 2.2 or later.\n") + sys.exit(1) + from signal import signal, \ SIGPIPE, SIGINT, SIG_DFL, \ default_int_handler diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr new file mode 100755 index 0000000000..c5822e4ac9 --- /dev/null +++ b/contrib/remote-helpers/git-remote-bzr @@ -0,0 +1,725 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 Felipe Contreras +# + +# +# Just copy to your ~/bin, or anywhere in your $PATH. +# Then you can clone with: +# % git clone bzr::/path/to/bzr/repo/or/url +# +# For example: +# % git clone bzr::$HOME/myrepo +# or +# % git clone bzr::lp:myrepo +# + +import sys + +import bzrlib +if hasattr(bzrlib, "initialize"): + bzrlib.initialize() + +import bzrlib.plugin +bzrlib.plugin.load_plugins() + +import bzrlib.generate_ids +import bzrlib.transport + +import sys +import os +import json +import re +import StringIO + +NAME_RE = re.compile('^([^<>]+)') +AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$') +RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)') + +def die(msg, *args): + sys.stderr.write('ERROR: %s\n' % (msg % args)) + sys.exit(1) + +def warn(msg, *args): + sys.stderr.write('WARNING: %s\n' % (msg % args)) + +def gittz(tz): + return '%+03d%02d' % (tz / 3600, tz % 3600 / 60) + +class Marks: + + def __init__(self, path): + self.path = path + self.tips = {} + self.marks = {} + self.rev_marks = {} + self.last_mark = 0 + self.load() + + def load(self): + if not os.path.exists(self.path): + return + + tmp = json.load(open(self.path)) + self.tips = tmp['tips'] + self.marks = tmp['marks'] + self.last_mark = tmp['last-mark'] + + for rev, mark in self.marks.iteritems(): + self.rev_marks[mark] = rev + + def dict(self): + return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark } + + def store(self): + json.dump(self.dict(), open(self.path, 'w')) + + def __str__(self): + return str(self.dict()) + + def from_rev(self, rev): + return self.marks[rev] + + def to_rev(self, mark): + return self.rev_marks[mark] + + def next_mark(self): + self.last_mark += 1 + return self.last_mark + + def get_mark(self, rev): + self.last_mark += 1 + self.marks[rev] = self.last_mark + return self.last_mark + + def is_marked(self, rev): + return self.marks.has_key(rev) + + def new_mark(self, rev, mark): + self.marks[rev] = mark + self.rev_marks[mark] = rev + self.last_mark = mark + + def get_tip(self, branch): + return self.tips.get(branch, None) + + def set_tip(self, branch, tip): + self.tips[branch] = tip + +class Parser: + + def __init__(self, repo): + self.repo = repo + self.line = self.get_line() + + def get_line(self): + return sys.stdin.readline().strip() + + def __getitem__(self, i): + return self.line.split()[i] + + def check(self, word): + return self.line.startswith(word) + + def each_block(self, separator): + while self.line != separator: + yield self.line + self.line = self.get_line() + + def __iter__(self): + return self.each_block('') + + def next(self): + self.line = self.get_line() + if self.line == 'done': + self.line = None + + def get_mark(self): + i = self.line.index(':') + 1 + return int(self.line[i:]) + + def get_data(self): + if not self.check('data'): + return None + i = self.line.index(' ') + 1 + size = int(self.line[i:]) + return sys.stdin.read(size) + + def get_author(self): + m = RAW_AUTHOR_RE.match(self.line) + if not m: + return None + _, name, email, date, tz = m.groups() + committer = '%s <%s>' % (name, email) + tz = int(tz) + tz = ((tz / 100) * 3600) + ((tz % 100) * 60) + return (committer, int(date), tz) + +def rev_to_mark(rev): + global marks + return marks.from_rev(rev) + +def mark_to_rev(mark): + global marks + return marks.to_rev(mark) + +def fixup_user(user): + name = mail = None + user = user.replace('"', '') + m = AUTHOR_RE.match(user) + if m: + name = m.group(1) + mail = m.group(2).strip() + else: + m = NAME_RE.match(user) + if m: + name = m.group(1).strip() + + return '%s <%s>' % (name, mail) + +def get_filechanges(cur, prev): + modified = {} + removed = {} + + changes = cur.changes_from(prev) + + for path, fid, kind in changes.added: + modified[path] = fid + for path, fid, kind in changes.removed: + removed[path] = None + for path, fid, kind, mod, _ in changes.modified: + modified[path] = fid + for oldpath, newpath, fid, kind, mod, _ in changes.renamed: + removed[oldpath] = None + modified[newpath] = fid + + return modified, removed + +def export_files(tree, files): + global marks, filenodes + + final = [] + for path, fid in files.iteritems(): + kind = tree.kind(fid) + + h = tree.get_file_sha1(fid) + + if kind == 'symlink': + d = tree.get_symlink_target(fid) + mode = '120000' + elif kind == 'file': + + if tree.is_executable(fid): + mode = '100755' + else: + mode = '100644' + + # is the blog already exported? + if h in filenodes: + mark = filenodes[h] + final.append((mode, mark, path)) + continue + + d = tree.get_file_text(fid) + elif kind == 'directory': + continue + else: + die("Unhandled kind '%s' for path '%s'" % (kind, path)) + + mark = marks.next_mark() + filenodes[h] = mark + + print "blob" + print "mark :%u" % mark + print "data %d" % len(d) + print d + + final.append((mode, mark, path)) + + return final + +def export_branch(branch, name): + global prefix, dirname + + ref = '%s/heads/%s' % (prefix, name) + tip = marks.get_tip(name) + + repo = branch.repository + repo.lock_read() + revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward') + count = 0 + + revs = [revid for revid, _, _, _ in revs if not marks.is_marked(revid)] + + for revid in revs: + + rev = repo.get_revision(revid) + + parents = rev.parent_ids + time = rev.timestamp + tz = rev.timezone + committer = rev.committer.encode('utf-8') + committer = "%s %u %s" % (fixup_user(committer), time, gittz(tz)) + author = committer + msg = rev.message.encode('utf-8') + + msg += '\n' + + if len(parents) == 0: + parent = bzrlib.revision.NULL_REVISION + else: + parent = parents[0] + + cur_tree = repo.revision_tree(revid) + prev = repo.revision_tree(parent) + modified, removed = get_filechanges(cur_tree, prev) + + modified_final = export_files(cur_tree, modified) + + if len(parents) == 0: + print 'reset %s' % ref + + print "commit %s" % ref + print "mark :%d" % (marks.get_mark(revid)) + print "author %s" % (author) + print "committer %s" % (committer) + print "data %d" % (len(msg)) + print msg + + for i, p in enumerate(parents): + try: + m = rev_to_mark(p) + except KeyError: + # ghost? + continue + if i == 0: + print "from :%s" % m + else: + print "merge :%s" % m + + for f in modified_final: + print "M %s :%u %s" % f + for f in removed: + print "D %s" % (f) + print + + count += 1 + if (count % 100 == 0): + print "progress revision %s (%d/%d)" % (revid, count, len(revs)) + print "#############################################################" + + repo.unlock() + + revid = branch.last_revision() + + # make sure the ref is updated + print "reset %s" % ref + print "from :%u" % rev_to_mark(revid) + print + + marks.set_tip(name, revid) + +def export_tag(repo, name): + global tags + try: + print "reset refs/tags/%s" % name + print "from :%u" % rev_to_mark(tags[name]) + print + except KeyError: + warn("TODO: fetch tag '%s'" % name) + +def do_import(parser): + global dirname + + branch = parser.repo + path = os.path.join(dirname, 'marks-git') + + print "feature done" + if os.path.exists(path): + print "feature import-marks=%s" % path + print "feature export-marks=%s" % path + sys.stdout.flush() + + while parser.check('import'): + ref = parser[1] + if ref.startswith('refs/heads/'): + name = ref[len('refs/heads/'):] + export_branch(branch, name) + if ref.startswith('refs/tags/'): + name = ref[len('refs/tags/'):] + export_tag(branch, name) + parser.next() + + print 'done' + + sys.stdout.flush() + +def parse_blob(parser): + global blob_marks + + parser.next() + mark = parser.get_mark() + parser.next() + data = parser.get_data() + blob_marks[mark] = data + parser.next() + +class CustomTree(): + + def __init__(self, repo, revid, parents, files): + global files_cache + + self.repo = repo + self.revid = revid + self.parents = parents + self.updates = {} + + def copy_tree(revid): + files = files_cache[revid] = {} + tree = repo.repository.revision_tree(revid) + repo.lock_read() + try: + for path, entry in tree.iter_entries_by_dir(): + files[path] = entry.file_id + finally: + repo.unlock() + return files + + if len(parents) == 0: + self.base_id = bzrlib.revision.NULL_REVISION + self.base_files = {} + else: + self.base_id = parents[0] + self.base_files = files_cache.get(self.base_id, None) + if not self.base_files: + self.base_files = copy_tree(self.base_id) + + self.files = files_cache[revid] = self.base_files.copy() + + for path, f in files.iteritems(): + fid = self.files.get(path, None) + if not fid: + fid = bzrlib.generate_ids.gen_file_id(path) + f['path'] = path + self.updates[fid] = f + + def last_revision(self): + return self.base_id + + def iter_changes(self): + changes = [] + + def get_parent(dirname, basename): + parent_fid = self.base_files.get(dirname, None) + if parent_fid: + return parent_fid + parent_fid = self.files.get(dirname, None) + if parent_fid: + return parent_fid + if basename == '': + return None + fid = bzrlib.generate_ids.gen_file_id(path) + d = add_entry(fid, dirname, 'directory') + return fid + + def add_entry(fid, path, kind, mode = None): + dirname, basename = os.path.split(path) + parent_fid = get_parent(dirname, basename) + + executable = False + if mode == '100755': + executable = True + elif mode == '120000': + kind = 'symlink' + + change = (fid, + (None, path), + True, + (False, True), + (None, parent_fid), + (None, basename), + (None, kind), + (None, executable)) + self.files[path] = change[0] + changes.append(change) + return change + + def update_entry(fid, path, kind, mode = None): + dirname, basename = os.path.split(path) + parent_fid = get_parent(dirname, basename) + + executable = False + if mode == '100755': + executable = True + elif mode == '120000': + kind = 'symlink' + + change = (fid, + (path, path), + True, + (True, True), + (None, parent_fid), + (None, basename), + (None, kind), + (None, executable)) + self.files[path] = change[0] + changes.append(change) + return change + + def remove_entry(fid, path, kind): + dirname, basename = os.path.split(path) + parent_fid = get_parent(dirname, basename) + change = (fid, + (path, None), + True, + (True, False), + (parent_fid, None), + (None, None), + (None, None), + (None, None)) + del self.files[path] + changes.append(change) + return change + + for fid, f in self.updates.iteritems(): + path = f['path'] + + if 'deleted' in f: + remove_entry(fid, path, 'file') + continue + + if path in self.base_files: + update_entry(fid, path, 'file', f['mode']) + else: + add_entry(fid, path, 'file', f['mode']) + + return changes + + def get_file_with_stat(self, file_id, path=None): + return (StringIO.StringIO(self.updates[file_id]['data']), None) + + def get_symlink_target(self, file_id): + return self.updates[file_id]['data'] + +def parse_commit(parser): + global marks, blob_marks, bmarks, parsed_refs + global mode + + parents = [] + + ref = parser[1] + parser.next() + + if ref != 'refs/heads/master': + die("bzr doesn't support multiple branches; use 'master'") + + commit_mark = parser.get_mark() + parser.next() + author = parser.get_author() + parser.next() + committer = parser.get_author() + parser.next() + data = parser.get_data() + parser.next() + if parser.check('from'): + parents.append(parser.get_mark()) + parser.next() + while parser.check('merge'): + parents.append(parser.get_mark()) + parser.next() + + files = {} + + for line in parser: + if parser.check('M'): + t, m, mark_ref, path = line.split(' ', 3) + mark = int(mark_ref[1:]) + f = { 'mode' : m, 'data' : blob_marks[mark] } + elif parser.check('D'): + t, path = line.split(' ') + f = { 'deleted' : True } + else: + die('Unknown file command: %s' % line) + files[path] = f + + repo = parser.repo + + committer, date, tz = committer + parents = [str(mark_to_rev(p)) for p in parents] + revid = bzrlib.generate_ids.gen_revision_id(committer, date) + props = {} + props['branch-nick'] = repo.nick + + mtree = CustomTree(repo, revid, parents, files) + changes = mtree.iter_changes() + + repo.lock_write() + try: + builder = repo.get_commit_builder(parents, None, date, tz, committer, props, revid) + try: + list(builder.record_iter_changes(mtree, mtree.last_revision(), changes)) + builder.finish_inventory() + builder.commit(data.decode('utf-8', 'replace')) + except Exception, e: + builder.abort() + raise + finally: + repo.unlock() + + parsed_refs[ref] = revid + marks.new_mark(revid, commit_mark) + +def parse_reset(parser): + global parsed_refs + + ref = parser[1] + parser.next() + + if ref != 'refs/heads/master': + die("bzr doesn't support multiple branches; use 'master'") + + # ugh + if parser.check('commit'): + parse_commit(parser) + return + if not parser.check('from'): + return + from_mark = parser.get_mark() + parser.next() + + parsed_refs[ref] = mark_to_rev(from_mark) + +def do_export(parser): + global parsed_refs, dirname, peer + + parser.next() + + for line in parser.each_block('done'): + if parser.check('blob'): + parse_blob(parser) + elif parser.check('commit'): + parse_commit(parser) + elif parser.check('reset'): + parse_reset(parser) + elif parser.check('tag'): + pass + elif parser.check('feature'): + pass + else: + die('unhandled export command: %s' % line) + + repo = parser.repo + + for ref, revid in parsed_refs.iteritems(): + if ref == 'refs/heads/master': + repo.generate_revision_history(revid, marks.get_tip('master')) + revno, revid = repo.last_revision_info() + if peer: + if hasattr(peer, "import_last_revision_info_and_tags"): + peer.import_last_revision_info_and_tags(repo, revno, revid) + else: + peer.import_last_revision_info(repo.repository, revno, revid) + wt = peer.bzrdir.open_workingtree() + else: + wt = repo.bzrdir.open_workingtree() + wt.update() + print "ok %s" % ref + print + +def do_capabilities(parser): + global dirname + + print "import" + print "export" + print "refspec refs/heads/*:%s/heads/*" % prefix + + path = os.path.join(dirname, 'marks-git') + + if os.path.exists(path): + print "*import-marks %s" % path + print "*export-marks %s" % path + + print + +def do_list(parser): + global tags + print "? refs/heads/%s" % 'master' + for tag, revid in parser.repo.tags.get_tag_dict().items(): + print "? refs/tags/%s" % tag + tags[tag] = revid + print "@refs/heads/%s HEAD" % 'master' + print + +def get_repo(url, alias): + global dirname, peer + + origin = bzrlib.bzrdir.BzrDir.open(url) + branch = origin.open_branch() + + if not isinstance(origin.transport, bzrlib.transport.local.LocalTransport): + clone_path = os.path.join(dirname, 'clone') + remote_branch = branch + if os.path.exists(clone_path): + # pull + d = bzrlib.bzrdir.BzrDir.open(clone_path) + branch = d.open_branch() + result = branch.pull(remote_branch, [], None, False) + else: + # clone + d = origin.sprout(clone_path, None, + hardlink=True, create_tree_if_local=False, + source_branch=remote_branch) + branch = d.open_branch() + branch.bind(remote_branch) + + peer = remote_branch + else: + peer = None + + return branch + +def main(args): + global marks, prefix, dirname + global tags, filenodes + global blob_marks + global parsed_refs + global files_cache + + alias = args[1] + url = args[2] + + prefix = 'refs/bzr/%s' % alias + tags = {} + filenodes = {} + blob_marks = {} + parsed_refs = {} + files_cache = {} + + gitdir = os.environ['GIT_DIR'] + dirname = os.path.join(gitdir, 'bzr', alias) + + if not os.path.exists(dirname): + os.makedirs(dirname) + + repo = get_repo(url, alias) + + marks_path = os.path.join(dirname, 'marks-int') + marks = Marks(marks_path) + + parser = Parser(repo) + for line in parser: + if parser.check('capabilities'): + do_capabilities(parser) + elif parser.check('list'): + do_list(parser) + elif parser.check('import'): + do_import(parser) + elif parser.check('export'): + do_export(parser) + else: + die('unhandled command: %s' % line) + sys.stdout.flush() + + marks.store() + +sys.exit(main(sys.argv)) diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index c7006000a6..328c2dc76d 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -53,7 +53,7 @@ def gittz(tz): return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60) def hgmode(mode): - m = { '0100755': 'x', '0120000': 'l' } + m = { '100755': 'x', '120000': 'l' } return m.get(mode, '') def get_config(config): @@ -720,6 +720,14 @@ def do_export(parser): if peer: parser.repo.push(peer, force=False) +def fix_path(alias, repo, orig_url): + repo_url = util.url(repo.url()) + url = util.url(orig_url) + if str(url) == str(repo_url): + return + cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % repo_url] + subprocess.call(cmd) + def main(args): global prefix, dirname, branches, bmarks global marks, blob_marks, parsed_refs @@ -766,6 +774,9 @@ def main(args): repo = get_repo(url, alias) prefix = 'refs/hg/%s' % alias + if not is_tmp: + fix_path(alias, peer or repo, url) + if not os.path.exists(dirname): os.makedirs(dirname) diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh new file mode 100755 index 0000000000..70aa8a010a --- /dev/null +++ b/contrib/remote-helpers/test-bzr.sh @@ -0,0 +1,143 @@ +#!/bin/sh +# +# Copyright (c) 2012 Felipe Contreras +# + +test_description='Test remote-bzr' + +. ./test-lib.sh + +if ! test_have_prereq PYTHON; then + skip_all='skipping remote-bzr tests; python not available' + test_done +fi + +if ! "$PYTHON_PATH" -c 'import bzrlib'; then + skip_all='skipping remote-bzr tests; bzr not available' + test_done +fi + +cmd=' +import bzrlib +bzrlib.initialize() +import bzrlib.plugin +bzrlib.plugin.load_plugins() +import bzrlib.plugins.fastimport +' + +if ! "$PYTHON_PATH" -c "$cmd"; then + echo "consider setting BZR_PLUGIN_PATH=$HOME/.bazaar/plugins" 1>&2 + skip_all='skipping remote-bzr tests; bzr-fastimport not available' + test_done +fi + +check () { + (cd $1 && + git log --format='%s' -1 && + git symbolic-ref HEAD) > actual && + (echo $2 && + echo "refs/heads/$3") > expected && + test_cmp expected actual +} + +bzr whoami "A U Thor <author@example.com>" + +test_expect_success 'cloning' ' + (bzr init bzrrepo && + cd bzrrepo && + echo one > content && + bzr add content && + bzr commit -m one + ) && + + git clone "bzr::$PWD/bzrrepo" gitrepo && + check gitrepo one master +' + +test_expect_success 'pulling' ' + (cd bzrrepo && + echo two > content && + bzr commit -m two + ) && + + (cd gitrepo && git pull) && + + check gitrepo two master +' + +test_expect_success 'pushing' ' + (cd gitrepo && + echo three > content && + git commit -a -m three && + git push + ) && + + echo three > expected && + cat bzrrepo/content > actual && + test_cmp expected actual +' + +test_expect_success 'roundtrip' ' + (cd gitrepo && + git pull && + git log --format="%s" -1 origin/master > actual) && + echo three > expected && + test_cmp expected actual && + + (cd gitrepo && git push && git pull) && + + (cd bzrrepo && + echo four > content && + bzr commit -m four + ) && + + (cd gitrepo && git pull && git push) && + + check gitrepo four master && + + (cd gitrepo && + echo five > content && + git commit -a -m five && + git push && git pull + ) && + + (cd bzrrepo && bzr revert) && + + echo five > expected && + cat bzrrepo/content > actual && + test_cmp expected actual +' + +cat > expected <<EOF +100644 blob 54f9d6da5c91d556e6b54340b1327573073030af content +100755 blob 68769579c3eaadbe555379b9c3538e6628bae1eb executable +120000 blob 6b584e8ece562ebffc15d38808cd6b98fc3d97ea link +EOF + +test_expect_success 'special modes' ' + (cd bzrrepo && + echo exec > executable + chmod +x executable && + bzr add executable + bzr commit -m exec && + ln -s content link + bzr add link + bzr commit -m link && + mkdir dir && + bzr add dir && + bzr commit -m dir) && + + (cd gitrepo && + git pull + git ls-tree HEAD > ../actual) && + + test_cmp expected actual && + + (cd gitrepo && + git cat-file -p HEAD:link > ../actual) && + + echo -n content > expected && + test_cmp expected actual +' + +test_done diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh index 3e76d9fb60..7e3967f5b6 100755 --- a/contrib/remote-helpers/test-hg-hg-git.sh +++ b/contrib/remote-helpers/test-hg-hg-git.sh @@ -109,6 +109,74 @@ setup () { setup +test_expect_success 'executable bit' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + git init -q gitrepo && + cd gitrepo && + echo alpha > alpha && + chmod 0644 alpha && + git add alpha && + git commit -m "add alpha" && + chmod 0755 alpha && + git add alpha && + git commit -m "set executable bit" && + chmod 0644 alpha && + git add alpha && + git commit -m "clear executable bit" + ) && + + for x in hg git; do + ( + hg_clone_$x gitrepo hgrepo-$x && + cd hgrepo-$x && + hg_log . && + hg manifest -r 1 -v && + hg manifest -v + ) > output-$x && + + git_clone_$x hgrepo-$x gitrepo2-$x && + git_log gitrepo2-$x > log-$x + done && + cp -r log-* output-* /tmp/foo/ && + + test_cmp output-hg output-git && + test_cmp log-hg log-git +' + +test_expect_success 'symlink' ' + mkdir -p tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + ( + git init -q gitrepo && + cd gitrepo && + echo alpha > alpha && + git add alpha && + git commit -m "add alpha" && + ln -s alpha beta && + git add beta && + git commit -m "add beta" + ) && + + for x in hg git; do + ( + hg_clone_$x gitrepo hgrepo-$x && + cd hgrepo-$x && + hg_log . && + hg manifest -v + ) > output-$x && + + git_clone_$x hgrepo-$x gitrepo2-$x && + git_log gitrepo2-$x > log-$x + done && + + test_cmp output-hg output-git && + test_cmp log-hg log-git +' + test_expect_success 'merge conflict 1' ' mkdir -p tmp && cd tmp && test_when_finished "cd .. && rm -rf tmp" && diff --git a/contrib/svn-fe/svnrdump_sim.py b/contrib/svn-fe/svnrdump_sim.py index 1cfac4a6f8..17cf6f961f 100755 --- a/contrib/svn-fe/svnrdump_sim.py +++ b/contrib/svn-fe/svnrdump_sim.py @@ -7,6 +7,10 @@ to the highest revision that should be available. """ import sys, os +if sys.hexversion < 0x02040000: + # The limiter is the ValueError() calls. This may be too conservative + sys.stderr.write("svnrdump-sim.py: requires Python 2.4 or later.\n") + sys.exit(1) def getrevlimit(): var = 'SVNRMAX' diff --git a/contrib/vim/README b/contrib/vim/README index fca1e17251..8f16d06972 100644 --- a/contrib/vim/README +++ b/contrib/vim/README @@ -17,16 +17,6 @@ To install: 1. Copy these files to vim's syntax directory $HOME/.vim/syntax 2. To auto-detect the editing of various git-related filetypes: - $ cat >>$HOME/.vim/filetype.vim <<'EOF' - autocmd BufNewFile,BufRead *.git/COMMIT_EDITMSG setf gitcommit - autocmd BufNewFile,BufRead *.git/config,.gitconfig setf gitconfig - autocmd BufNewFile,BufRead git-rebase-todo setf gitrebase - autocmd BufNewFile,BufRead .msg.[0-9]* - \ if getline(1) =~ '^From.*# This line is ignored.$' | - \ setf gitsendemail | - \ endif - autocmd BufNewFile,BufRead *.git/** - \ if getline(1) =~ '^\x\{40\}\>\|^ref: ' | - \ setf git | - \ endif - EOF + + $ curl http://ftp.vim.org/pub/vim/runtime/filetype.vim | + sed -ne '/^" Git$/, /^$/ p' >>$HOME/.vim/filetype.vim @@ -11,18 +11,21 @@ enum { D = GIT_DIGIT, G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */ - P = GIT_PATHSPEC_MAGIC /* other non-alnum, except for ] and } */ + P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */ + X = GIT_CNTRL, + U = GIT_PUNCT, + Z = GIT_CNTRL | GIT_SPACE }; -unsigned char sane_ctype[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */ +const unsigned char sane_ctype[256] = { + X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ - A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P, /* 80.. 95 */ + A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ - A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0, /* 112..127 */ + A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */ /* Nothing in the 128.. range */ }; @@ -3626,6 +3626,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, FIND_COPIES_HARDER); else if (!strcmp(arg, "--follow")) DIFF_OPT_SET(options, FOLLOW_RENAMES); + else if (!strcmp(arg, "--no-follow")) + DIFF_OPT_CLR(options, FOLLOW_RENAMES); else if (!strcmp(arg, "--color")) options->use_color = 1; else if (!prefixcmp(arg, "--color=")) { @@ -2,12 +2,15 @@ * This handles recursive filename detection with exclude * files, index knowledge etc.. * + * See Documentation/technical/api-directory-listing.txt + * * Copyright (C) Linus Torvalds, 2005-2006 * Junio Hamano, 2005-2006 */ #include "cache.h" #include "dir.h" #include "refs.h" +#include "wildmatch.h" struct path_simplify { int len; @@ -34,10 +37,33 @@ int fnmatch_icase(const char *pattern, const char *string, int flags) return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0)); } +inline int git_fnmatch(const char *pattern, const char *string, + int flags, int prefix) +{ + int fnm_flags = 0; + if (flags & GFNM_PATHNAME) + fnm_flags |= FNM_PATHNAME; + if (prefix > 0) { + if (strncmp(pattern, string, prefix)) + return FNM_NOMATCH; + pattern += prefix; + string += prefix; + } + if (flags & GFNM_ONESTAR) { + int pattern_len = strlen(++pattern); + int string_len = strlen(string); + return string_len < pattern_len || + strcmp(pattern, + string + string_len - pattern_len); + } + return fnmatch(pattern, string, fnm_flags); +} + static size_t common_prefix_len(const char **pathspec) { const char *n, *first; size_t max = 0; + int literal = limit_pathspec_to_literal(); if (!pathspec) return max; @@ -47,7 +73,7 @@ static size_t common_prefix_len(const char **pathspec) size_t i, len = 0; for (i = 0; first == n || i < max; i++) { char c = n[i]; - if (!c || c != first[i] || is_glob_special(c)) + if (!c || c != first[i] || (!literal && is_glob_special(c))) break; if (c == '/') len = i + 1; @@ -117,6 +143,7 @@ int within_depth(const char *name, int namelen, static int match_one(const char *match, const char *name, int namelen) { int matchlen; + int literal = limit_pathspec_to_literal(); /* If the match was just the prefix, we matched */ if (!*match) @@ -126,7 +153,7 @@ static int match_one(const char *match, const char *name, int namelen) for (;;) { unsigned char c1 = tolower(*match); unsigned char c2 = tolower(*name); - if (c1 == '\0' || is_glob_special(c1)) + if (c1 == '\0' || (!literal && is_glob_special(c1))) break; if (c1 != c2) return 0; @@ -138,7 +165,7 @@ static int match_one(const char *match, const char *name, int namelen) for (;;) { unsigned char c1 = *match; unsigned char c2 = *name; - if (c1 == '\0' || is_glob_special(c1)) + if (c1 == '\0' || (!literal && is_glob_special(c1))) break; if (c1 != c2) return 0; @@ -148,14 +175,16 @@ static int match_one(const char *match, const char *name, int namelen) } } - /* * If we don't match the matchstring exactly, * we need to match by fnmatch */ matchlen = strlen(match); - if (strncmp_icase(match, name, matchlen)) + if (strncmp_icase(match, name, matchlen)) { + if (literal) + return 0; return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0; + } if (namelen == matchlen) return MATCHED_EXACTLY; @@ -230,7 +259,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, return MATCHED_RECURSIVELY; } - if (item->use_wildcard && !fnmatch(match, name, 0)) + if (item->nowildcard_len < item->len && + !git_fnmatch(match, name, + item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + item->nowildcard_len - prefix)) return MATCHED_FNMATCH; return 0; @@ -347,7 +379,7 @@ void parse_exclude_pattern(const char **pattern, } void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *which) + int baselen, struct exclude_list *el) { struct exclude *x; int patternlen; @@ -371,8 +403,8 @@ void add_exclude(const char *string, const char *base, x->base = base; x->baselen = baselen; x->flags = flags; - ALLOC_GROW(which->excludes, which->nr + 1, which->alloc); - which->excludes[which->nr++] = x; + ALLOC_GROW(el->excludes, el->nr + 1, el->alloc); + el->excludes[el->nr++] = x; } static void *read_skip_worktree_file_from_index(const char *path, size_t *size) @@ -398,7 +430,11 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size) return data; } -void free_excludes(struct exclude_list *el) +/* + * Frees memory within el which was allocated for exclude patterns and + * the file buffer. Does not free el itself. + */ +void clear_exclude_list(struct exclude_list *el) { int i; @@ -414,7 +450,7 @@ int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, char **buf_p, - struct exclude_list *which, + struct exclude_list *el, int check_index) { struct stat st; @@ -463,7 +499,7 @@ int add_excludes_from_file_to_list(const char *fname, if (buf[i] == '\n') { if (entry != buf + i && entry[0] != '#') { buf[i - (i && buf[i-1] == '\r')] = 0; - add_exclude(entry, base, baselen, which); + add_exclude(entry, base, baselen, el); } entry = buf + i + 1; } @@ -478,6 +514,10 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname) die("cannot use %s as an exclude file", fname); } +/* + * Loads the per-directory exclude list for the substring of base + * which has a char length of baselen. + */ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { struct exclude_list *el; @@ -488,7 +528,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX)) return; /* too long a path -- ignore */ - /* Pop the ones that are not the prefix of the path being checked. */ + /* Pop the directories that are not the prefix of the path being checked. */ el = &dir->exclude_list[EXC_DIRS]; while ((stk = dir->exclude_stack) != NULL) { if (stk->baselen <= baselen && @@ -595,25 +635,30 @@ int match_pathname(const char *pathname, int pathlen, namelen -= prefix; } - return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0; + return wildmatch(pattern, name, + ignore_case ? FNM_CASEFOLD : 0) == 0; } -/* Scan the list and let the last match determine the fate. - * Return 1 for exclude, 0 for include and -1 for undecided. +/* + * Scan the given exclude list in reverse to see whether pathname + * should be ignored. The first match (i.e. the last on the list), if + * any, determines the fate. Returns the exclude_list element which + * matched, or NULL for undecided. */ -int excluded_from_list(const char *pathname, - int pathlen, const char *basename, int *dtype, - struct exclude_list *el) +static struct exclude *last_exclude_matching_from_list(const char *pathname, + int pathlen, + const char *basename, + int *dtype, + struct exclude_list *el) { int i; if (!el->nr) - return -1; /* undefined */ + return NULL; /* undefined */ for (i = el->nr - 1; 0 <= i; i--) { struct exclude *x = el->excludes[i]; const char *exclude = x->pattern; - int to_exclude = x->flags & EXC_FLAG_NEGATIVE ? 0 : 1; int prefix = x->nowildcardlen; if (x->flags & EXC_FLAG_MUSTBEDIR) { @@ -628,7 +673,7 @@ int excluded_from_list(const char *pathname, pathlen - (basename - pathname), exclude, prefix, x->patternlen, x->flags)) - return to_exclude; + return x; continue; } @@ -636,28 +681,64 @@ int excluded_from_list(const char *pathname, if (match_pathname(pathname, pathlen, x->base, x->baselen ? x->baselen - 1 : 0, exclude, prefix, x->patternlen, x->flags)) - return to_exclude; + return x; } + return NULL; /* undecided */ +} + +/* + * Scan the list and let the last match determine the fate. + * Return 1 for exclude, 0 for include and -1 for undecided. + */ +int is_excluded_from_list(const char *pathname, + int pathlen, const char *basename, int *dtype, + struct exclude_list *el) +{ + struct exclude *exclude; + exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return -1; /* undecided */ } -static int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) +/* + * Loads the exclude lists for the directory containing pathname, then + * scans all exclude lists to determine whether pathname is excluded. + * Returns the exclude_list element which matched, or NULL for + * undecided. + */ +static struct exclude *last_exclude_matching(struct dir_struct *dir, + const char *pathname, + int *dtype_p) { int pathlen = strlen(pathname); int st; + struct exclude *exclude; const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; prep_exclude(dir, pathname, basename-pathname); for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_from_list(pathname, pathlen, basename, - dtype_p, &dir->exclude_list[st])) { - case 0: - return 0; - case 1: - return 1; - } + exclude = last_exclude_matching_from_list( + pathname, pathlen, basename, dtype_p, + &dir->exclude_list[st]); + if (exclude) + return exclude; } + return NULL; +} + +/* + * Loads the exclude lists for the directory containing pathname, then + * scans all exclude lists to determine whether pathname is excluded. + * Returns 1 if true, otherwise 0. + */ +static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) +{ + struct exclude *exclude = + last_exclude_matching(dir, pathname, dtype_p); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return 0; } @@ -665,6 +746,7 @@ void path_exclude_check_init(struct path_exclude_check *check, struct dir_struct *dir) { check->dir = dir; + check->exclude = NULL; strbuf_init(&check->path, 256); } @@ -674,32 +756,41 @@ void path_exclude_check_clear(struct path_exclude_check *check) } /* - * Is this name excluded? This is for a caller like show_files() that - * do not honor directory hierarchy and iterate through paths that are - * possibly in an ignored directory. + * For each subdirectory in name, starting with the top-most, checks + * to see if that subdirectory is excluded, and if so, returns the + * corresponding exclude structure. Otherwise, checks whether name + * itself (which is presumably a file) is excluded. * * A path to a directory known to be excluded is left in check->path to * optimize for repeated checks for files in the same excluded directory. */ -int path_excluded(struct path_exclude_check *check, - const char *name, int namelen, int *dtype) +struct exclude *last_exclude_matching_path(struct path_exclude_check *check, + const char *name, int namelen, + int *dtype) { int i; struct strbuf *path = &check->path; + struct exclude *exclude; /* * we allow the caller to pass namelen as an optimization; it * must match the length of the name, as we eventually call - * excluded() on the whole name string. + * is_excluded() on the whole name string. */ if (namelen < 0) namelen = strlen(name); + /* + * If path is non-empty, and name is equal to path or a + * subdirectory of path, name should be excluded, because + * it's inside a directory which is already known to be + * excluded and was previously left in check->path. + */ if (path->len && path->len <= namelen && !memcmp(name, path->buf, path->len) && (!name[path->len] || name[path->len] == '/')) - return 1; + return check->exclude; strbuf_setlen(path, 0); for (i = 0; name[i]; i++) { @@ -707,8 +798,12 @@ int path_excluded(struct path_exclude_check *check, if (ch == '/') { int dt = DT_DIR; - if (excluded(check->dir, path->buf, &dt)) - return 1; + exclude = last_exclude_matching(check->dir, + path->buf, &dt); + if (exclude) { + check->exclude = exclude; + return exclude; + } } strbuf_addch(path, ch); } @@ -716,7 +811,22 @@ int path_excluded(struct path_exclude_check *check, /* An entry in the index; cannot be a directory with subentries */ strbuf_setlen(path, 0); - return excluded(check->dir, name, dtype); + return last_exclude_matching(check->dir, name, dtype); +} + +/* + * Is this name excluded? This is for a caller like show_files() that + * do not honor directory hierarchy and iterate through paths that are + * possibly in an ignored directory. + */ +int is_path_excluded(struct path_exclude_check *check, + const char *name, int namelen, int *dtype) +{ + struct exclude *exclude = + last_exclude_matching_path(check, name, namelen, dtype); + if (exclude) + return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; + return 0; } static struct dir_entry *dir_entry_new(const char *pathname, int len) @@ -732,7 +842,8 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) { - if (cache_name_exists(pathname, len, ignore_case)) + if (!(dir->flags & DIR_SHOW_IGNORED) && + cache_name_exists(pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); @@ -834,8 +945,9 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len) * traversal routine. * * Case 1: If we *already* have entries in the index under that - * directory name, we always recurse into the directory to see - * all the files. + * directory name, we recurse into the directory to see all the files, + * unless the directory is excluded and we want to show ignored + * directories * * Case 2: If we *already* have that directory name as a gitlink, * we always continue to see it as a gitlink, regardless of whether @@ -849,6 +961,9 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len) * just a directory, unless "hide_empty_directories" is * also true and the directory is empty, in which case * we just ignore it entirely. + * if we are looking for ignored directories, look if it + * contains only ignored files to decide if it must be shown as + * ignored or not. * (b) if it looks like a git directory, and we don't have * 'no_gitlinks' set we treat it as a gitlink, and show it * as a directory. @@ -861,12 +976,15 @@ enum directory_treatment { }; static enum directory_treatment treat_directory(struct dir_struct *dir, - const char *dirname, int len, + const char *dirname, int len, int exclude, const struct path_simplify *simplify) { /* The "len-1" is to strip the final '/' */ switch (directory_exists_in_index(dirname, len-1)) { case index_directory: + if ((dir->flags & DIR_SHOW_OTHER_DIRECTORIES) && exclude) + break; + return recurse_into_directory; case index_gitdir: @@ -886,7 +1004,23 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, } /* This is the "show_other_directories" case */ - if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) + + /* + * We are looking for ignored files and our directory is not ignored, + * check if it contains only ignored files + */ + if ((dir->flags & DIR_SHOW_IGNORED) && !exclude) { + int ignored; + dir->flags &= ~DIR_SHOW_IGNORED; + dir->flags |= DIR_HIDE_EMPTY_DIRECTORIES; + ignored = read_directory_recursive(dir, dirname, len, 1, simplify); + dir->flags &= ~DIR_HIDE_EMPTY_DIRECTORIES; + dir->flags |= DIR_SHOW_IGNORED; + + return ignored ? ignore_directory : show_directory; + } + if (!(dir->flags & DIR_SHOW_IGNORED) && + !(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return show_directory; if (!read_directory_recursive(dir, dirname, len, 1, simplify)) return ignore_directory; @@ -894,6 +1028,45 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, } /* + * Decide what to do when we find a file while traversing the + * filesystem. Mostly two cases: + * + * 1. We are looking for ignored files + * (a) File is ignored, include it + * (b) File is in ignored path, include it + * (c) File is not ignored, exclude it + * + * 2. Other scenarios, include the file if not excluded + * + * Return 1 for exclude, 0 for include. + */ +static int treat_file(struct dir_struct *dir, struct strbuf *path, int exclude, int *dtype) +{ + struct path_exclude_check check; + int exclude_file = 0; + + if (exclude) + exclude_file = !(dir->flags & DIR_SHOW_IGNORED); + else if (dir->flags & DIR_SHOW_IGNORED) { + /* Always exclude indexed files */ + struct cache_entry *ce = index_name_exists(&the_index, + path->buf, path->len, ignore_case); + + if (ce) + return 1; + + path_exclude_check_init(&check, dir); + + if (!is_path_excluded(&check, path->buf, path->len, dtype)) + exclude_file = 1; + + path_exclude_check_clear(&check); + } + + return exclude_file; +} + +/* * This is an inexact early pruning of any recursive directory * reading - if the path cannot possibly be in the pathspec, * return true, and we'll skip it early. @@ -1016,7 +1189,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, const struct path_simplify *simplify, int dtype, struct dirent *de) { - int exclude = excluded(dir, path->buf, &dtype); + int exclude = is_excluded(dir, path->buf, &dtype); if (exclude && (dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path->buf, path->len, simplify)) dir_add_ignored(dir, path->buf, path->len); @@ -1031,27 +1204,14 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, if (dtype == DT_UNKNOWN) dtype = get_dtype(de, path->buf, path->len); - /* - * Do we want to see just the ignored files? - * We still need to recurse into directories, - * even if we don't ignore them, since the - * directory may contain files that we do.. - */ - if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) { - if (dtype != DT_DIR) - return path_ignored; - } - switch (dtype) { default: return path_ignored; case DT_DIR: strbuf_addch(path, '/'); - switch (treat_directory(dir, path->buf, path->len, simplify)) { + + switch (treat_directory(dir, path->buf, path->len, exclude, simplify)) { case show_directory: - if (exclude != !!(dir->flags - & DIR_SHOW_IGNORED)) - return path_ignored; break; case recurse_into_directory: return path_recurse; @@ -1061,7 +1221,12 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, break; case DT_REG: case DT_LNK: - break; + switch (treat_file(dir, path, exclude, &dtype)) { + case 1: + return path_ignored; + default: + break; + } } return path_handled; } @@ -1429,9 +1594,18 @@ int init_pathspec(struct pathspec *pathspec, const char **paths) item->match = path; item->len = strlen(path); - item->use_wildcard = !no_wildcard(path); - if (item->use_wildcard) - pathspec->has_wildcard = 1; + item->flags = 0; + if (limit_pathspec_to_literal()) { + item->nowildcard_len = item->len; + } else { + item->nowildcard_len = simple_length(path); + if (item->nowildcard_len < item->len) { + pathspec->has_wildcard = 1; + if (path[item->nowildcard_len] == '*' && + no_wildcard(path + item->nowildcard_len + 1)) + item->flags |= PATHSPEC_ONESTAR; + } + } } qsort(pathspec->items, pathspec->nr, @@ -1445,3 +1619,11 @@ void free_pathspec(struct pathspec *pathspec) free(pathspec->items); pathspec->items = NULL; } + +int limit_pathspec_to_literal(void) +{ + static int flag = -1; + if (flag < 0) + flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0); + return flag; +} @@ -1,6 +1,8 @@ #ifndef DIR_H #define DIR_H +/* See Documentation/technical/api-directory-listing.txt */ + #include "strbuf.h" struct dir_entry { @@ -13,6 +15,12 @@ struct dir_entry { #define EXC_FLAG_MUSTBEDIR 8 #define EXC_FLAG_NEGATIVE 16 +/* + * Each .gitignore file will be parsed into patterns which are then + * appended to the relevant exclude_list (either EXC_DIRS or + * EXC_FILE). exclude_lists are also used to represent the list of + * --exclude values passed via CLI args (EXC_CMDL). + */ struct exclude_list { int nr; int alloc; @@ -26,9 +34,15 @@ struct exclude_list { } **excludes; }; +/* + * The contents of the per-directory exclude files are lazily read on + * demand and then cached in memory, one per exclude_stack struct, in + * order to avoid opening and parsing each one every time that + * directory is traversed. + */ struct exclude_stack { - struct exclude_stack *prev; - char *filebuf; + struct exclude_stack *prev; /* the struct exclude_stack for the parent directory */ + char *filebuf; /* remember pointer to per-directory exclude file contents so we can free() */ int baselen; int exclude_ix; }; @@ -59,6 +73,14 @@ struct dir_struct { #define EXC_DIRS 1 #define EXC_FILE 2 + /* + * Temporary variables which are used during loading of the + * per-directory exclude lists. + * + * exclude_stack points to the top of the exclude_stack, and + * basebuf contains the full path to the current + * (sub)directory in the traversal. + */ struct exclude_stack *exclude_stack; char basebuf[PATH_MAX]; }; @@ -76,8 +98,8 @@ extern int within_depth(const char *name, int namelen, int depth, int max_depth) extern int fill_directory(struct dir_struct *dir, const char **pathspec); extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec); -extern int excluded_from_list(const char *pathname, int pathlen, const char *basename, - int *dtype, struct exclude_list *el); +extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, + int *dtype, struct exclude_list *el); struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len); /* @@ -91,26 +113,29 @@ extern int match_pathname(const char *, int, const char *, int, int, int); /* - * The excluded() API is meant for callers that check each level of leading - * directory hierarchies with excluded() to avoid recursing into excluded + * The is_excluded() API is meant for callers that check each level of leading + * directory hierarchies with is_excluded() to avoid recursing into excluded * directories. Callers that do not do so should use this API instead. */ struct path_exclude_check { struct dir_struct *dir; + struct exclude *exclude; struct strbuf path; }; extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *); extern void path_exclude_check_clear(struct path_exclude_check *); -extern int path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype); +extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, const char *, + int namelen, int *dtype); +extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype); extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, - char **buf_p, struct exclude_list *which, int check_index); + char **buf_p, struct exclude_list *el, int check_index); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void parse_exclude_pattern(const char **string, int *patternlen, int *flags, int *nowildcardlen); extern void add_exclude(const char *string, const char *base, - int baselen, struct exclude_list *which); -extern void free_excludes(struct exclude_list *el); + int baselen, struct exclude_list *el); +extern void clear_exclude_list(struct exclude_list *el); extern int file_exists(const char *); extern int is_inside_dir(const char *dir); @@ -139,4 +164,13 @@ extern int strcmp_icase(const char *a, const char *b); extern int strncmp_icase(const char *a, const char *b, size_t count); extern int fnmatch_icase(const char *pattern, const char *string, int flags); +/* + * The prefix part of pattern must not contains wildcards. + */ +#define GFNM_PATHNAME 1 /* similar to FNM_PATHNAME */ +#define GFNM_ONESTAR 2 /* there is only _one_ wildcard, a star */ + +extern int git_fnmatch(const char *pattern, const char *string, + int flags, int prefix); + #endif diff --git a/fetch-pack.c b/fetch-pack.c index 099ff4ddff..f0acdf7331 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -874,8 +874,6 @@ static int fetch_pack_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static struct lock_file lock; - static void fetch_pack_setup(void) { static int did_setup; @@ -917,6 +915,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args, ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile); if (args->depth > 0) { + static struct lock_file lock; struct cache_time mtime; struct strbuf sb = STRBUF_INIT; char *shallow = git_path("shallow"); @@ -142,6 +142,9 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) int has_null_sha1 = 0; int has_full_path = 0; int has_empty_name = 0; + int has_dot = 0; + int has_dotdot = 0; + int has_dotgit = 0; int has_zero_pad = 0; int has_bad_modes = 0; int has_dup_entries = 0; @@ -168,6 +171,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) has_full_path = 1; if (!*name) has_empty_name = 1; + if (!strcmp(name, ".")) + has_dot = 1; + if (!strcmp(name, "..")) + has_dotdot = 1; + if (!strcmp(name, ".git")) + has_dotgit = 1; has_zero_pad |= *(char *)desc.buffer == '0'; update_tree_entry(&desc); @@ -217,6 +226,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) retval += error_func(&item->object, FSCK_WARN, "contains full pathnames"); if (has_empty_name) retval += error_func(&item->object, FSCK_WARN, "contains empty pathname"); + if (has_dot) + retval += error_func(&item->object, FSCK_WARN, "contains '.'"); + if (has_dotdot) + retval += error_func(&item->object, FSCK_WARN, "contains '..'"); + if (has_dotgit) + retval += error_func(&item->object, FSCK_WARN, "contains '.git'"); if (has_zero_pad) retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes"); if (has_bad_modes) diff --git a/git-compat-util.h b/git-compat-util.h index 590d5d3188..e5a4b7450b 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -75,7 +75,7 @@ # endif #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \ !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \ - !defined(__TANDEM) + !defined(__TANDEM) && !defined(__QNX__) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #endif @@ -99,12 +99,14 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> -#ifdef __TANDEM /* or HAVE_STRINGS_H or !NO_STRINGS_H? */ +#ifdef HAVE_STRINGS_H #include <strings.h> /* for strcasecmp() */ #endif #include <errno.h> #include <limits.h> +#ifdef NEEDS_SYS_PARAM_H #include <sys/param.h> +#endif #include <sys/types.h> #include <dirent.h> #include <sys/time.h> @@ -288,6 +290,17 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); +/* + * Let callers be aware of the constant return value; this can help + * gcc with -Wuninitialized analysis. We have to restrict this trick to + * gcc, though, because of the variadic macro and the magic ## comma pasting + * behavior. But since we're only trying to help gcc, anyway, it's OK; other + * compilers will fall back to using the function as usual. + */ +#ifdef __GNUC__ +#define error(fmt, ...) (error((fmt), ##__VA_ARGS__), -1) +#endif + extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params)); extern void set_error_routine(void (*routine)(const char *err, va_list params)); @@ -411,6 +424,10 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif +#ifdef NO_GETPAGESIZE +#define getpagesize() sysconf(_SC_PAGESIZE) +#endif + #ifdef FREAD_READS_DIRECTORIES #ifdef fopen #undef fopen @@ -511,13 +528,19 @@ extern const char tolower_trans_tbl[256]; #undef isupper #undef tolower #undef toupper -extern unsigned char sane_ctype[256]; +#undef iscntrl +#undef ispunct +#undef isxdigit + +extern const unsigned char sane_ctype[256]; #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 #define GIT_ALPHA 0x04 #define GIT_GLOB_SPECIAL 0x08 #define GIT_REGEX_SPECIAL 0x10 #define GIT_PATHSPEC_MAGIC 0x20 +#define GIT_CNTRL 0x40 +#define GIT_PUNCT 0x80 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) #define isascii(x) (((x) & ~0x7f) == 0) #define isspace(x) sane_istest(x,GIT_SPACE) @@ -529,6 +552,10 @@ extern unsigned char sane_ctype[256]; #define isupper(x) sane_iscase(x, 0) #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL) +#define iscntrl(x) (sane_istest(x,GIT_CNTRL)) +#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \ + GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC) +#define isxdigit(x) (hexval_table[x] != -1) #define tolower(x) sane_case((unsigned char)(x), 0x20) #define toupper(x) sane_case((unsigned char)(x), 0) #define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC) @@ -8,7 +8,13 @@ # License: MIT <http://www.opensource.org/licenses/mit-license.php> # -import optparse, sys, os, marshal, subprocess, shelve +import sys +if sys.hexversion < 0x02040000: + # The limiter is the subprocess module + sys.stderr.write("git-p4: requires Python 2.4 or later.\n") + sys.exit(1) + +import optparse, os, marshal, subprocess, shelve import tempfile, getopt, os.path, time, platform import re, shutil diff --git a/git-rebase--am.sh b/git-rebase--am.sh index 392ebc9790..97f31dc7af 100644 --- a/git-rebase--am.sh +++ b/git-rebase--am.sh @@ -18,6 +18,7 @@ esac test -n "$rebase_root" && root_flag=--root +ret=0 if test -n "$keep_empty" then # we have to do this the hard way. git format-patch completely squashes @@ -25,13 +26,49 @@ then # itself well to recording empty patches. fortunately, cherry-pick # makes this easy git cherry-pick --allow-empty "$revisions" + ret=$? else + rm -f "$GIT_DIR/rebased-patches" + git format-patch -k --stdout --full-index --ignore-if-in-upstream \ --src-prefix=a/ --dst-prefix=b/ \ - --no-renames $root_flag "$revisions" | - git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" -fi && move_to_original_branch + --no-renames $root_flag "$revisions" >"$GIT_DIR/rebased-patches" + ret=$? + + if test 0 != $ret + then + rm -f "$GIT_DIR/rebased-patches" + case "$head_name" in + refs/heads/*) + git checkout -q "$head_name" + ;; + *) + git checkout -q "$orig_head" + ;; + esac + + cat >&2 <<-EOF + + git encountered an error while preparing the patches to replay + these revisions: + + $revisions + + As a result, git cannot rebase them. + EOF + exit $? + fi + + git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" <"$GIT_DIR/rebased-patches" + ret=$? + + rm -f "$GIT_DIR/rebased-patches" +fi + +if test 0 != $ret +then + test -d "$state_dir" && write_basic_state + exit $ret +fi -ret=$? -test 0 != $ret -a -d "$state_dir" && write_basic_state -exit $ret +move_to_original_branch diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 44901d53c4..8ed7fccc18 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -190,6 +190,11 @@ is_empty_commit() { test "$tree" = "$ptree" } +is_merge_commit() +{ + git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1 +} + # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and # GIT_AUTHOR_DATE exported from the current environment. do_with_author () { @@ -874,7 +879,7 @@ git rev-list $merges_option --pretty=oneline --abbrev-commit \ while read -r shortsha1 rest do - if test -z "$keep_empty" && is_empty_commit $shortsha1 + if test -z "$keep_empty" && is_empty_commit $shortsha1 && ! is_merge_commit $shortsha1 then comment_out="# " else diff --git a/git-remote-testgit b/git-remote-testgit new file mode 100755 index 0000000000..b395c8de59 --- /dev/null +++ b/git-remote-testgit @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# Copyright (c) 2012 Felipe Contreras + +alias=$1 +url=$2 + +dir="$GIT_DIR/testgit/$alias" +prefix="refs/testgit/$alias" + +default_refspec="refs/heads/*:${prefix}/heads/*" + +refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}" + +test -z "$refspec" && prefix="refs" + +export GIT_DIR="$url/.git" + +mkdir -p "$dir" + +if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS" +then + gitmarks="$dir/git.marks" + testgitmarks="$dir/testgit.marks" + test -e "$gitmarks" || >"$gitmarks" + test -e "$testgitmarks" || >"$testgitmarks" + testgitmarks_args=( "--"{import,export}"-marks=$testgitmarks" ) +fi + +while read line +do + case $line in + capabilities) + echo 'import' + echo 'export' + test -n "$refspec" && echo "refspec $refspec" + if test -n "$gitmarks" + then + echo "*import-marks $gitmarks" + echo "*export-marks $gitmarks" + fi + echo + ;; + list) + git for-each-ref --format='? %(refname)' 'refs/heads/' + head=$(git symbolic-ref HEAD) + echo "@$head HEAD" + echo + ;; + import*) + # read all import lines + while true + do + ref="${line#* }" + refs="$refs $ref" + read line + test "${line%% *}" != "import" && break + done + + if test -n "$gitmarks" + then + echo "feature import-marks=$gitmarks" + echo "feature export-marks=$gitmarks" + fi + echo "feature done" + git fast-export "${testgitmarks_args[@]}" $refs | + sed -e "s#refs/heads/#${prefix}/heads/#g" + echo "done" + ;; + export) + before=$(git for-each-ref --format='%(refname) %(objectname)') + + git fast-import "${testgitmarks_args[@]}" --quiet + + after=$(git for-each-ref --format='%(refname) %(objectname)') + + # figure out which refs were updated + join -e 0 -o '0 1.2 2.2' -a 2 <(echo "$before") <(echo "$after") | + while read ref a b + do + test $a == $b && continue + echo "ok $ref" + done + + echo + ;; + '') + exit + ;; + esac +done diff --git a/git-remote-testgit.py b/git-remote-testpy.py index 5f3ebd244d..d94a66a870 100644 --- a/git-remote-testgit.py +++ b/git-remote-testpy.py @@ -31,6 +31,11 @@ from git_remote_helpers.git.exporter import GitExporter from git_remote_helpers.git.importer import GitImporter from git_remote_helpers.git.non_local import NonLocalGit +if sys.hexversion < 0x01050200: + # os.makedirs() is the limiter + sys.stderr.write("git-remote-testgit: requires Python 1.5.2 or later.\n") + sys.exit(1) + def get_repo(alias, url): """Returns a git repository object initialized for usage. """ @@ -91,7 +96,7 @@ def do_capabilities(repo, args): if not os.path.exists(dirname): os.makedirs(dirname) - path = os.path.join(dirname, 'testgit.marks') + path = os.path.join(dirname, 'git.marks') print "*export-marks %s" % path if os.path.exists(path): @@ -159,6 +164,11 @@ def do_import(repo, args): ref = line[7:].strip() refs.append(ref) + print "feature done" + + if os.environ.get("GIT_REMOTE_TESTGIT_FAILURE"): + die('Told to fail') + repo = update_local_repo(repo) repo.exporter.export_repo(repo.gitdir, refs) @@ -172,6 +182,9 @@ def do_export(repo, args): if not repo.gitdir: die("Need gitdir to export") + if os.environ.get("GIT_REMOTE_TESTGIT_FAILURE"): + die('Told to fail') + update_local_repo(repo) changed = repo.importer.do_import(repo.gitdir) diff --git a/git-send-email.perl b/git-send-email.perl index 94c7f76a15..be809e5b59 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1285,10 +1285,10 @@ foreach my $t (@files) { } if (defined $input_format && $input_format eq 'mbox') { - if (/^Subject:\s+(.*)$/) { + if (/^Subject:\s+(.*)$/i) { $subject = $1; } - elsif (/^From:\s+(.*)$/) { + elsif (/^From:\s+(.*)$/i) { ($author, $author_encoding) = unquote_rfc2047($1); next if $suppress_cc{'author'}; next if $suppress_cc{'self'} and $author eq $sender; @@ -1296,14 +1296,14 @@ foreach my $t (@files) { $1, $_) unless $quiet; push @cc, $1; } - elsif (/^To:\s+(.*)$/) { + elsif (/^To:\s+(.*)$/i) { foreach my $addr (parse_address_line($1)) { printf("(mbox) Adding to: %s from line '%s'\n", $addr, $_) unless $quiet; push @to, $addr; } } - elsif (/^Cc:\s+(.*)$/) { + elsif (/^Cc:\s+(.*)$/i) { foreach my $addr (parse_address_line($1)) { if (unquote_rfc2047($addr) eq $sender) { next if ($suppress_cc{'self'}); @@ -1325,7 +1325,7 @@ foreach my $t (@files) { elsif (/^Message-Id: (.*)/i) { $message_id = $1; } - elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { + elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) { push @xh, $_; } diff --git a/git-submodule.sh b/git-submodule.sh index 2365149d0b..22ec5b63b4 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -8,7 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /') USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>] or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] or: $dashless [--quiet] init [--] [<path>...] - or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] + or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] or: $dashless [--quiet] foreach [--recursive] <command> or: $dashless [--quiet] sync [--recursive] [--] [<path>...]" @@ -26,6 +26,7 @@ cached= recursive= init= files= +remote= nofetch= update= prefix= @@ -153,6 +154,32 @@ die_if_unmatched () } # +# Print a submodule configuration setting +# +# $1 = submodule name +# $2 = option name +# $3 = default value +# +# Checks in the usual git-config places first (for overrides), +# otherwise it falls back on .gitmodules. This allows you to +# distribute project-wide defaults in .gitmodules, while still +# customizing individual repositories if necessary. If the option is +# not in .gitmodules either, print a default value. +# +get_submodule_config () { + name="$1" + option="$2" + default="$3" + value=$(git config submodule."$name"."$option") + if test -z "$value" + then + value=$(git config -f .gitmodules submodule."$name"."$option") + fi + printf '%s' "${value:-$default}" +} + + +# # Map submodule path to submodule name # # $1 = path @@ -390,6 +417,10 @@ Use -f if you really want to add it." >&2 git config -f .gitmodules submodule."$sm_name".path "$sm_path" && git config -f .gitmodules submodule."$sm_name".url "$repo" && + if test -n "$branch" + then + git config -f .gitmodules submodule."$sm_name".branch "$branch" + fi && git add --force .gitmodules || die "$(eval_gettext "Failed to register submodule '\$sm_path'")" } @@ -533,6 +564,9 @@ cmd_update() -i|--init) init=1 ;; + --remote) + remote=1 + ;; -N|--no-fetch) nofetch=1 ;; @@ -593,6 +627,7 @@ cmd_update() fi name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) + branch=$(get_submodule_config "$name" branch master) if ! test -z "$update" then update_module=$update @@ -627,6 +662,20 @@ Maybe you want to use 'update --init'?")" die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")" fi + if test -n "$remote" + then + if test -z "$nofetch" + then + # Fetch remote before determining tracking $sha1 + (clear_local_git_env; cd "$sm_path" && git-fetch) || + die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" + fi + remote_name=$(clear_local_git_env; cd "$sm_path" && get_default_remote) + sha1=$(clear_local_git_env; cd "$sm_path" && + git rev-parse --verify "${remote_name}/${branch}") || + die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")" + fi + if test "$subsha1" != "$sha1" -o -n "$force" then subforce=$force diff --git a/git-svn.perl b/git-svn.perl index bd5266c86b..d0866946ce 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -114,6 +114,7 @@ my ($_stdin, $_help, $_edit, $_message, $_file, $_branch_dest, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_fetch_parent, + $_before, $_after, $_merge, $_strategy, $_preserve_merges, $_dry_run, $_local, $_prefix, $_no_checkout, $_url, $_verbose, $_commit_url, $_tag, $_merge_info, $_interactive); @@ -258,7 +259,8 @@ my %cmd = ( } ], 'find-rev' => [ \&cmd_find_rev, "Translate between SVN revision numbers and tree-ish", - {} ], + { 'before' => \$_before, + 'after' => \$_after } ], 'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory", { 'merge|m|M' => \$_merge, 'verbose|v' => \$_verbose, @@ -1191,7 +1193,13 @@ sub cmd_find_rev { "$head history\n"; } my $desired_revision = substr($revision_or_hash, 1); - $result = $gs->rev_map_get($desired_revision, $uuid); + if ($_before) { + $result = $gs->find_rev_before($desired_revision, 1); + } elsif ($_after) { + $result = $gs->find_rev_after($desired_revision, 1); + } else { + $result = $gs->rev_map_get($desired_revision, $uuid); + } } else { my (undef, $rev, undef) = cmt_metadata($revision_or_hash); $result = $rev; @@ -135,6 +135,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) git_config_push_parameter((*argv)[1]); (*argv)++; (*argc)--; + } else if (!strcmp(cmd, "--literal-pathspecs")) { + setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; + } else if (!strcmp(cmd, "--no-literal-pathspecs")) { + setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1); + if (envchanged) + *envchanged = 1; } else { fprintf(stderr, "Unknown option: %s\n", cmd); usage(git_usage_string); diff --git a/git_remote_helpers/git/__init__.py b/git_remote_helpers/git/__init__.py index e69de29bb2..1dbb1b0148 100644 --- a/git_remote_helpers/git/__init__.py +++ b/git_remote_helpers/git/__init__.py @@ -0,0 +1,5 @@ +import sys +if sys.hexversion < 0x02040000: + # The limiter is the subprocess module + sys.stderr.write("git_remote_helpers: requires Python 2.4 or later.\n") + sys.exit(1) diff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py index 5c6b595e16..e28cc8f986 100644 --- a/git_remote_helpers/git/importer.py +++ b/git_remote_helpers/git/importer.py @@ -39,7 +39,7 @@ class GitImporter(object): gitdir = self.repo.gitpath else: gitdir = os.path.abspath(os.path.join(dirname, '.git')) - path = os.path.abspath(os.path.join(dirname, 'git.marks')) + path = os.path.abspath(os.path.join(dirname, 'testgit.marks')) if not os.path.exists(dirname): os.makedirs(dirname) diff --git a/http-push.c b/http-push.c index 8701c1215d..9923441a4e 100644 --- a/http-push.c +++ b/http-push.c @@ -172,28 +172,7 @@ enum dav_header_flag { static char *xml_entities(const char *s) { struct strbuf buf = STRBUF_INIT; - while (*s) { - size_t len = strcspn(s, "\"<>&"); - strbuf_add(&buf, s, len); - s += len; - switch (*s) { - case '"': - strbuf_addstr(&buf, """); - break; - case '<': - strbuf_addstr(&buf, "<"); - break; - case '>': - strbuf_addstr(&buf, ">"); - break; - case '&': - strbuf_addstr(&buf, "&"); - break; - case 0: - return strbuf_detach(&buf, NULL); - } - s++; - } + strbuf_addstr_xml_quoted(&buf, s); return strbuf_detach(&buf, NULL); } diff --git a/imap-send.c b/imap-send.c index d42e471297..e521e2fd22 100644 --- a/imap-send.c +++ b/imap-send.c @@ -69,8 +69,7 @@ struct store { }; struct msg_data { - char *data; - int len; + struct strbuf data; unsigned char flags; }; @@ -1264,45 +1263,49 @@ static int imap_make_flags(int flags, char *buf) return d; } -static void lf_to_crlf(struct msg_data *msg) +static void lf_to_crlf(struct strbuf *msg) { + size_t new_len; char *new; int i, j, lfnum = 0; - if (msg->data[0] == '\n') + if (msg->buf[0] == '\n') lfnum++; for (i = 1; i < msg->len; i++) { - if (msg->data[i - 1] != '\r' && msg->data[i] == '\n') + if (msg->buf[i - 1] != '\r' && msg->buf[i] == '\n') lfnum++; } - new = xmalloc(msg->len + lfnum); - if (msg->data[0] == '\n') { + new_len = msg->len + lfnum; + new = xmalloc(new_len + 1); + if (msg->buf[0] == '\n') { new[0] = '\r'; new[1] = '\n'; i = 1; j = 2; } else { - new[0] = msg->data[0]; + new[0] = msg->buf[0]; i = 1; j = 1; } for ( ; i < msg->len; i++) { - if (msg->data[i] != '\n') { - new[j++] = msg->data[i]; + if (msg->buf[i] != '\n') { + new[j++] = msg->buf[i]; continue; } - if (msg->data[i - 1] != '\r') + if (msg->buf[i - 1] != '\r') new[j++] = '\r'; /* otherwise it already had CR before */ new[j++] = '\n'; } - msg->len += lfnum; - free(msg->data); - msg->data = new; + strbuf_attach(msg, new, new_len, new_len + 1); } -static int imap_store_msg(struct store *gctx, struct msg_data *data) +/* + * Store msg to IMAP. Also detach and free the data from msg->data, + * leaving msg->data empty. + */ +static int imap_store_msg(struct store *gctx, struct msg_data *msg) { struct imap_store *ctx = (struct imap_store *)gctx; struct imap *imap = ctx->imap; @@ -1311,16 +1314,15 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data) int ret, d; char flagstr[128]; - lf_to_crlf(data); + lf_to_crlf(&msg->data); memset(&cb, 0, sizeof(cb)); - cb.dlen = data->len; - cb.data = xmalloc(cb.dlen); - memcpy(cb.data, data->data, data->len); + cb.dlen = msg->data.len; + cb.data = strbuf_detach(&msg->data, NULL); d = 0; - if (data->flags) { - d = imap_make_flags(data->flags, flagstr); + if (msg->flags) { + d = imap_make_flags(msg->flags, flagstr); flagstr[d++] = ' '; } flagstr[d] = 0; @@ -1337,75 +1339,46 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data) return DRV_OK; } -static void encode_html_chars(struct strbuf *p) -{ - int i; - for (i = 0; i < p->len; i++) { - if (p->buf[i] == '&') - strbuf_splice(p, i, 1, "&", 5); - if (p->buf[i] == '<') - strbuf_splice(p, i, 1, "<", 4); - if (p->buf[i] == '>') - strbuf_splice(p, i, 1, ">", 4); - if (p->buf[i] == '"') - strbuf_splice(p, i, 1, """, 6); - } -} -static void wrap_in_html(struct msg_data *msg) +static void wrap_in_html(struct strbuf *msg) { struct strbuf buf = STRBUF_INIT; - struct strbuf **lines; - struct strbuf **p; static char *content_type = "Content-Type: text/html;\n"; static char *pre_open = "<pre>\n"; static char *pre_close = "</pre>\n"; - int added_header = 0; - - strbuf_attach(&buf, msg->data, msg->len, msg->len); - lines = strbuf_split(&buf, '\n'); - strbuf_release(&buf); - for (p = lines; *p; p++) { - if (! added_header) { - if ((*p)->len == 1 && *((*p)->buf) == '\n') { - strbuf_addstr(&buf, content_type); - strbuf_addbuf(&buf, *p); - strbuf_addstr(&buf, pre_open); - added_header = 1; - continue; - } - } - else - encode_html_chars(*p); - strbuf_addbuf(&buf, *p); - } + const char *body = strstr(msg->buf, "\n\n"); + + if (!body) + return; /* Headers but no body; no wrapping needed */ + + body += 2; + + strbuf_add(&buf, msg->buf, body - msg->buf - 1); + strbuf_addstr(&buf, content_type); + strbuf_addch(&buf, '\n'); + strbuf_addstr(&buf, pre_open); + strbuf_addstr_xml_quoted(&buf, body); strbuf_addstr(&buf, pre_close); - strbuf_list_free(lines); - msg->len = buf.len; - msg->data = strbuf_detach(&buf, NULL); + + strbuf_release(msg); + *msg = buf; } #define CHUNKSIZE 0x1000 -static int read_message(FILE *f, struct msg_data *msg) +static int read_message(FILE *f, struct strbuf *all_msgs) { - struct strbuf buf = STRBUF_INIT; - - memset(msg, 0, sizeof(*msg)); - do { - if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0) + if (strbuf_fread(all_msgs, CHUNKSIZE, f) <= 0) break; } while (!feof(f)); - msg->len = buf.len; - msg->data = strbuf_detach(&buf, NULL); - return msg->len; + return ferror(f) ? -1 : 0; } -static int count_messages(struct msg_data *msg) +static int count_messages(struct strbuf *all_msgs) { int count = 0; - char *p = msg->data; + char *p = all_msgs->buf; while (1) { if (!prefixcmp(p, "From ")) { @@ -1426,34 +1399,39 @@ static int count_messages(struct msg_data *msg) return count; } -static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs) +/* + * Copy the next message from all_msgs, starting at offset *ofs, to + * msg. Update *ofs to the start of the following message. Return + * true iff a message was successfully copied. + */ +static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) { char *p, *data; + size_t len; - memset(msg, 0, sizeof *msg); if (*ofs >= all_msgs->len) return 0; - data = &all_msgs->data[*ofs]; - msg->len = all_msgs->len - *ofs; + data = &all_msgs->buf[*ofs]; + len = all_msgs->len - *ofs; - if (msg->len < 5 || prefixcmp(data, "From ")) + if (len < 5 || prefixcmp(data, "From ")) return 0; p = strchr(data, '\n'); if (p) { - p = &p[1]; - msg->len -= p-data; - *ofs += p-data; + p++; + len -= p - data; + *ofs += p - data; data = p; } p = strstr(data, "\nFrom "); if (p) - msg->len = &p[1] - data; + len = &p[1] - data; - msg->data = xmemdupz(data, msg->len); - *ofs += msg->len; + strbuf_add(msg, data, len); + *ofs += len; return 1; } @@ -1504,7 +1482,8 @@ static int git_imap_config(const char *key, const char *val, void *cb) int main(int argc, char **argv) { - struct msg_data all_msgs, msg; + struct strbuf all_msgs = STRBUF_INIT; + struct msg_data msg = {STRBUF_INIT, 0}; struct store *ctx = NULL; int ofs = 0; int r; @@ -1537,7 +1516,12 @@ int main(int argc, char **argv) } /* read the messages */ - if (!read_message(stdin, &all_msgs)) { + if (read_message(stdin, &all_msgs)) { + fprintf(stderr, "error reading input\n"); + return 1; + } + + if (all_msgs.len == 0) { fprintf(stderr, "nothing to send\n"); return 1; } @@ -1559,11 +1543,12 @@ int main(int argc, char **argv) ctx->name = imap_folder; while (1) { unsigned percent = n * 100 / total; + fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); - if (!split_msg(&all_msgs, &msg, &ofs)) + if (!split_msg(&all_msgs, &msg.data, &ofs)) break; if (server.use_html) - wrap_in_html(&msg); + wrap_in_html(&msg.data); r = imap_store_msg(ctx, &msg); if (r != DRV_OK) break; diff --git a/log-tree.c b/log-tree.c index 4f86defe32..5dc45c4812 100644 --- a/log-tree.c +++ b/log-tree.c @@ -299,26 +299,34 @@ static unsigned int digits_in_number(unsigned int number) return result; } -void get_patch_filename(struct commit *commit, const char *subject, int nr, - const char *suffix, struct strbuf *buf) +void fmt_output_subject(struct strbuf *filename, + const char *subject, + struct rev_info *info) { - int suffix_len = strlen(suffix) + 1; - int start_len = buf->len; - - strbuf_addf(buf, commit || subject ? "%04d-" : "%d", nr); - if (commit || subject) { - int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len; - struct pretty_print_context ctx = {0}; - - if (subject) - strbuf_addstr(buf, subject); - else if (commit) - format_commit_message(commit, "%f", buf, &ctx); - - if (max_len < buf->len) - strbuf_setlen(buf, max_len); - strbuf_addstr(buf, suffix); - } + const char *suffix = info->patch_suffix; + int nr = info->nr; + int start_len = filename->len; + int max_len = start_len + FORMAT_PATCH_NAME_MAX - (strlen(suffix) + 1); + + if (0 < info->reroll_count) + strbuf_addf(filename, "v%d-", info->reroll_count); + strbuf_addf(filename, "%04d-%s", nr, subject); + + if (max_len < filename->len) + strbuf_setlen(filename, max_len); + strbuf_addstr(filename, suffix); +} + +void fmt_output_commit(struct strbuf *filename, + struct commit *commit, + struct rev_info *info) +{ + struct pretty_print_context ctx = {0}; + struct strbuf subject = STRBUF_INIT; + + format_commit_message(commit, "%f", &subject, &ctx); + fmt_output_subject(filename, subject.buf, info); + strbuf_release(&subject); } void log_write_email_headers(struct rev_info *opt, struct commit *commit, @@ -387,8 +395,10 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, mime_boundary_leader, opt->mime_boundary); extra_headers = subject_buffer; - get_patch_filename(opt->numbered_files ? NULL : commit, NULL, - opt->nr, opt->patch_suffix, &filename); + if (opt->numbered_files) + strbuf_addf(&filename, "%d", opt->nr); + else + fmt_output_commit(&filename, commit, opt); snprintf(buffer, sizeof(buffer) - 1, "\n--%s%s\n" "Content-Type: text/x-patch;" @@ -671,6 +681,8 @@ void show_log(struct rev_info *opt) ctx.preserve_subject = opt->preserve_subject; ctx.reflog_info = opt->reflog_info; ctx.fmt = opt->commit_format; + ctx.mailmap = opt->mailmap; + ctx.color = opt->diffopt.use_color; pretty_print_commit(&ctx, commit, &msgbuf); if (opt->add_signoff) diff --git a/log-tree.h b/log-tree.h index f5ac238bba..9140f48216 100644 --- a/log-tree.h +++ b/log-tree.h @@ -21,7 +21,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, void load_ref_decorations(int flags); #define FORMAT_PATCH_NAME_MAX 64 -void get_patch_filename(struct commit *commit, const char *subject, int nr, - const char *suffix, struct strbuf *buf); +void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *); +void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *); #endif @@ -10,6 +10,7 @@ static inline void debug_mm(const char *format, ...) {} #endif const char *git_mailmap_file; +const char *git_mailmap_blob; struct mailmap_info { char *name; @@ -129,54 +130,120 @@ static char *parse_name_and_email(char *buffer, char **name, return (*right == '\0' ? NULL : right); } -static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev) +static void read_mailmap_line(struct string_list *map, char *buffer, + char **repo_abbrev) +{ + char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL; + if (buffer[0] == '#') { + static const char abbrev[] = "# repo-abbrev:"; + int abblen = sizeof(abbrev) - 1; + int len = strlen(buffer); + + if (!repo_abbrev) + return; + + if (len && buffer[len - 1] == '\n') + buffer[--len] = 0; + if (!strncmp(buffer, abbrev, abblen)) { + char *cp; + + if (repo_abbrev) + free(*repo_abbrev); + *repo_abbrev = xmalloc(len); + + for (cp = buffer + abblen; isspace(*cp); cp++) + ; /* nothing */ + strcpy(*repo_abbrev, cp); + } + return; + } + if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) + parse_name_and_email(name2, &name2, &email2, 1); + + if (email1) + add_mapping(map, name1, email1, name2, email2); +} + +static int read_mailmap_file(struct string_list *map, const char *filename, + char **repo_abbrev) { char buffer[1024]; - FILE *f = (filename == NULL ? NULL : fopen(filename, "r")); + FILE *f; - if (f == NULL) - return 1; - while (fgets(buffer, sizeof(buffer), f) != NULL) { - char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL; - if (buffer[0] == '#') { - static const char abbrev[] = "# repo-abbrev:"; - int abblen = sizeof(abbrev) - 1; - int len = strlen(buffer); - - if (!repo_abbrev) - continue; - - if (len && buffer[len - 1] == '\n') - buffer[--len] = 0; - if (!strncmp(buffer, abbrev, abblen)) { - char *cp; - - if (repo_abbrev) - free(*repo_abbrev); - *repo_abbrev = xmalloc(len); - - for (cp = buffer + abblen; isspace(*cp); cp++) - ; /* nothing */ - strcpy(*repo_abbrev, cp); - } - continue; - } - if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) - parse_name_and_email(name2, &name2, &email2, 1); + if (!filename) + return 0; - if (email1) - add_mapping(map, name1, email1, name2, email2); + f = fopen(filename, "r"); + if (!f) { + if (errno == ENOENT) + return 0; + return error("unable to open mailmap at %s: %s", + filename, strerror(errno)); } + + while (fgets(buffer, sizeof(buffer), f) != NULL) + read_mailmap_line(map, buffer, repo_abbrev); fclose(f); return 0; } +static void read_mailmap_buf(struct string_list *map, + const char *buf, unsigned long len, + char **repo_abbrev) +{ + while (len) { + const char *end = strchrnul(buf, '\n'); + unsigned long linelen = end - buf + 1; + char *line = xmemdupz(buf, linelen); + + read_mailmap_line(map, line, repo_abbrev); + + free(line); + buf += linelen; + len -= linelen; + } +} + +static int read_mailmap_blob(struct string_list *map, + const char *name, + char **repo_abbrev) +{ + unsigned char sha1[20]; + char *buf; + unsigned long size; + enum object_type type; + + if (!name) + return 0; + if (get_sha1(name, sha1) < 0) + return 0; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("unable to read mailmap object at %s", name); + if (type != OBJ_BLOB) + return error("mailmap is not a blob: %s", name); + + read_mailmap_buf(map, buf, size, repo_abbrev); + + free(buf); + return 0; +} + int read_mailmap(struct string_list *map, char **repo_abbrev) { + int err = 0; + map->strdup_strings = 1; - /* each failure returns 1, so >1 means both calls failed */ - return read_single_mailmap(map, ".mailmap", repo_abbrev) + - read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1; + map->cmp = strcasecmp; + + if (!git_mailmap_blob && is_bare_repository()) + git_mailmap_blob = "HEAD:.mailmap"; + + err |= read_mailmap_file(map, ".mailmap", repo_abbrev); + err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev); + err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev); + return err; } void clear_mailmap(struct string_list *map) @@ -187,60 +254,95 @@ void clear_mailmap(struct string_list *map) debug_mm("mailmap: cleared\n"); } +/* + * Look for an entry in map that match string[0:len]; string[len] + * does not have to be NUL (but it could be). + */ +static struct string_list_item *lookup_prefix(struct string_list *map, + const char *string, size_t len) +{ + int i = string_list_find_insert_index(map, string, 1); + if (i < 0) { + /* exact match */ + i = -1 - i; + if (!string[len]) + return &map->items[i]; + /* + * that map entry matches exactly to the string, including + * the cruft at the end beyond "len". That is not a match + * with string[0:len] that we are looking for. + */ + } else if (!string[len]) { + /* + * asked with the whole string, and got nothing. No + * matching entry can exist in the map. + */ + return NULL; + } + + /* + * i is at the exact match to an overlong key, or location the + * overlong key would be inserted, which must come after the + * real location of the key if one exists. + */ + while (0 <= --i && i < map->nr) { + int cmp = strncasecmp(map->items[i].string, string, len); + if (cmp < 0) + /* + * "i" points at a key definitely below the prefix; + * the map does not have string[0:len] in it. + */ + break; + else if (!cmp && !map->items[i].string[len]) + /* found it */ + return &map->items[i]; + /* + * otherwise, the string at "i" may be string[0:len] + * followed by a string that sorts later than string[len:]; + * keep trying. + */ + } + return NULL; +} + int map_user(struct string_list *map, - char *email, int maxlen_email, char *name, int maxlen_name) + const char **email, size_t *emaillen, + const char **name, size_t *namelen) { - char *end_of_email; struct string_list_item *item; struct mailmap_entry *me; - char buf[1024], *mailbuf; - int i; - - /* figure out space requirement for email */ - end_of_email = strchr(email, '>'); - if (!end_of_email) { - /* email passed in might not be wrapped in <>, but end with a \0 */ - end_of_email = memchr(email, '\0', maxlen_email); - if (!end_of_email) - return 0; - } - if (end_of_email - email + 1 < sizeof(buf)) - mailbuf = buf; - else - mailbuf = xmalloc(end_of_email - email + 1); - - /* downcase the email address */ - for (i = 0; i < end_of_email - email; i++) - mailbuf[i] = tolower(email[i]); - mailbuf[i] = 0; - - debug_mm("map_user: map '%s' <%s>\n", name, mailbuf); - item = string_list_lookup(map, mailbuf); + + debug_mm("map_user: map '%.*s' <%.*s>\n", + *name, *namelen, *emaillen, *email); + + item = lookup_prefix(map, *email, *emaillen); if (item != NULL) { me = (struct mailmap_entry *)item->util; if (me->namemap.nr) { /* The item has multiple items, so we'll look up on name too */ /* If the name is not found, we choose the simple entry */ - struct string_list_item *subitem = string_list_lookup(&me->namemap, name); + struct string_list_item *subitem; + subitem = lookup_prefix(&me->namemap, *name, *namelen); if (subitem) item = subitem; } } - if (mailbuf != buf) - free(mailbuf); if (item != NULL) { struct mailmap_info *mi = (struct mailmap_info *)item->util; - if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) { + if (mi->name == NULL && mi->email == NULL) { debug_mm("map_user: -- (no simple mapping)\n"); return 0; } - if (maxlen_email && mi->email) - strlcpy(email, mi->email, maxlen_email); - else - *end_of_email = '\0'; - if (maxlen_name && mi->name) - strlcpy(name, mi->name, maxlen_name); - debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? mi->email : ""); + if (mi->email) { + *email = mi->email; + *emaillen = strlen(*email); + } + if (mi->name) { + *name = mi->name; + *namelen = strlen(*name); + } + debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name, + *emaillen, *email); return 1; } debug_mm("map_user: --\n"); @@ -4,7 +4,7 @@ int read_mailmap(struct string_list *map, char **repo_abbrev); void clear_mailmap(struct string_list *map); -int map_user(struct string_list *mailmap, - char *email, int maxlen_email, char *name, int maxlen_name); +int map_user(struct string_list *map, + const char **email, size_t *emaillen, const char **name, size_t *namelen); #endif diff --git a/merge-file.c b/merge-blobs.c index 7845528e88..57211bccb7 100644 --- a/merge-file.c +++ b/merge-blobs.c @@ -3,7 +3,7 @@ #include "xdiff-interface.h" #include "ll-merge.h" #include "blob.h" -#include "merge-file.h" +#include "merge-blobs.h" static int fill_mmfile_blob(mmfile_t *f, struct blob *obj) { @@ -80,7 +80,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2) return xdi_diff(f1, f2, &xpp, &xecfg, &ecb); } -void *merge_file(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size) +void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size) { void *res = NULL; mmfile_t f1, f2, common; diff --git a/merge-blobs.h b/merge-blobs.h new file mode 100644 index 0000000000..62b569e472 --- /dev/null +++ b/merge-blobs.h @@ -0,0 +1,8 @@ +#ifndef MERGE_BLOBS_H +#define MERGE_BLOBS_H + +#include "blob.h" + +extern void *merge_blobs(const char *, struct blob *, struct blob *, struct blob *, unsigned long *); + +#endif /* MERGE_BLOBS_H */ diff --git a/merge-file.h b/merge-file.h deleted file mode 100644 index 9b3b83ac6c..0000000000 --- a/merge-file.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef MERGE_FILE_H -#define MERGE_FILE_H - -extern void *merge_file(const char *path, struct blob *base, struct blob *our, - struct blob *their, unsigned long *size); - -#endif diff --git a/merge-recursive.c b/merge-recursive.c index d8820604ca..33ba5dc07c 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -976,7 +976,7 @@ merge_file_special_markers(struct merge_options *o, return mfi; } -static struct merge_file_info merge_file(struct merge_options *o, +static struct merge_file_info merge_file_one(struct merge_options *o, const char *path, const unsigned char *o_sha, int o_mode, const unsigned char *a_sha, int a_mode, @@ -1166,7 +1166,7 @@ static void conflict_rename_rename_1to2(struct merge_options *o, struct merge_file_info mfi; struct diff_filespec other; struct diff_filespec *add; - mfi = merge_file(o, one->path, + mfi = merge_file_one(o, one->path, one->sha1, one->mode, a->sha1, a->mode, b->sha1, b->mode, @@ -1450,7 +1450,7 @@ static int process_renames(struct merge_options *o, ren1_dst, branch2); if (o->call_depth) { struct merge_file_info mfi; - mfi = merge_file(o, ren1_dst, null_sha1, 0, + mfi = merge_file_one(o, ren1_dst, null_sha1, 0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_other.sha1, dst_other.mode, branch1, branch2); diff --git a/mergetools/p4merge b/mergetools/p4merge index 295361a8aa..52f7c8f705 100644 --- a/mergetools/p4merge +++ b/mergetools/p4merge @@ -1,29 +1,21 @@ diff_cmd () { + empty_file= + # p4merge does not like /dev/null - rm_local= - rm_remote= if test "/dev/null" = "$LOCAL" then - LOCAL="./p4merge-dev-null.LOCAL.$$" - >"$LOCAL" - rm_local=true + LOCAL="$(create_empty_file)" fi if test "/dev/null" = "$REMOTE" then - REMOTE="./p4merge-dev-null.REMOTE.$$" - >"$REMOTE" - rm_remote=true + REMOTE="$(create_empty_file)" fi "$merge_tool_path" "$LOCAL" "$REMOTE" - if test -n "$rm_local" - then - rm -f "$LOCAL" - fi - if test -n "$rm_remote" + if test -n "$empty_file" then - rm -f "$REMOTE" + rm -f "$empty_file" fi } @@ -33,3 +25,10 @@ merge_cmd () { "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED" check_unchanged } + +create_empty_file () { + empty_file="${TMPDIR:-/tmp}/git-difftool-p4merge-empty-file.$$" + >"$empty_file" + + printf "$empty_file" +} diff --git a/parse-options.c b/parse-options.c index c1c66bd408..67e98a6323 100644 --- a/parse-options.c +++ b/parse-options.c @@ -18,15 +18,6 @@ int optbug(const struct option *opt, const char *reason) return error("BUG: switch '%c' %s", opt->short_name, reason); } -int opterror(const struct option *opt, const char *reason, int flags) -{ - if (flags & OPT_SHORT) - return error("switch `%c' %s", opt->short_name, reason); - if (flags & OPT_UNSET) - return error("option `no-%s' %s", opt->long_name, reason); - return error("option `%s' %s", opt->long_name, reason); -} - static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, int flags, const char **arg) { @@ -594,3 +585,12 @@ static int parse_options_usage(struct parse_opt_ctx_t *ctx, return usage_with_options_internal(ctx, usagestr, opts, 0, err); } +#undef opterror +int opterror(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + return error("switch `%c' %s", opt->short_name, reason); + if (flags & OPT_UNSET) + return error("option `no-%s' %s", opt->long_name, reason); + return error("option `%s' %s", opt->long_name, reason); +} diff --git a/parse-options.h b/parse-options.h index 71a39c60d9..e703853749 100644 --- a/parse-options.h +++ b/parse-options.h @@ -177,6 +177,10 @@ extern NORETURN void usage_msg_opt(const char *msg, extern int optbug(const struct option *opt, const char *reason); extern int opterror(const struct option *opt, const char *reason, int flags); +#ifdef __GNUC__ +#define opterror(o,r,f) (opterror((o),(r),(f)), -1) +#endif + /*----- incremental advanced APIs -----*/ enum { @@ -12,6 +12,7 @@ */ #include "cache.h" #include "strbuf.h" +#include "string-list.h" static char bad_path[] = "/bad-path/"; @@ -569,43 +570,38 @@ int normalize_path_copy(char *dst, const char *src) /* * path = Canonical absolute path - * prefix_list = Colon-separated list of absolute paths + * prefixes = string_list containing normalized, absolute paths without + * trailing slashes (except for the root directory, which is denoted by "/"). * - * Determines, for each path in prefix_list, whether the "prefix" really + * Determines, for each path in prefixes, whether the "prefix" * is an ancestor directory of path. Returns the length of the longest * ancestor directory, excluding any trailing slashes, or -1 if no prefix - * is an ancestor. (Note that this means 0 is returned if prefix_list is - * "/".) "/foo" is not considered an ancestor of "/foobar". Directories + * is an ancestor. (Note that this means 0 is returned if prefixes is + * ["/"].) "/foo" is not considered an ancestor of "/foobar". Directories * are not considered to be their own ancestors. path must be in a * canonical form: empty components, or "." or ".." components are not - * allowed. prefix_list may be null, which is like "". + * allowed. */ -int longest_ancestor_length(const char *path, const char *prefix_list) +int longest_ancestor_length(const char *path, struct string_list *prefixes) { - char buf[PATH_MAX+1]; - const char *ceil, *colon; - int len, max_len = -1; + int i, max_len = -1; - if (prefix_list == NULL || !strcmp(path, "/")) + if (!strcmp(path, "/")) return -1; - for (colon = ceil = prefix_list; *colon; ceil = colon+1) { - for (colon = ceil; *colon && *colon != PATH_SEP; colon++); - len = colon - ceil; - if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) - continue; - strlcpy(buf, ceil, len+1); - if (normalize_path_copy(buf, buf) < 0) - continue; - len = strlen(buf); - if (len > 0 && buf[len-1] == '/') - buf[--len] = '\0'; + for (i = 0; i < prefixes->nr; i++) { + const char *ceil = prefixes->items[i].string; + int len = strlen(ceil); - if (!strncmp(path, buf, len) && - path[len] == '/' && - len > max_len) { + if (len == 1 && ceil[0] == '/') + len = 0; /* root matches anything, with length 0 */ + else if (!strncmp(path, ceil, len) && path[len] == '/') + ; /* match of length len */ + else + continue; /* no match */ + + if (len > max_len) max_len = len; - } } return max_len; diff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm index 3bbc20a054..3db152155c 100644 --- a/perl/Git/SVN/Editor.pm +++ b/perl/Git/SVN/Editor.pm @@ -145,7 +145,8 @@ sub repo_path { sub url_path { my ($self, $path) = @_; if ($self->{url} =~ m#^https?://#) { - $path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg; + # characters are taken from subversion/libsvn_subr/path.c + $path =~ s#([^~a-zA-Z0-9_./!$&'()*+,-])#uc sprintf("%%%02x",ord($1))#eg; } $self->{url} . '/' . $self->repo_path($path); } @@ -358,12 +359,12 @@ sub T { mode_a => $m->{mode_a}, mode_b => '000000', sha1_a => $m->{sha1_a}, sha1_b => '0' x 40, chg => 'D', file_b => $m->{file_b} - }); + }, $deletions); $self->A({ mode_a => '000000', mode_b => $m->{mode_b}, sha1_a => '0' x 40, sha1_b => $m->{sha1_b}, chg => 'A', file_b => $m->{file_b} - }); + }, $deletions); return; } diff --git a/perl/Git/SVN/Utils.pm b/perl/Git/SVN/Utils.pm index 8b8cf3755c..3d1a0933a2 100644 --- a/perl/Git/SVN/Utils.pm +++ b/perl/Git/SVN/Utils.pm @@ -155,7 +155,7 @@ sub _canonicalize_url_path { my @parts; foreach my $part (split m{/+}, $uri_path) { - $part =~ s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; + $part =~ s/([^!\$%&'()*+,.\/\w:=\@_`~-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; push @parts, $part; } @@ -33,10 +33,10 @@ msgid "" "appropriate to mark resolution and make a commit,\n" "or use 'git commit -a'." msgstr "" -"Korrigiere dies im Arbeitsbaum,\n" -"und benutze dann 'git add/rm <Datei>'\n" +"Korrigieren Sie dies im Arbeitsbaum,\n" +"und benutzen Sie dann 'git add/rm <Datei>'\n" "um die Auflösung entsprechend zu markieren und einzutragen,\n" -"oder benutze 'git commit -a'." +"oder benutzen Sie 'git commit -a'." #: archive.c:10 msgid "git archive [options] <tree-ish> [<path>...]" @@ -131,7 +131,7 @@ msgid "" "Use '\\!' for literal leading exclamation." msgstr "" "Verneinende Muster sind in Git-Attributen verboten.\n" -"Benutze '\\!' für führende Ausrufezeichen." +"Benutzen Sie '\\!' für führende Ausrufezeichen." #: bundle.c:36 #, c-format @@ -381,7 +381,7 @@ msgstr "Vorhandene Git-Kommandos in '%s'" #: help.c:219 msgid "git commands available from elsewhere on your $PATH" -msgstr "Vorhandene Git-Kommandos irgendwo in deinem $PATH" +msgstr "Vorhandene Git-Kommandos irgendwo in Ihrem $PATH" #: help.c:275 #, c-format @@ -394,7 +394,7 @@ msgstr "" #: help.c:332 msgid "Uh oh. Your system reports no Git commands at all." -msgstr "Uh oh. Keine Git-Kommandos auf deinem System vorhanden." +msgstr "Uh oh. Keine Git-Kommandos auf Ihrem System vorhanden." #: help.c:354 #, c-format @@ -402,8 +402,8 @@ msgid "" "WARNING: You called a Git command named '%s', which does not exist.\n" "Continuing under the assumption that you meant '%s'" msgstr "" -"Warnung: Du hast das nicht existierende Git-Kommando '%s' ausgeführt.\n" -"Setze fort unter der Annahme das du '%s' gemeint hast" +"Warnung: Sie haben das nicht existierende Git-Kommando '%s' ausgeführt.\n" +"Setze fort unter der Annahme, dass Sie '%s' gemeint haben" #: help.c:359 #, c-format @@ -424,10 +424,10 @@ msgid_plural "" "Did you mean one of these?" msgstr[0] "" "\n" -"Hast du das gemeint?" +"Haben Sie das gemeint?" msgstr[1] "" "\n" -"Hast du eines von diesen gemeint?" +"Haben Sie eines von diesen gemeint?" #: merge.c:56 msgid "failed to read the cache" @@ -721,12 +721,12 @@ msgstr " %s" #, c-format msgid "Your branch is ahead of '%s' by %d commit.\n" msgid_plural "Your branch is ahead of '%s' by %d commits.\n" -msgstr[0] "Dein Zweig ist vor '%s' um %d Version.\n" -msgstr[1] "Dein Zweig ist vor '%s' um %d Versionen.\n" +msgstr[0] "Ihr Zweig ist vor '%s' um %d Version.\n" +msgstr[1] "Ihr Zweig ist vor '%s' um %d Versionen.\n" #: remote.c:1637 msgid " (use \"git push\" to publish your local commits)\n" -msgstr " (benutze \"git push\" um deine lokalen Versionen herauszubringen)\n" +msgstr " (benutzen Sie \"git push\" um lokalen Versionen herauszubringen)\n" #: remote.c:1640 #, c-format @@ -734,15 +734,15 @@ msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n" msgid_plural "" "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n" msgstr[0] "" -"Dein Zweig ist zu '%s' um %d Version hinterher, und kann vorgespult werden.\n" +"Ihr Zweig ist zu '%s' um %d Version hinterher, und kann vorgespult werden.\n" msgstr[1] "" -"Dein Zweig ist zu '%s' um %d Versionen hinterher, und kann vorgespult " +"Ihr Zweig ist zu '%s' um %d Versionen hinterher, und kann vorgespult " "werden.\n" #: remote.c:1647 msgid " (use \"git pull\" to update your local branch)\n" msgstr "" -" (benutze \"git pull\" um deinen lokalen Zweig zu aktualisieren)\n" +" (benutzen Sie \"git pull\" um Ihren lokalen Zweig zu aktualisieren)\n" #: remote.c:1650 #, c-format @@ -753,16 +753,16 @@ msgid_plural "" "Your branch and '%s' have diverged,\n" "and have %d and %d different commits each, respectively.\n" msgstr[0] "" -"Dein Zweig und '%s' sind divergiert,\n" +"Ihr Zweig und '%s' sind divergiert,\n" "und haben jeweils %d und %d unterschiedliche Versionen.\n" msgstr[1] "" -"Dein Zweig und '%s' sind divergiert,\n" +"Ihr Zweig und '%s' sind divergiert,\n" "und haben jeweils %d und %d unterschiedliche Versionen.\n" #: remote.c:1659 msgid " (use \"git pull\" to merge the remote branch into yours)\n" msgstr "" -" (benutze \"git pull\" um deinen Zweig mit dem externen zusammenzuführen)\n" +" (benutzen Sie \"git pull\" um Ihren Zweig mit dem externen zusammenzuführen)\n" #: sequencer.c:123 builtin/merge.c:761 builtin/merge.c:874 builtin/merge.c:984 #: builtin/merge.c:994 @@ -781,7 +781,7 @@ msgid "" "after resolving the conflicts, mark the corrected paths\n" "with 'git add <paths>' or 'git rm <paths>'" msgstr "" -"nach Auflösung der Konflikte, markiere die korrigierten Pfade\n" +"nach Auflösung der Konflikte, markieren Sie die korrigierten Pfade\n" "mit 'git add <Pfade>' oder 'git rm <Pfade>'" #: sequencer.c:149 @@ -790,9 +790,9 @@ msgid "" "with 'git add <paths>' or 'git rm <paths>'\n" "and commit the result with 'git commit'" msgstr "" -"nach Auflösung der Konflikte, markiere die korrigierten Pfade\n" -"mit 'git add <Pfade>' oder 'git rm <Pfade>'und trage das Ergebnis ein mit " -"'git commit'" +"nach Auflösung der Konflikte, markieren Sie die korrigierten Pfade\n" +"mit 'git add <Pfade>' oder 'git rm <Pfade>'und tragen Sie das Ergebnis mit\n" +"'git commit' ein" #: sequencer.c:162 sequencer.c:770 sequencer.c:853 #, c-format @@ -807,15 +807,15 @@ msgstr "Fehler bei Nachbereitung von %s" #: sequencer.c:180 msgid "Your local changes would be overwritten by cherry-pick." msgstr "" -"Deine lokalen Änderungen würden von \"cherry-pick\" überschrieben werden." +"Ihre lokalen Änderungen würden von \"cherry-pick\" überschrieben werden." #: sequencer.c:182 msgid "Your local changes would be overwritten by revert." -msgstr "Deine lokalen Änderungen würden von \"revert\" überschrieben werden." +msgstr "Ihre lokalen Änderungen würden von \"revert\" überschrieben werden." #: sequencer.c:185 msgid "Commit your changes or stash them to proceed." -msgstr "Trage deine Änderungen ein oder benutze \"stash\" um fortzufahren." +msgstr "Tragen Sie Ihre Änderungen ein oder benutzen Sie \"stash\" um fortzufahren." #. TRANSLATORS: %s will be "revert" or "cherry-pick" #: sequencer.c:235 @@ -843,11 +843,11 @@ msgstr "Konnte Elternversion %s nicht parsen\n" #: sequencer.c:403 msgid "Your index file is unmerged." -msgstr "Deine Bereitstellungsdatei ist nicht zusammengeführt." +msgstr "Ihre Bereitstellungsdatei ist nicht zusammengeführt." #: sequencer.c:406 msgid "You do not have a valid HEAD" -msgstr "Du hast keine gültige Zweigspitze (HEAD)" +msgstr "Sie haben keine gültige Zweigspitze (HEAD)" #: sequencer.c:421 #, c-format @@ -953,7 +953,7 @@ msgstr "\"cherry-pick\" oder \"revert\" ist bereits im Gang" #: sequencer.c:752 msgid "try \"git cherry-pick (--continue | --quit | --abort)\"" -msgstr "versuche \"git cherry-pick (--continue | --quit | --abort)\"" +msgstr "versuchen Sie \"git cherry-pick (--continue | --quit | --abort)\"" #: sequencer.c:756 #, c-format @@ -1053,28 +1053,28 @@ msgstr "Nicht zusammengeführte Pfade:" #, c-format msgid " (use \"git reset %s <file>...\" to unstage)" msgstr "" -" (benutze \"git reset %s <Datei>...\" zum Herausnehmen aus der " +" (benutzen Sie \"git reset %s <Datei>...\" zum Herausnehmen aus der " "Bereitstellung)" #: wt-status.c:169 wt-status.c:196 msgid " (use \"git rm --cached <file>...\" to unstage)" msgstr "" -" (benutze \"git rm --cached <Datei>...\" zum Herausnehmen aus der " +" (benutzen Sie \"git rm --cached <Datei>...\" zum Herausnehmen aus der " "Bereitstellung)" #: wt-status.c:173 msgid " (use \"git add <file>...\" to mark resolution)" -msgstr " (benutze \"git add/rm <Datei>...\" um die Auflösung zu markieren)" +msgstr " (benutzen Sie \"git add/rm <Datei>...\" um die Auflösung zu markieren)" #: wt-status.c:175 wt-status.c:179 msgid " (use \"git add/rm <file>...\" as appropriate to mark resolution)" msgstr "" -" (benutze \"git add/rm <Datei>...\" um die Auflösung entsprechend zu " +" (benutzen Sie \"git add/rm <Datei>...\" um die Auflösung entsprechend zu " "markieren)" #: wt-status.c:177 msgid " (use \"git rm <file>...\" to mark resolution)" -msgstr " (benutze \"git add/rm <Datei>...\" um die Auflösung zu markieren)" +msgstr " (benutzen Sie \"git add/rm <Datei>...\" um die Auflösung zu markieren)" #: wt-status.c:188 msgid "Changes to be committed:" @@ -1086,29 +1086,29 @@ msgstr "Änderungen, die nicht zum Eintragen bereitgestellt sind:" #: wt-status.c:210 msgid " (use \"git add <file>...\" to update what will be committed)" -msgstr " (benutze \"git add <Datei>...\" zum Bereitstellen)" +msgstr " (benutzen Sie \"git add <Datei>...\" zum Bereitstellen)" #: wt-status.c:212 msgid " (use \"git add/rm <file>...\" to update what will be committed)" -msgstr " (benutze \"git add/rm <Datei>...\" zum Bereitstellen)" +msgstr " (benutzen Sie \"git add/rm <Datei>...\" zum Bereitstellen)" #: wt-status.c:213 msgid "" " (use \"git checkout -- <file>...\" to discard changes in working directory)" msgstr "" -" (benutze \"git checkout -- <Datei>...\" um die Änderungen im " +" (benutzen Sie \"git checkout -- <Datei>...\" um die Änderungen im " "Arbeitsverzeichnis zu verwerfen)" #: wt-status.c:215 msgid " (commit or discard the untracked or modified content in submodules)" msgstr "" -" (trage ein oder verwerfe den unbeobachteten oder geänderten Inhalt in den " +" (tragen Sie ein oder verwerfen Sie den unbeobachteten oder geänderten Inhalt in den " "Unterprojekten)" #: wt-status.c:227 #, c-format msgid " (use \"git %s <file>...\" to include in what will be committed)" -msgstr " (benutze \"git %s <Datei>...\" zum Einfügen in die Eintragung)" +msgstr " (benutzen Sie \"git %s <Datei>...\" zum Einfügen in die Eintragung)" #: wt-status.c:244 msgid "bug" @@ -1201,20 +1201,20 @@ msgstr "Fehler: unbehandelter Differenz-Status %c" #: wt-status.c:785 msgid "You have unmerged paths." -msgstr "Du hast nicht zusammengeführte Pfade." +msgstr "Sie haben nicht zusammengeführte Pfade." #: wt-status.c:788 wt-status.c:912 msgid " (fix conflicts and run \"git commit\")" -msgstr " (behebe die Konflikte und führe \"git commit\" aus)" +msgstr " (beheben Sie die Konflikte und führen Sie \"git commit\" aus)" #: wt-status.c:791 msgid "All conflicts fixed but you are still merging." msgstr "" -"Alle Konflikte sind behoben, aber du bist immer noch beim Zusammenführen." +"Alle Konflikte sind behoben, aber Sie sind immer noch beim Zusammenführen." #: wt-status.c:794 msgid " (use \"git commit\" to conclude merge)" -msgstr " (benutze \"git commit\" um die Zusammenführung abzuschließen)" +msgstr " (benutzen Sie \"git commit\" um die Zusammenführung abzuschließen)" #: wt-status.c:804 msgid "You are in the middle of an am session." @@ -1226,80 +1226,80 @@ msgstr "Der aktuelle Patch ist leer." #: wt-status.c:811 msgid " (fix conflicts and then run \"git am --resolved\")" -msgstr " (behebe die Konflikte und führe dann \"git am --resolved\" aus)" +msgstr " (beheben Sie die Konflikte und führen Sie dann \"git am --resolved\" aus)" #: wt-status.c:813 msgid " (use \"git am --skip\" to skip this patch)" -msgstr " (benutze \"git am --skip\" um diesen Patch auszulassen)" +msgstr " (benutzen Sie \"git am --skip\" um diesen Patch auszulassen)" #: wt-status.c:815 msgid " (use \"git am --abort\" to restore the original branch)" msgstr "" -" (benutze \"git am --abort\" um den ursprünglichen Zweig wiederherzustellen)" +" (benutzen Sie \"git am --abort\" um den ursprünglichen Zweig wiederherzustellen)" #: wt-status.c:873 wt-status.c:883 msgid "You are currently rebasing." -msgstr "Du bist gerade beim Neuaufbau." +msgstr "Sie sind gerade beim Neuaufbau." #: wt-status.c:876 msgid " (fix conflicts and then run \"git rebase --continue\")" -msgstr " (behebe die Konflikte und führe dann \"git rebase --continue\" aus)" +msgstr " (beheben Sie die Konflikte und führen Sie dann \"git rebase --continue\" aus)" #: wt-status.c:878 msgid " (use \"git rebase --skip\" to skip this patch)" -msgstr " (benutze \"git rebase --skip\" um diesen Patch auszulassen)" +msgstr " (benutzen Sie \"git rebase --skip\" um diesen Patch auszulassen)" #: wt-status.c:880 msgid " (use \"git rebase --abort\" to check out the original branch)" msgstr "" -" (benutze \"git rebase --abort\" um den ursprünglichen Zweig auszuchecken)" +" (benutzen Sie \"git rebase --abort\" um den ursprünglichen Zweig auszuchecken)" #: wt-status.c:886 msgid " (all conflicts fixed: run \"git rebase --continue\")" -msgstr " (alle Konflikte behoben: führe \"git rebase --continue\" aus)" +msgstr " (alle Konflikte behoben: führen Sie \"git rebase --continue\" aus)" #: wt-status.c:888 msgid "You are currently splitting a commit during a rebase." -msgstr "Du teilst gerade eine Version während eines Neuaufbaus auf." +msgstr "Sie teilen gerade eine Version während eines Neuaufbaus auf." #: wt-status.c:891 msgid " (Once your working directory is clean, run \"git rebase --continue\")" msgstr "" -" (Sobald dein Arbeitsverzeichnis sauber ist, führe \"git rebase --continue" +" (Sobald Ihr Arbeitsverzeichnis sauber ist, führen Sie \"git rebase --continue" "\" aus)" #: wt-status.c:893 msgid "You are currently editing a commit during a rebase." -msgstr "Du editierst gerade eine Version während eines Neuaufbaus." +msgstr "Sie editieren gerade eine Version während eines Neuaufbaus." #: wt-status.c:896 msgid " (use \"git commit --amend\" to amend the current commit)" msgstr "" -" (benutze \"git commit --amend\" um die aktuelle Version nachzubessern)" +" (benutzen Sie \"git commit --amend\" um die aktuelle Version nachzubessern)" #: wt-status.c:898 msgid "" " (use \"git rebase --continue\" once you are satisfied with your changes)" msgstr "" -" (benutze \"git rebase --continue\" sobald deine Änderungen abgeschlossen " +" (benutzen Sie \"git rebase --continue\" sobald Ihre Änderungen abgeschlossen " "sind)" #: wt-status.c:908 msgid "You are currently cherry-picking." -msgstr "Du führst gerade \"cherry-pick\" aus." +msgstr "Sie führen gerade \"cherry-pick\" aus." #: wt-status.c:915 msgid " (all conflicts fixed: run \"git commit\")" -msgstr " (alle Konflikte behoben: führe \"git commit\" aus)" +msgstr " (alle Konflikte behoben: führen Sie \"git commit\" aus)" #: wt-status.c:924 msgid "You are currently bisecting." -msgstr "Du bist gerade beim Halbieren." +msgstr "Sie sind gerade beim Halbieren." #: wt-status.c:927 msgid " (use \"git bisect reset\" to get back to the original branch)" msgstr "" -" (benutze \"git bisect reset\" um zum ursprünglichen Zweig zurückzukehren)" +" (benutzen Sie \"git bisect reset\" um zum ursprünglichen Zweig zurückzukehren)" #: wt-status.c:978 msgid "On branch " @@ -1328,7 +1328,7 @@ msgstr "Unbeobachtete Dateien nicht aufgelistet%s" #: wt-status.c:1017 msgid " (use -u option to show untracked files)" -msgstr " (benutze die Option -u um unbeobachteten Dateien anzuzeigen)" +msgstr " (benutzen Sie die Option -u um unbeobachteten Dateien anzuzeigen)" #: wt-status.c:1023 msgid "No changes" @@ -1338,7 +1338,7 @@ msgstr "Keine Änderungen" #, c-format msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n" msgstr "" -"keine Änderungen zum Eintragen hinzugefügt (benutze \"git add\" und/oder " +"keine Änderungen zum Eintragen hinzugefügt (benutzen Sie \"git add\" und/oder " "\"git commit -a\")\n" #: wt-status.c:1031 @@ -1353,7 +1353,7 @@ msgid "" "track)\n" msgstr "" "nichts zum Eintragen hinzugefügt, aber es gibt unbeobachtete Dateien " -"(benutze \"git add\" zum Beobachten)\n" +"(benutzen Sie \"git add\" zum Beobachten)\n" #: wt-status.c:1037 #, c-format @@ -1364,8 +1364,8 @@ msgstr "nichts zum Eintragen hinzugefügt, aber es gibt unbeobachtete Dateien\n" #, c-format msgid "nothing to commit (create/copy files and use \"git add\" to track)\n" msgstr "" -"nichts einzutragen (Erstelle/Kopiere Dateien und benutze \"git add\" zum " -"Beobachten)\n" +"nichts einzutragen (Erstellen/Kopieren Sie Dateien und benutzen Sie \"git add\" " +"zum Beobachten)\n" #: wt-status.c:1043 wt-status.c:1048 #, c-format @@ -1376,7 +1376,7 @@ msgstr "nichts einzutragen\n" #, c-format msgid "nothing to commit (use -u to show untracked files)\n" msgstr "" -"nichts einzutragen (benutze die Option -u, um unbeobachtete Dateien " +"nichts einzutragen (benutzen Sie die Option -u, um unbeobachtete Dateien " "anzuzeigen)\n" #: wt-status.c:1050 @@ -1477,7 +1477,7 @@ msgstr "Konnte '%s' nicht anwenden." #: builtin/add.c:313 msgid "The following paths are ignored by one of your .gitignore files:\n" msgstr "" -"Die folgenden Pfade werden durch eine deiner \".gitignore\" Dateien " +"Die folgenden Pfade werden durch eine Ihrer \".gitignore\" Dateien " "ignoriert:\n" #: builtin/add.c:319 builtin/clean.c:52 builtin/fetch.c:78 builtin/mv.c:63 @@ -1538,7 +1538,7 @@ msgstr "prüft ob - auch fehlende - Dateien im Probelauf ignoriert werden" #: builtin/add.c:353 #, c-format msgid "Use -f if you really want to add them.\n" -msgstr "Verwende -f wenn du diese wirklich hinzufügen möchtest.\n" +msgstr "Verwenden Sie -f wenn Sie diese wirklich hinzufügen möchten.\n" #: builtin/add.c:354 msgid "no files added" @@ -1565,7 +1565,7 @@ msgstr "Nichts spezifiziert, nichts hinzugefügt.\n" #: builtin/add.c:415 #, c-format msgid "Maybe you wanted to say 'git add .'?\n" -msgstr "Wolltest du vielleicht 'git add .' sagen?\n" +msgstr "Wollten Sie vielleicht 'git add .' sagen?\n" #: builtin/add.c:421 builtin/clean.c:95 builtin/commit.c:291 builtin/mv.c:82 #: builtin/rm.c:235 @@ -2269,7 +2269,7 @@ msgid "" "If you are sure you want to delete it, run 'git branch -D %s'." msgstr "" "Der Zweig '%s' ist nicht vollständig zusammengeführt.\n" -"Wenn du sicher bist diesen Zweig zu entfernen, führe 'git branch -D %s' aus." +"Wenn Sie sicher sind diesen Zweig zu entfernen, führen Sie 'git branch -D %s' aus." #: builtin/branch.c:180 msgid "Update of config-file failed" @@ -2287,7 +2287,7 @@ msgstr "Konnte Versionsobjekt für Zweigspitze (HEAD) nicht nachschlagen." #, c-format msgid "Cannot delete the branch '%s' which you are currently on." msgstr "" -"Kann Zweig '%s' nicht entfernen, da du dich gerade auf diesem befindest." +"Kann Zweig '%s' nicht entfernen, da Sie sich gerade auf diesem befinden." #: builtin/branch.c:235 #, c-format @@ -2365,7 +2365,7 @@ msgstr "Konnte einige Referenzen nicht lesen" #: builtin/branch.c:638 msgid "cannot rename the current branch while not on any." msgstr "" -"Kann aktuellen Zweig nicht umbenennen, solange du dich auf keinem befindest." +"Kann aktuellen Zweig nicht umbenennen, solange Sie sich auf keinem befinden." #: builtin/branch.c:648 #, c-format @@ -2527,8 +2527,8 @@ msgid "" "The --set-upstream flag is deprecated and will be removed. Consider using --" "track or --set-upstream-to\n" msgstr "" -"Die --set-upstream Option ist veraltet und wird entfernt. Benutze --track " -"oder --set-upstream-to\n" +"Die --set-upstream Option ist veraltet und wird entfernt. Benutzen Sie " +"--track oder --set-upstream-to\n" #: builtin/branch.c:934 #, c-format @@ -2538,8 +2538,8 @@ msgid "" "\n" msgstr "" "\n" -"Wenn du wolltest, dass '%s' den Zweig '%s' als externen Übernahmezweig hat, " -"führe aus:\n" +"Wenn Sie wollten, dass '%s' den Zweig '%s' als externen Übernahmezweig hat, " +"führen Sie aus:\n" #: builtin/branch.c:935 #, c-format @@ -2743,7 +2743,7 @@ msgstr "Pfad '%s' ist nicht zusammengeführt." #: builtin/checkout.c:448 msgid "you need to resolve your current index first" -msgstr "Du musst zuerst deine aktuelle Bereitstellung auflösen." +msgstr "Sie müssen zuerst Ihre aktuelle Bereitstellung auflösen." #: builtin/checkout.c:569 #, c-format @@ -2798,13 +2798,13 @@ msgid_plural "" "\n" "%s\n" msgstr[0] "" -"Warnung: Du bist um %d Version hinterher, nicht verbunden zu\n" -"einem deiner Zweige:\n" +"Warnung: Sie sind um %d Version hinterher, nicht verbunden zu\n" +"einem Ihrer Zweige:\n" "\n" "%s\n" msgstr[1] "" -"Warnung: Du bist um %d Versionen hinterher, nicht verbunden zu\n" -"einem deiner Zweige:\n" +"Warnung: Sie sind um %d Versionen hinterher, nicht verbunden zu\n" +"einem Ihrer Zweige:\n" "\n" "%s\n" @@ -2817,7 +2817,7 @@ msgid "" " git branch new_branch_name %s\n" "\n" msgstr "" -"Wenn du diese durch einen neuen Zweig behalten möchtest, dann könnte jetzt\n" +"Wenn Sie diese durch einen neuen Zweig behalten möchten, dann könnte jetzt\n" "ein guter Zeitpunkt sein dies zu tun mit:\n" "\n" " git branch neuer_zweig_name %s\n" @@ -2833,7 +2833,7 @@ msgstr "Vorherige Position der Zweigspitze (HEAD) war" #: builtin/checkout.c:761 builtin/checkout.c:950 msgid "You are on a branch yet to be born" -msgstr "du bist auf einem Zweig, der noch geboren wird" +msgstr "Sie sind auf einem Zweig, der noch geboren wird" #. case (1) #: builtin/checkout.c:886 @@ -2946,7 +2946,7 @@ msgstr "--track benötigt einen Zweignamen" #: builtin/checkout.c:1081 msgid "Missing branch name; try -b" -msgstr "Vermisse Zweignamen; versuche -b" +msgstr "Vermisse Zweignamen; versuchen Sie -b" #: builtin/checkout.c:1116 msgid "invalid path specification" @@ -2959,7 +2959,7 @@ msgid "" "Did you intend to checkout '%s' which can not be resolved as commit?" msgstr "" "Kann nicht gleichzeitig Pfade aktualisieren und zu Zweig '%s' wechseln.\n" -"Hast du beabsichtigt '%s' auszuchecken, welcher nicht als Version aufgelöst " +"Haben Sie beabsichtigt '%s' auszuchecken, welcher nicht als Version aufgelöst " "werden kann?" #: builtin/checkout.c:1128 @@ -2973,7 +2973,7 @@ msgid "" "checking out of the index." msgstr "" "git checkout: --ours/--theirs, --force und --merge sind inkompatibel wenn\n" -"du aus der Bereitstellung auscheckst." +"Sie aus der Bereitstellung auschecken." #: builtin/clean.c:19 msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..." @@ -3203,7 +3203,7 @@ msgstr "Zu viele Argumente." #: builtin/clone.c:694 msgid "You must specify a repository to clone." -msgstr "Du musst ein Projektarchiv zum Klonen angeben." +msgstr "Sie müssen ein Projektarchiv zum Klonen angeben." #: builtin/clone.c:705 #, c-format @@ -3217,7 +3217,7 @@ msgstr "Projektarchiv '%s' existiert nicht." #: builtin/clone.c:724 msgid "--depth is ignored in local clones; use file:// instead." -msgstr "--depth wird in lokalen Klonen ignoriert; benutze stattdessen file://." +msgstr "--depth wird in lokalen Klonen ignoriert; benutzen Sie stattdessen file://." #: builtin/clone.c:734 #, c-format @@ -3261,7 +3261,7 @@ msgstr "externer Zweig %s nicht im anderen Projektarchiv %s gefunden" #: builtin/clone.c:879 msgid "You appear to have cloned an empty repository." -msgstr "Du scheinst ein leeres Projektarchiv geklont zu haben." +msgstr "Sie scheinen ein leeres Projektarchiv geklont zu haben." #: builtin/column.c:9 msgid "git column [options]" @@ -3316,15 +3316,15 @@ msgid "" "\n" " git commit --amend --reset-author\n" msgstr "" -"Dein Name und E-Mail Adresse wurden automatisch auf Basis\n" -"deines Benutzer- und Rechnernamens konfiguriert. Bitte prüfe, dass diese\n" -"zutreffend sind. Du kannst diese Meldung unterdrücken, indem du diese\n" -"explizit setzt:\n" +"Ihr Name und E-Mail Adresse wurden automatisch auf Basis\n" +"Ihres Benutzer- und Rechnernamens konfiguriert. Bitte prüfen Sie, dass\n" +"diese zutreffend sind. Sie können diese Meldung unterdrücken, indem Sie\n" +"diese explizit setzen:\n" "\n" -" git config --global user.name \"Dein Name\"\n" -" git config --global user.email deine@emailadresse.de\n" +" git config --global user.name \"Ihr Name\"\n" +" git config --global user.email ihre@emailadresse.de\n" "\n" -"Nachdem du das getan hast, kannst du deine Identität für diese Version " +"Nachdem Sie das getan hast, können Sie Ihre Identität für diese Version " "ändern mit:\n" "\n" " git commit --amend --reset-author\n" @@ -3335,8 +3335,8 @@ msgid "" "it empty. You can repeat your command with --allow-empty, or you can\n" "remove the commit entirely with \"git reset HEAD^\".\n" msgstr "" -"Du fragtest die jüngste Version nachzubessern, aber das würde diese leer\n" -"machen. Du kannst Dein Kommando mit --allow-empty wiederholen, oder die\n" +"Sie fragten die jüngste Version nachzubessern, aber das würde diese leer\n" +"machen. Sie können Ihr Kommando mit --allow-empty wiederholen, oder die\n" "Version mit \"git reset HEAD^\" vollständig entfernen.\n" #: builtin/commit.c:61 @@ -3350,11 +3350,11 @@ msgid "" msgstr "" "Der letzte \"cherry-pick\" ist jetzt leer, möglicherweise durch eine " "Konfliktauflösung.\n" -"Wenn du dies trotzdem eintragen willst, benutze:\n" +"Wenn Sie dies trotzdem eintragen wollen, benutzen Sie:\n" "\n" " git commit --allow-empty\n" "\n" -"Andernfalls benutze bitte 'git reset'\n" +"Andernfalls benutzen Sie bitte 'git reset'\n" #: builtin/commit.c:258 msgid "failed to unpack HEAD tree object" @@ -3456,10 +3456,10 @@ msgid "" "and try again.\n" msgstr "" "\n" -"Es sieht so aus, als trägst du eine Zusammenführung ein.\n" -"Falls das nicht korrekt ist, lösche bitte die Datei\n" +"Es sieht so aus, als tragen Sie eine Zusammenführung ein.\n" +"Falls das nicht korrekt ist, löschen Sie bitte die Datei\n" "\t%s\n" -"und versuche es erneut.\n" +"und versuchen Sie es erneut.\n" #: builtin/commit.c:723 #, c-format @@ -3471,17 +3471,17 @@ msgid "" "and try again.\n" msgstr "" "\n" -"Es sieht so aus, als trägst du ein \"cherry-pick\" ein.\n" -"Falls das nicht korrekt ist, lösche bitte die Datei\n" +"Es sieht so aus, als tragen Sie ein \"cherry-pick\" ein.\n" +"Falls das nicht korrekt ist, löschen Sie bitte die Datei\n" "\t%s\n" -"und versuche es erneut.\n" +"und versuchen Sie es erneut.\n" #: builtin/commit.c:735 msgid "" "Please enter the commit message for your changes. Lines starting\n" "with '#' will be ignored, and an empty message aborts the commit.\n" msgstr "" -"Bitte gebe eine Versionsbeschreibung für deine Änderungen ein. Zeilen,\n" +"Bitte geben Sie eine Versionsbeschreibung für Ihre Änderungen ein. Zeilen,\n" "die mit '#' beginnen, werden ignoriert, und eine leere Versionsbeschreibung\n" "bricht die Eintragung ab.\n" @@ -3491,8 +3491,8 @@ msgid "" "with '#' will be kept; you may remove them yourself if you want to.\n" "An empty message aborts the commit.\n" msgstr "" -"Bitte gebe eine Versionsbeschreibung für deine Änderungen ein. Zeilen, die\n" -"mit '#' beginnen, werden beibehalten; wenn du möchtest, kannst du diese " +"Bitte geben Sie eine Versionsbeschreibung für Ihre Änderungen ein. Zeilen, die\n" +"mit '#' beginnen, werden beibehalten; wenn Sie möchten, können Sie diese " "entfernen.\n" "Eine leere Versionsbeschreibung bricht die Eintragung ab.\n" @@ -3535,7 +3535,7 @@ msgstr "Verwendung von --reset-author und --author macht keinen Sinn." #: builtin/commit.c:995 msgid "You have nothing to amend." -msgstr "Du hast nichts zum nachbessern." +msgstr "Sie haben nichts zum nachbessern." #: builtin/commit.c:998 msgid "You are in the middle of a merge -- cannot amend." @@ -3726,7 +3726,7 @@ msgstr "" #: builtin/commit.c:1380 msgid "the commit is authored by me now (used with -C/-c/--amend)" -msgstr "Setze mich als Autor der Version (benutzt mit -C/-c/--amend)" +msgstr "Setzt Sie als Autor der Version (benutzt mit -C/-c/--amend)" #: builtin/commit.c:1381 builtin/log.c:1073 builtin/revert.c:109 msgid "add Signed-off-by:" @@ -3836,7 +3836,7 @@ msgstr "Konnte Versionsbeschreibung nicht lesen: %s" #: builtin/commit.c:1534 #, c-format msgid "Aborting commit; you did not edit the message.\n" -msgstr "Eintragung abgebrochen; du hast die Beschreibung nicht editiert.\n" +msgstr "Eintragung abgebrochen; Sie haben die Beschreibung nicht editiert.\n" #: builtin/commit.c:1539 #, c-format @@ -3862,8 +3862,8 @@ msgid "" "not exceeded, and then \"git reset HEAD\" to recover." msgstr "" "Das Projektarchiv wurde aktualisiert, aber die \"new_index\"-Datei\n" -"konnte nicht geschrieben werden. Prüfe, dass deine Festplatte nicht\n" -"voll und Dein Kontingent nicht aufgebraucht ist und führe\n" +"konnte nicht geschrieben werden. Prüfen Sie, dass Ihre Festplatte nicht\n" +"voll und Ihr Kontingent nicht aufgebraucht ist und führen Sie\n" "anschließend \"git reset HEAD\" zu Wiederherstellung aus." #: builtin/config.c:7 @@ -4041,7 +4041,7 @@ msgid "" "However, there were unannotated tags: try --tags." msgstr "" "Keine annotierten Markierungen können '%s' beschreiben.\n" -"Jedoch gab es nicht annotierte Markierungen: versuche --tags." +"Jedoch gab es nicht annotierte Markierungen: versuchen Sie --tags." #: builtin/describe.c:357 #, c-format @@ -4050,7 +4050,7 @@ msgid "" "Try --always, or create some tags." msgstr "" "Keine Markierungen können '%s' beschreiben.\n" -"Versuche --always oder erstelle einige Markierungen." +"Versuchen Sie --always oder erstellen Sie einige Markierungen." #: builtin/describe.c:378 #, c-format @@ -4354,7 +4354,7 @@ msgid "" "some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting branches" msgstr "" -"Einige lokale Referenzen konnten nicht aktualisiert werden; versuche\n" +"Einige lokale Referenzen konnten nicht aktualisiert werden; versuchen Sie\n" "'git remote prune %s' um jeden älteren, widersprüchlichen Zweig zu löschen." #: builtin/fetch.c:549 @@ -4412,13 +4412,13 @@ msgid "" "No remote repository specified. Please, specify either a URL or a\n" "remote name from which new revisions should be fetched." msgstr "" -"Kein externes Projektarchiv angegeben. Bitte gebe entweder eine URL\n" +"Kein externes Projektarchiv angegeben. Bitte geben Sie entweder eine URL\n" "oder den Namen des externen Archivs an, von welchem neue\n" "Versionen angefordert werden sollen." #: builtin/fetch.c:932 msgid "You need to specify a tag name." -msgstr "Du musst den Namen der Markierung angeben." +msgstr "Sie müssen den Namen der Markierung angeben." #: builtin/fetch.c:984 msgid "fetch --all does not take a repository argument" @@ -4587,15 +4587,15 @@ msgid "" "run \"git gc\" manually. See \"git help gc\" for more information.\n" msgstr "" "Die Datenbank des Projektarchivs wird für eine optimale Performance\n" -"komprimiert. Du kannst auch \"git gc\" manuell ausführen.\n" +"komprimiert. Sie können auch \"git gc\" manuell ausführen.\n" "Siehe \"git help gc\" für weitere Informationen.\n" #: builtin/gc.c:249 msgid "" "There are too many unreachable loose objects; run 'git prune' to remove them." msgstr "" -"Es gibt zu viele unerreichbare lose Objekte; führe 'git prune' aus, um diese " -"zu löschen." +"Es gibt zu viele unerreichbare lose Objekte; führen Sie 'git prune' aus, um " +"diese zu löschen." #: builtin/grep.c:22 msgid "git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]" @@ -4918,7 +4918,7 @@ msgid "" "Please consider using 'man.<tool>.cmd' instead." msgstr "" "'%s': Pfad für nicht unterstützten Handbuchbetrachter.\n" -"Du könntest stattdessen 'man.<Werkzeug>.cmd' benutzen." +"Sie könnten stattdessen 'man.<Werkzeug>.cmd' benutzen." #: builtin/help.c:229 #, c-format @@ -4927,7 +4927,7 @@ msgid "" "Please consider using 'man.<tool>.path' instead." msgstr "" "'%s': Kommando für unterstützten Handbuchbetrachter.\n" -"Du könntest stattdessen 'man.<Werkzeug>.path' benutzen." +"Sie könnten stattdessen 'man.<Werkzeug>.path' benutzen." #: builtin/help.c:299 msgid "The most commonly used git commands are:" @@ -5636,8 +5636,8 @@ msgstr "git cherry [-v] [<Übernahmezweig> [<Arbeitszweig> [<Limit>]]]" msgid "" "Could not find a tracked remote branch, please specify <upstream> manually.\n" msgstr "" -"Konnte gefolgten, externen Zweig nicht finden, bitte gebe <upstream> manuell " -"an.\n" +"Konnte gefolgten, externen Zweig nicht finden, bitte geben Sie <upstream> " +"manuell an.\n" #: builtin/log.c:1517 builtin/log.c:1519 builtin/log.c:1531 #, c-format @@ -5956,7 +5956,7 @@ msgstr "konnte nicht von '%s' lesen" #, c-format msgid "Not committing merge; use 'git commit' to complete the merge.\n" msgstr "" -"Zusammenführung wurde nicht eingetragen; benutze 'git commit' um die " +"Zusammenführung wurde nicht eingetragen; benutzen Sie 'git commit' um die " "Zusammenführung abzuschließen.\n" #: builtin/merge.c:788 @@ -5967,7 +5967,7 @@ msgid "" "Lines starting with '#' will be ignored, and an empty message aborts\n" "the commit.\n" msgstr "" -"Bitte gebe eine Versionsbeschreibung ein um zu erklären, warum diese " +"Bitte geben Sie eine Versionsbeschreibung ein um zu erklären, warum diese " "Zusammenführung erforderlich ist,\n" "insbesondere wenn es einen aktualisierten, externen Zweig mit einem Thema-" "Zweig zusammenführt.\n" @@ -5988,8 +5988,8 @@ msgstr "Wunderbar.\n" #, c-format msgid "Automatic merge failed; fix conflicts and then commit the result.\n" msgstr "" -"Automatische Zusammenführung fehlgeschlagen; behebe die Konflikte und trage " -"dann das Ergebnis ein.\n" +"Automatische Zusammenführung fehlgeschlagen; beheben Sie die Konflikte und tragen " +"Sie dann das Ergebnis ein.\n" #: builtin/merge.c:905 #, c-format @@ -5998,7 +5998,7 @@ msgstr "'%s' ist keine Version" #: builtin/merge.c:946 msgid "No current branch." -msgstr "Du befindest dich auf keinem Zweig." +msgstr "Sie befinden sich auf keinem Zweig." #: builtin/merge.c:948 msgid "No remote for the current branch." @@ -6029,34 +6029,34 @@ msgid "" "You have not concluded your merge (MERGE_HEAD exists).\n" "Please, commit your changes before you can merge." msgstr "" -"Du hast deine Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert).\n" -"Bitte trage deine Änderungen ein, bevor du zusammenführen kannst." +"Sie haben Ihre Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert).\n" +"Bitte tragen Sie Ihre Änderungen ein, bevor Sie zusammenführen können." #: builtin/merge.c:1129 git-pull.sh:34 msgid "You have not concluded your merge (MERGE_HEAD exists)." msgstr "" -"Du hast deine Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert)." +"Sie haben Ihre Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert)." #: builtin/merge.c:1133 msgid "" "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you can merge." msgstr "" -"Du hast \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert).\n" -"Bitte trage deine Änderungen ein, bevor du zusammenführen kannst." +"Sie haben \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert).\n" +"Bitte tragen Sie Ihre Änderungen ein, bevor Sie zusammenführen können." #: builtin/merge.c:1136 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)." msgstr "" -"Du hast \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert)." +"Sie haben \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert)." #: builtin/merge.c:1145 msgid "You cannot combine --squash with --no-ff." -msgstr "Du kannst --squash nicht mit --no-ff kombinieren." +msgstr "Sie können --squash nicht mit --no-ff kombinieren." #: builtin/merge.c:1150 msgid "You cannot combine --no-ff with --ff-only." -msgstr "Du kannst --no-ff nicht mit --ff--only kombinieren." +msgstr "Sie können --no-ff nicht mit --ff--only kombinieren." #: builtin/merge.c:1157 msgid "No commit specified and merge.defaultToUpstream not set." @@ -6116,7 +6116,7 @@ msgstr "Zusammenführung mit Strategie %s fehlgeschlagen.\n" #: builtin/merge.c:1491 #, c-format msgid "Using the %s to prepare resolving by hand.\n" -msgstr "Benutze \"%s\" um die Auflösung per Hand vorzubereiten.\n" +msgstr "Benutzen Sie \"%s\" um die Auflösung per Hand vorzubereiten.\n" #: builtin/merge.c:1503 #, c-format @@ -6461,7 +6461,7 @@ msgstr "konnte Datei '%s' nicht erstellen" #: builtin/notes.c:192 msgid "Please supply the note contents using either -m or -F option" -msgstr "Bitte liefere den Notiz-Inhalt unter Verwendung der Option -m oder -F." +msgstr "Bitte liefern Sie den Notiz-Inhalt unter Verwendung der Option -m oder -F." #: builtin/notes.c:213 builtin/notes.c:976 #, c-format @@ -6575,7 +6575,7 @@ msgid "" "existing notes" msgstr "" "Konnte Notizen nicht hinzufügen. Existierende Notizen für Objekt %s " -"gefunden. Verwende '-f' um die existierenden Notizen zu überschreiben." +"gefunden. Verwenden Sie '-f' um die existierenden Notizen zu überschreiben." #: builtin/notes.c:588 builtin/notes.c:665 #, c-format @@ -6603,7 +6603,7 @@ msgid "" "existing notes" msgstr "" "Kann Notizen nicht kopieren. Existierende Notizen für Objekt %s gefunden. " -"Verwende '-f' um die existierenden Notizen zu überschreiben." +"Verwenden Sie '-f' um die existierenden Notizen zu überschreiben." #: builtin/notes.c:671 #, c-format @@ -6617,7 +6617,7 @@ msgid "" "Please use 'git notes add -f -m/-F/-c/-C' instead.\n" msgstr "" "Die Optionen -m/-F/-c/-C sind für das Unterkommando 'edit' veraltet.\n" -"Bitte benutze stattdessen 'git notes add -f -m/-F/-c/-C'.\n" +"Bitte benutzen Sie stattdessen 'git notes add -f -m/-F/-c/-C'.\n" #: builtin/notes.c:867 msgid "General options" @@ -6908,14 +6908,14 @@ msgid "" " git push %s %s\n" "%s" msgstr "" -"Der Name des externen Übernahmezweiges stimmt nicht mit dem Namen deines\n" +"Der Name des externen Übernahmezweiges stimmt nicht mit dem Namen Ihres\n" "aktuellen Zweiges überein. Um auf den Übernahmezweig in dem externen\n" -"Projektarchiv zu versenden, benutze:\n" +"Projektarchiv zu versenden, benutzen Sie:\n" "\n" " git push %s HEAD:%s\n" "\n" "Um auf den Zweig mit dem selben Namen in dem externen Projekarchiv\n" -"zu versenden, benutze:\n" +"zu versenden, benutzen Sie:\n" "\n" " git push %s %s\n" "%s" @@ -6929,9 +6929,9 @@ msgid "" "\n" " git push %s HEAD:<name-of-remote-branch>\n" msgstr "" -"Du befindest dich sich im Moment auf keinem Zweig.\n" +"Sie befinden sich im Moment auf keinem Zweig.\n" "Um die Historie, führend zum aktuellen (freistehende Zweigspitze (HEAD))\n" -"Status zu versenden, benutze\n" +"Status zu versenden, benutzen Sie\n" "\n" " git push %s HEAD:<Name-des-externen-Zweiges>\n" @@ -6945,7 +6945,7 @@ msgid "" msgstr "" "Der aktuelle Zweig %s hat keinen Zweig im externen Projektarchiv.\n" "Um den aktuellen Zweig zu versenden und das Fernarchiv als externes\n" -"Projektarchiv zu verwenden, benutze\n" +"Projektarchiv zu verwenden, benutzen Sie\n" "\n" " git push --set-upstream %s %s\n" @@ -6961,7 +6961,7 @@ msgid "" "your current branch '%s', without telling me what to push\n" "to update which remote branch." msgstr "" -"Du versendest nach '%s', welches kein externes Projektarchiv deines\n" +"Sie versenden nach '%s', welches kein externes Projektarchiv Ihres\n" "aktuellen Zweiges '%s' ist, ohne mir mitzuteilen, was ich versenden\n" "soll, um welchen externen Zweig zu aktualisieren." @@ -6985,26 +6985,25 @@ msgstr "" "'push.default' ist nicht gesetzt; der implizit gesetzte Wert\n" "wird in Git 2.0 von 'matching' nach 'simple' geändert. Um diese Meldung zu\n" "unterdrücken und das aktuelle Verhalten nach Änderung des Standardwertes\n" -"beizubehalten, benutze:\n" +"beizubehalten, benutzen Sie:\n" " git config --global push.default matching\n" "\n" "Um diese Meldung zu unterdrücken und das neue Verhalten jetzt zu " -"übernehmen,\n" -"benutze:\n" +"übernehmen, benutzen Sie:\n" "\n" " git config --global push.default simple\n" "\n" -"Führe 'git help config' aus und suche nach 'push.default' für weitere " -"Informationen.\n" +"Führen Sie 'git help config' aus und suchen Sie nach 'push.default' für " +"weitere Informationen.\n" "(Der Modus 'simple' wurde in Git 1.7.11 eingeführt. Benutze den ähnlichen " -"Modus 'current' anstatt 'simple', falls du gelegentlich ältere Versionen von " -"Git benutzt.)" +"Modus 'current' anstatt 'simple', falls Sie gelegentlich ältere Versionen von " +"Git benutzen.)" #: builtin/push.c:199 msgid "" "You didn't specify any refspecs to push, and push.default is \"nothing\"." msgstr "" -"Du hast keine Referenzspezifikationen zum Versenden angegeben, und push." +"Sie haben keine Referenzspezifikationen zum Versenden angegeben, und push." "default ist \"nothing\"." #: builtin/push.c:206 @@ -7014,9 +7013,10 @@ msgid "" "before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details." msgstr "" -"Aktualisierungen wurden zurückgewiesen, weil die Spitze deines aktuellen\n" -"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Führe die\n" -"externen Änderungen zusammen (z.B. 'git pull') bevor du erneut versendest.\n" +"Aktualisierungen wurden zurückgewiesen, weil die Spitze Ihres aktuellen\n" +"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Führen Sie\n" +"die externen Änderungen zusammen (z.B. 'git pull') bevor Sie erneut\n" +"versenden.\n" "Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n" "für weitere Details." @@ -7028,8 +7028,8 @@ msgid "" "to 'simple', 'current' or 'upstream' to push only the current branch." msgstr "" "Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n" -"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Wenn du nicht\n" -"beabsichtigt hast, diesen Zweig zu versenden, kannst du auch den zu " +"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie nicht\n" +"beabsichtigt haben, diesen Zweig zu versenden, können Sie auch den zu " "versendenden\n" "Zweig spezifizieren oder die Konfigurationsvariable 'push.default' zu " "'simple', 'current'\n" @@ -7043,9 +7043,9 @@ msgid "" "See the 'Note about fast-forwards' in 'git push --help' for details." msgstr "" "Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n" -"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Checke diesen\n" -"Zweig aus und führe die externen Änderungen zusammen (z.B. 'git pull')\n" -"bevor du erneut versendest.\n" +"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Checken Sie\n" +"diesen Zweig aus und führen Sie die externen Änderungen zusammen\n" +"(z.B. 'git pull') bevor Sie erneut versenden.\n" "Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n" "für weitere Details." @@ -7077,12 +7077,12 @@ msgid "" " git push <name>\n" msgstr "" "Kein Ziel zum Versenden konfiguriert.\n" -"Entweder spezifizierst du die URL von der Kommandozeile oder konfigurierst " +"Entweder spezifizieren Sie die URL von der Kommandozeile oder konfigurieren " "ein externes Projektarchiv unter Benutzung von\n" "\n" " git remote add <Name> <URL>\n" "\n" -"und versendest dann unter Benutzung dieses Namens\n" +"und versenden dann unter Benutzung dieses Namens\n" "\n" " git push <Name>\n" @@ -7331,7 +7331,7 @@ msgid "" "\t use --mirror=fetch or --mirror=push instead" msgstr "" "--mirror ist gefährlich und veraltet; bitte\n" -"\t benutze stattdessen --mirror=fetch oder --mirror=push" +"\t benutzen Sie stattdessen --mirror=fetch oder --mirror=push" #: builtin/remote.c:147 #, c-format @@ -7440,7 +7440,7 @@ msgstr "" "Keine Aktualisierung der nicht standardmäßigen Referenzspezifikation zum " "Abholen\n" "\t%s\n" -"\tBitte aktualisiere, falls notwendig, die Konfiguration manuell." +"\tBitte aktualisieren Sie, falls notwendig, die Konfiguration manuell." #: builtin/remote.c:683 #, c-format @@ -7477,11 +7477,11 @@ msgid_plural "" msgstr[0] "" "Hinweis: Ein Zweig außerhalb der /refs/remotes/ Hierachie wurde nicht " "gelöscht;\n" -"um diesen zu löschen, benutze:" +"um diesen zu löschen, benutzen Sie:" msgstr[1] "" "Hinweis: Einige Zweige außer der /refs/remotes/ Hierarchie wurden nicht " "entfernt;\n" -"um diese zu entfernen, benutze:" +"um diese zu entfernen, benutzen Sie:" #: builtin/remote.c:943 #, c-format @@ -7494,7 +7494,7 @@ msgstr " gefolgt" #: builtin/remote.c:948 msgid " stale (use 'git remote prune' to remove)" -msgstr " veraltet (benutze 'git remote prune' zum Löschen)" +msgstr " veraltet (benutzen Sie 'git remote prune' zum Löschen)" #: builtin/remote.c:950 msgid " ???" @@ -7647,8 +7647,8 @@ msgstr "Kann Hauptzweig des externen Projektarchivs nicht bestimmen" #: builtin/remote.c:1218 msgid "Multiple remote HEAD branches. Please choose one explicitly with:" msgstr "" -"Mehrere Hauptzweige im externen Projektarchiv. Bitte wähle explizit einen " -"aus mit:" +"Mehrere Hauptzweige im externen Projektarchiv. Bitte wählen Sie explizit " +"einen aus mit:" #: builtin/remote.c:1228 #, c-format @@ -7814,7 +7814,7 @@ msgstr "keep" #: builtin/reset.c:77 msgid "You do not have a valid HEAD." -msgstr "Du hast keine gültige Zweigspitze (HEAD)." +msgstr "Sie haben keine gültige Zweigspitze (HEAD)." #: builtin/reset.c:79 msgid "Failed to find tree of HEAD." @@ -7881,7 +7881,8 @@ msgstr "--patch ist inkompatibel mit --{hard,mixed,soft}" #: builtin/reset.c:317 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead." msgstr "" -"--mixed mit Pfaden ist veraltet; benutze stattdessen 'git reset -- <Pfade>'." +"--mixed mit Pfaden ist veraltet; benutzen Sie stattdessen " +"'git reset -- <Pfade>'." #: builtin/reset.c:319 #, c-format @@ -7922,7 +7923,7 @@ msgstr "" " oder: git rev-parse --sq-quote [<Argumente>...]\n" " oder: git rev-parse [Optionen] [<Argumente>...]\n" "\n" -"Führe \"git rev-parse --parseopt -h\" für weitere Informationen bei erster " +"Führen Sie \"git rev-parse --parseopt -h\" für weitere Informationen bei erster " "Verwendung aus." #: builtin/revert.c:22 @@ -8025,8 +8026,8 @@ msgid "" "(use 'rm -rf' if you really want to remove it including all of its history)" msgstr "" "Unterprojekt '%s' (oder ein geschachteltes Unterprojekt hiervon) verwendet\n" -"ein .git-Verzeichnis (benutze 'rm -rf' wenn du dieses wirklich mitsamt\n" -"seiner Historie löschen möchtest)" +"ein .git-Verzeichnis (benutzen Sie 'rm -rf' wenn Sie dieses wirklich mitsamt\n" +"seiner Historie löschen möchten)" #: builtin/rm.c:174 #, c-format @@ -8035,7 +8036,7 @@ msgid "" "(use -f to force removal)" msgstr "" "'%s' hat bereitgestellten Inhalt unterschiedlich zu der Datei und der\n" -"Zweigspitze (HEAD) (benutze -f um die Entfernung zu erzwingen)" +"Zweigspitze (HEAD) (benutzen Sie -f um die Entfernung zu erzwingen)" #: builtin/rm.c:180 #, c-format @@ -8044,7 +8045,7 @@ msgid "" "(use --cached to keep the file, or -f to force removal)" msgstr "" "'%s' hat Änderungen in der Bereitstellung\n" -"(benutze --cached um die Datei zu behalten, oder -f um die Entfernung zu " +"(benutzen Sie --cached um die Datei zu behalten, oder -f um die Entfernung zu " "erzwingen)" #: builtin/rm.c:191 @@ -8054,7 +8055,7 @@ msgid "" "(use --cached to keep the file, or -f to force removal)" msgstr "" "'%s' hat lokale Modifikationen\n" -"(benutze --cached um die Datei zu behalten, oder -f um die Entfernung zu " +"(benutzen Sie --cached um die Datei zu behalten, oder -f um die Entfernung zu " "erzwingen)" #: builtin/rm.c:207 @@ -8334,7 +8335,7 @@ msgid "" msgstr "" "\n" "#\n" -"# Gebe eine Markierungsbeschreibung ein\n" +"# Geben Sie eine Markierungsbeschreibung ein.\n" "# Zeilen, die mit '#' beginnen, werden ignoriert.\n" "#\n" @@ -8349,9 +8350,9 @@ msgid "" msgstr "" "\n" "#\n" -"# Gebe eine Markierungsbeschreibung ein\n" -"# Zeilen, die mit '#' beginnen, werden behalten; du darfst diese\n" -"# selbst entfernen wenn du möchtest.\n" +"# Geben Sie eine Markierungsbeschreibung ein.\n" +"# Zeilen, die mit '#' beginnen, werden behalten; Sie dürfen diese\n" +"# selbst entfernen wenn Sie möchten.\n" "#\n" #: builtin/tag.c:298 @@ -8790,14 +8791,14 @@ msgstr "" #: git-am.sh:50 msgid "You need to set your committer info first" -msgstr "Du musst zuerst die Informationen des Eintragenden setzen." +msgstr "Sie müssen zuerst die Informationen des Eintragenden setzen." #: git-am.sh:95 msgid "" "You seem to have moved HEAD since the last 'am' failure.\n" "Not rewinding to ORIG_HEAD" msgstr "" -"Du scheinst seit dem letzten gescheiterten 'am' die Zweigspitze (HEAD)\n" +"Sie scheinen seit dem letzten gescheiterten 'am' die Zweigspitze (HEAD)\n" "geändert zu haben.\n" "Keine Zurücksetzung zu ORIG_HEAD." @@ -8808,12 +8809,11 @@ msgid "" "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n" "To restore the original branch and stop patching, run \"$cmdline --abort\"." msgstr "" -"Wenn du das Problem gelöst hast, führe \"$cmdline --resolved\" aus.\n" -"Falls du diesen Patch auslassen möchtest, führe stattdessen \"$cmdline --skip" -"\" aus.\n" -"Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der " -"Patches\n" -"abzubrechen, führe \"$cmdline --abort\" aus." +"Wenn Sie das Problem gelöst haben, führen Sie \"$cmdline --resolved\" aus.\n" +"Falls Sie diesen Patch auslassen möchten, führen Sie stattdessen\n" +"\"$cmdline --skip\" aus.\n" +"Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der\n" +"Patches abzubrechen, führen Sie \"$cmdline --abort\" aus." #: git-am.sh:121 msgid "Cannot fall back to three-way merge." @@ -8836,7 +8836,7 @@ msgid "" "Did you hand edit your patch?\n" "It does not apply to blobs recorded in its index." msgstr "" -"Hast du den Patch per Hand editiert?\n" +"Haben Sie den Patch per Hand editiert?\n" "Er kann nicht auf die Blobs in seiner 'index' Zeile angewendet werden." #: git-am.sh:163 @@ -8877,7 +8877,7 @@ msgstr "" #: git-am.sh:482 msgid "Please make up your mind. --skip or --abort?" -msgstr "Bitte werde dir klar. --skip oder --abort?" +msgstr "Bitte werden Sie sich klar. --skip oder --abort?" #: git-am.sh:509 msgid "Resolve operation not in progress, we are not resuming." @@ -8897,11 +8897,11 @@ msgid "" "To restore the original branch and stop patching run \"$cmdline --abort\"." msgstr "" "Patch ist leer. Wurde er falsch aufgeteilt?\n" -"Wenn du diesen Patch auslassen möchtest, führe stattdessen \"$cmdline --skip" -"\" aus.\n" +"Wenn Sie diesen Patch auslassen möchten, führen Sie stattdessen\n" +"\"$cmdline --skip\" aus.\n" "Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der " "Patches\n" -"abzubrechen, führe \"$cmdline --abort\" aus." +"abzubrechen, führen Sie \"$cmdline --abort\" aus." #: git-am.sh:706 msgid "Patch does not have a valid e-mail address." @@ -8935,9 +8935,9 @@ msgid "" "If there is nothing left to stage, chances are that something else\n" "already introduced the same changes; you might want to skip this patch." msgstr "" -"Keine Änderungen - hast du vergessen 'git add' zu benutzen?\n" +"Keine Änderungen - haben Sie vergessen 'git add' zu benutzen?\n" "Wenn keine Änderungen mehr zum Bereitstellen vorhanden sind, könnten\n" -"diese bereits anderweitig eingefügt worden sein; du könntest diesen Patch\n" +"diese bereits anderweitig eingefügt worden sein; Sie könnten diesen Patch\n" "auslassen." #: git-am.sh:829 @@ -8945,8 +8945,8 @@ msgid "" "You still have unmerged paths in your index\n" "did you forget to use 'git add'?" msgstr "" -"Du hast immer noch nicht zusammengeführte Pfade in der Bereitstellung.\n" -"Hast du vergessen 'git add' zu benutzen?" +"Sie haben immer noch nicht zusammengeführte Pfade in der Bereitstellung.\n" +"Haben Sie vergessen 'git add' zu benutzen?" #: git-am.sh:845 msgid "No changes -- Patch already applied." @@ -8972,14 +8972,14 @@ msgstr "wende zu leerer Historie an" #: git-bisect.sh:48 msgid "You need to start by \"git bisect start\"" -msgstr "Du musst mit \"git bisect start\" beginnen." +msgstr "Sie müssen mit \"git bisect start\" beginnen." #. TRANSLATORS: Make sure to include [Y] and [n] in your #. translation. The program will only accept English input #. at this point. #: git-bisect.sh:54 msgid "Do you want me to do it for you [Y/n]? " -msgstr "Willst du, dass ich es für dich mache [Y/n]? " +msgstr "Wollen Sie, dass ich es für Sie mache [Y/n]? " #: git-bisect.sh:95 #, sh-format @@ -9000,7 +9000,7 @@ msgstr "Ungültige Zweigspitze (HEAD) - Zweigspitze (HEAD) wird benötigt" msgid "" "Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'." msgstr "" -"Auschecken von '$start_head' fehlgeschlagen. Versuche 'git bisect reset " +"Auschecken von '$start_head' fehlgeschlagen. Versuchen Sie 'git bisect reset " "<gueltigerzweig>'." #: git-bisect.sh:140 @@ -9023,7 +9023,7 @@ msgstr "Ungültige Referenz-Eingabe: $arg" #: git-bisect.sh:232 msgid "Please call 'bisect_state' with at least one argument." -msgstr "Bitte rufe 'bisect_state' mit mindestens einem Argument auf." +msgstr "Bitte rufen Sie 'bisect_state' mit mindestens einem Argument auf." #: git-bisect.sh:244 #, sh-format @@ -9045,15 +9045,15 @@ msgstr "Warnung: halbiere nur mit einer fehlerhaften Version" #. at this point. #: git-bisect.sh:279 msgid "Are you sure [Y/n]? " -msgstr "Bist du sicher [Y/n]? " +msgstr "Sind Sie sicher [Y/n]? " #: git-bisect.sh:289 msgid "" "You need to give me at least one good and one bad revisions.\n" "(You can use \"git bisect bad\" and \"git bisect good\" for that.)" msgstr "" -"Du musst mindestens eine korrekte und eine fehlerhafte Version angeben.\n" -"(Du kannst dafür \"git bisect bad\" und \"git bisect good\" benutzen.)" +"Sie müssen mindestens eine korrekte und eine fehlerhafte Version angeben.\n" +"(Sie können dafür \"git bisect bad\" und \"git bisect good\" benutzen.)" #: git-bisect.sh:292 msgid "" @@ -9061,10 +9061,10 @@ msgid "" "You then need to give me at least one good and one bad revisions.\n" "(You can use \"git bisect bad\" and \"git bisect good\" for that.)" msgstr "" -"Du musst mit \"git bisect start\" beginnen.\n" -"Danach musst du mindestens eine korrekte und eine fehlerhafte Version " +"Sie müssen mit \"git bisect start\" beginnen.\n" +"Danach müssen Sie mindestens eine korrekte und eine fehlerhafte Version " "angeben.\n" -"(Du kannst dafür \"git bisect bad\" und \"git bisect good\" benutzen.)" +"(Sie können dafür \"git bisect bad\" und \"git bisect good\" benutzen.)" #: git-bisect.sh:347 git-bisect.sh:474 msgid "We are not bisecting." @@ -9082,7 +9082,7 @@ msgid "" "Try 'git bisect reset <commit>'." msgstr "" "Konnte die ursprüngliche Zweigspitze (HEAD) '$branch' nicht auschecken.\n" -"Versuche 'git bisect reset <Version>'." +"Versuchen Sie 'git bisect reset <Version>'." #: git-bisect.sh:390 msgid "No logfile given" @@ -9095,7 +9095,7 @@ msgstr "kann $file nicht für das Abspielen lesen" #: git-bisect.sh:408 msgid "?? what are you talking about?" -msgstr "?? Was redest du da?" +msgstr "?? Was reden Sie da?" #: git-bisect.sh:420 #, sh-format @@ -9134,14 +9134,14 @@ msgid "" "Please, fix them up in the work tree, and then use 'git add/rm <file>'\n" "as appropriate to mark resolution, or use 'git commit -a'." msgstr "" -"\"pull\" ist nicht möglich, weil du nicht zusammengeführte Dateien hast.\n" -"Bitte korrigiere dies im Arbeitsbaum und benutze dann 'git add/rm <Datei>'\n" -"um die Auflösung entsprechend zu markieren, oder benutze 'git commit -a'." +"\"pull\" ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben.\n" +"Bitte korrigieren Sie dies im Arbeitsbaum und benutzen Sie dann 'git add/rm <Datei>'\n" +"um die Auflösung entsprechend zu markieren, oder benutzen Sie 'git commit -a'." #: git-pull.sh:25 msgid "Pull is not possible because you have unmerged files." msgstr "" -"\"pull\" ist nicht möglich, weil du nicht zusammengeführte Dateien hast." +"\"pull\" ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben." #: git-pull.sh:197 msgid "updating an unborn branch with changes added to the index" @@ -9161,7 +9161,7 @@ msgid "" "Warning: commit $orig_head." msgstr "" "Warnung: Die Anforderung aktualisierte die Spitze des aktuellen Zweiges.\n" -"Warnung: Spule deinen Arbeitszweig von Version $orig_head vor." +"Warnung: Spule Ihren Arbeitszweig von Version $orig_head vor." #: git-pull.sh:254 msgid "Cannot merge multiple branches into empty head" @@ -9178,12 +9178,12 @@ msgid "" "To check out the original branch and stop rebasing, run \"git rebase --abort" "\"." msgstr "" -"Wenn du das Problem aufgelöst hast, führe \"git rebase --continue\" aus.\n" -"Falls du diesen Patch auslassen möchtest, führe stattdessen \"git rebase --" +"Wenn Sie das Problem aufgelöst haben, führen Sie \"git rebase --continue\" aus.\n" +"Falls Sie diesen Patch auslassen möchten, führen Sie stattdessen \"git rebase --" "skip\" aus.\n" "Um den ursprünglichen Zweig wiederherzustellen und den Neuaufbau " "abzubrechen,\n" -"führe \"git rebase --abort\" aus." +"führen Sie \"git rebase --abort\" aus." #: git-rebase.sh:160 msgid "The pre-rebase hook refused to rebase." @@ -9215,7 +9215,7 @@ msgid "" "You must edit all merge conflicts and then\n" "mark them as resolved using git add" msgstr "" -"Du musst alle Zusammenführungskonflikte editieren und diese dann\n" +"Sie müssen alle Zusammenführungskonflikte editieren und diese dann\n" "mittels \"git add\" als aufgelöst markieren" #: git-rebase.sh:340 @@ -9237,11 +9237,11 @@ msgid "" msgstr "" "Es sieht so aus, als ob es das Verzeichnis $state_dir_base bereits gibt\n" "und es könnte ein anderer Neuaufbau im Gange sein. Wenn das der Fall ist,\n" -"probiere bitte\n" +"probieren Sie bitte\n" "\t$cmd_live_rebase\n" -"Wenn das nicht der Fall ist, probiere bitte\n" +"Wenn das nicht der Fall ist, probieren Sie bitte\n" "\t$cmd_clear_stale_rebase\n" -"und führe dieses Kommando nochmal aus. Es wird angehalten, falls noch\n" +"und führen Sie dieses Kommando nochmal aus. Es wird angehalten, falls noch\n" "etwas Schützenswertes vorhanden ist." #: git-rebase.sh:404 @@ -9271,7 +9271,7 @@ msgstr "fatal: Zweig $branch_name nicht gefunden" #: git-rebase.sh:483 msgid "Please commit or stash them." -msgstr "Bitte trage die Änderungen ein oder benutze \"stash\"." +msgstr "Bitte tragen Sie die Änderungen ein oder benutzen Sie \"stash\"." #: git-rebase.sh:501 #, sh-format @@ -9293,7 +9293,7 @@ msgstr "Änderungen von $mb zu $onto:" #: git-rebase.sh:524 msgid "First, rewinding head to replay your work on top of it..." msgstr "" -"Zunächst wird die Zweigspitze zurückgespult, um deine Änderungen\n" +"Zunächst wird die Zweigspitze zurückgespult, um Ihre Änderungen\n" "darauf neu anzuwenden..." #: git-rebase.sh:532 @@ -9307,7 +9307,7 @@ msgstr "git stash clear mit Parametern ist nicht implementiert" #: git-stash.sh:74 msgid "You do not have the initial commit yet" -msgstr "Du hast bisher noch keine initiale Version" +msgstr "Sie haben bisher noch keine initiale Version" #: git-stash.sh:89 msgid "Cannot save the current index state" @@ -9346,7 +9346,7 @@ msgid "" " To provide a message, use git stash save -- '$option'" msgstr "" "Fehler: unbekannte Option für 'stash save': $option\n" -" Um eine Beschreibung anzugeben, benutze \"git stash save -- " +" Um eine Beschreibung anzugeben, benutzen Sie \"git stash save -- " "'$option'\"" #: git-stash.sh:223 @@ -9400,7 +9400,7 @@ msgstr "" #: git-stash.sh:424 msgid "Conflicts in index. Try without --index." -msgstr "Konflikte in der Bereitstellung. Versuche es ohne --index." +msgstr "Konflikte in der Bereitstellung. Versuchen Sie es ohne --index." #: git-stash.sh:426 msgid "Could not save index tree" @@ -9430,7 +9430,7 @@ msgstr "Kein Zweigname spezifiziert" #: git-stash.sh:571 msgid "(To restore them type \"git stash apply\")" -msgstr "(Zur Wiederherstellung gebe \"git stash apply\" ein)" +msgstr "(Zur Wiederherstellung geben Sie \"git stash apply\" ein)" #: git-submodule.sh:89 #, sh-format @@ -9471,9 +9471,9 @@ msgid "" "$sm_path\n" "Use -f if you really want to add it." msgstr "" -"Der folgende Pfad wird durch eine deiner \".gitignore\" Dateien ignoriert:\n" +"Der folgende Pfad wird durch eine Ihrer \".gitignore\" Dateien ignoriert:\n" "$sm_path\n" -"Benutze -f wenn du diesen wirklich hinzufügen möchtest." +"Benutzen Sie -f wenn Sie diesen wirklich hinzufügen möchten." #: git-submodule.sh:355 #, sh-format @@ -9497,7 +9497,7 @@ msgstr "Ein Git-Verzeichnis für '$sm_name' wurde lokal gefunden mit den " msgid "" "If you want to reuse this local git directory instead of cloning again from" msgstr "" -"Wenn du dieses lokale Git-Verzeichnis wiederverwenden möchtest, anstatt " +"Wenn Sie dieses lokale Git-Verzeichnis wiederverwenden möchtest, anstatt " "erneut zu klonen" #: git-submodule.sh:369 @@ -9505,7 +9505,7 @@ msgstr "" msgid "" "use the '--force' option. If the local git directory is not the correct repo" msgstr "" -"benutze die Option '--force'. Wenn das lokale Git-Verzeichnis nicht das " +"benutzen Sie die Option '--force'. Wenn das lokale Git-Verzeichnis nicht das " "korrekte Projektarchiv ist" #: git-submodule.sh:370 @@ -9514,8 +9514,8 @@ msgid "" "or you are unsure what this means choose another name with the '--name' " "option." msgstr "" -"oder du dir unsicher bist, was das bedeutet, wähle einen anderen Namen mit " -"der Option '--name'." +"oder Sie sich unsicher sind, was das bedeutet, wählen Sie einen anderen Namen" +"mit der Option '--name'." #: git-submodule.sh:372 #, sh-format @@ -9576,7 +9576,7 @@ msgid "" "Maybe you want to use 'update --init'?" msgstr "" "Unterprojekt-Pfad '$sm_path' ist nicht initialisiert\n" -"Vielleicht möchtest du 'update --init' benutzen?" +"Vielleicht möchten Sie 'update --init' benutzen?" #: git-submodule.sh:627 #, sh-format @@ -387,56 +387,79 @@ void pp_user_info(const struct pretty_print_context *pp, const char *what, struct strbuf *sb, const char *line, const char *encoding) { + struct strbuf name; + struct strbuf mail; + struct ident_split ident; + int linelen; + char *line_end, *date; + const char *mailbuf, *namebuf; + size_t namelen, maillen; int max_length = 78; /* per rfc2822 */ - char *date; - int namelen; unsigned long time; int tz; if (pp->fmt == CMIT_FMT_ONELINE) return; - date = strchr(line, '>'); - if (!date) + + line_end = strchr(line, '\n'); + if (!line_end) { + line_end = strchr(line, '\0'); + if (!line_end) + return; + } + + linelen = ++line_end - line; + if (split_ident_line(&ident, line, linelen)) return; - namelen = ++date - line; - time = strtoul(date, &date, 10); + + + mailbuf = ident.mail_begin; + maillen = ident.mail_end - ident.mail_begin; + namebuf = ident.name_begin; + namelen = ident.name_end - ident.name_begin; + + if (pp->mailmap) + map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen); + + strbuf_init(&mail, 0); + strbuf_init(&name, 0); + + strbuf_add(&mail, mailbuf, maillen); + strbuf_add(&name, namebuf, namelen); + + namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */ + time = strtoul(ident.date_begin, &date, 10); tz = strtol(date, NULL, 10); if (pp->fmt == CMIT_FMT_EMAIL) { - char *name_tail = strchr(line, '<'); - int display_name_length; - if (!name_tail) - return; - while (line < name_tail && isspace(name_tail[-1])) - name_tail--; - display_name_length = name_tail - line; strbuf_addstr(sb, "From: "); - if (needs_rfc2047_encoding(line, display_name_length, RFC2047_ADDRESS)) { - add_rfc2047(sb, line, display_name_length, - encoding, RFC2047_ADDRESS); + if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) { + add_rfc2047(sb, name.buf, name.len, + encoding, RFC2047_ADDRESS); max_length = 76; /* per rfc2047 */ - } else if (needs_rfc822_quoting(line, display_name_length)) { + } else if (needs_rfc822_quoting(name.buf, name.len)) { struct strbuf quoted = STRBUF_INIT; - add_rfc822_quoted("ed, line, display_name_length); + add_rfc822_quoted("ed, name.buf, name.len); strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len, -6, 1, max_length); strbuf_release("ed); } else { - strbuf_add_wrapped_bytes(sb, line, display_name_length, - -6, 1, max_length); + strbuf_add_wrapped_bytes(sb, name.buf, name.len, + -6, 1, max_length); } - if (namelen - display_name_length + last_line_length(sb) > max_length) { + if (namelen - name.len + last_line_length(sb) > max_length) strbuf_addch(sb, '\n'); - if (!isspace(name_tail[0])) - strbuf_addch(sb, ' '); - } - strbuf_add(sb, name_tail, namelen - display_name_length); - strbuf_addch(sb, '\n'); + + strbuf_addf(sb, " <%s>\n", mail.buf); } else { - strbuf_addf(sb, "%s: %.*s%.*s\n", what, + strbuf_addf(sb, "%s: %.*s%s <%s>\n", what, (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, - " ", namelen, line); + " ", name.buf, mail.buf); } + + strbuf_release(&mail); + strbuf_release(&name); + switch (pp->fmt) { case CMIT_FMT_MEDIUM: strbuf_addf(sb, "Date: %s\n", show_date(time, tz, pp->date_mode)); @@ -586,7 +609,8 @@ char *logmsg_reencode(const struct commit *commit, return out; } -static int mailmap_name(char *email, int email_len, char *name, int name_len) +static int mailmap_name(const char **email, size_t *email_len, + const char **name, size_t *name_len) { static struct string_list *mail_map; if (!mail_map) { @@ -603,36 +627,26 @@ static size_t format_person_part(struct strbuf *sb, char part, const int placeholder_len = 2; int tz; unsigned long date = 0; - char person_name[1024]; - char person_mail[1024]; struct ident_split s; - const char *name_start, *name_end, *mail_start, *mail_end; + const char *name, *mail; + size_t maillen, namelen; if (split_ident_line(&s, msg, len) < 0) goto skip; - name_start = s.name_begin; - name_end = s.name_end; - mail_start = s.mail_begin; - mail_end = s.mail_end; - - if (part == 'N' || part == 'E') { /* mailmap lookup */ - snprintf(person_name, sizeof(person_name), "%.*s", - (int)(name_end - name_start), name_start); - snprintf(person_mail, sizeof(person_mail), "%.*s", - (int)(mail_end - mail_start), mail_start); - mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name)); - name_start = person_name; - name_end = name_start + strlen(person_name); - mail_start = person_mail; - mail_end = mail_start + strlen(person_mail); - } + name = s.name_begin; + namelen = s.name_end - s.name_begin; + mail = s.mail_begin; + maillen = s.mail_end - s.mail_begin; + + if (part == 'N' || part == 'E') /* mailmap lookup */ + mailmap_name(&mail, &maillen, &name, &namelen); if (part == 'n' || part == 'N') { /* name */ - strbuf_add(sb, name_start, name_end-name_start); + strbuf_add(sb, name, namelen); return placeholder_len; } if (part == 'e' || part == 'E') { /* email */ - strbuf_add(sb, mail_start, mail_end-mail_start); + strbuf_add(sb, mail, maillen); return placeholder_len; } @@ -960,12 +974,19 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder, switch (placeholder[0]) { case 'C': if (placeholder[1] == '(') { - const char *end = strchr(placeholder + 2, ')'); + const char *begin = placeholder + 2; + const char *end = strchr(begin, ')'); char color[COLOR_MAXLEN]; + if (!end) return 0; - color_parse_mem(placeholder + 2, - end - (placeholder + 2), + if (!prefixcmp(begin, "auto,")) { + if (!want_color(c->pretty_ctx->color)) + return end - placeholder + 1; + begin += 5; + } + color_parse_mem(begin, + end - begin, "--pretty format", color); strbuf_addstr(sb, color); return end - placeholder + 1; @@ -1294,7 +1315,7 @@ static void pp_header(const struct pretty_print_context *pp, continue; } - if (!memcmp(line, "parent ", 7)) { + if (!prefixcmp(line, "parent ")) { if (linelen != 48) die("bad parent line in commit"); continue; @@ -1318,11 +1339,11 @@ static void pp_header(const struct pretty_print_context *pp, * FULL shows both authors but not dates. * FULLER shows both authors and dates. */ - if (!memcmp(line, "author ", 7)) { + if (!prefixcmp(line, "author ")) { strbuf_grow(sb, linelen + 80); pp_user_info(pp, "Author", sb, line + 7, encoding); } - if (!memcmp(line, "committer ", 10) && + if (!prefixcmp(line, "committer ") && (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) { strbuf_grow(sb, linelen + 80); pp_user_info(pp, "Commit", sb, line + 10, encoding); @@ -1279,12 +1279,34 @@ int match_push_refs(struct ref *src, struct ref **dst, return 0; } +static inline int is_forwardable(struct ref* ref) +{ + struct object *o; + + if (!prefixcmp(ref->name, "refs/tags/")) + return 0; + + /* old object must be a commit */ + o = parse_object(ref->old_sha1); + if (!o || o->type != OBJ_COMMIT) + return 0; + + /* new object must be commit-ish */ + o = deref_tag(parse_object(ref->new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + + return 1; +} + void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, int force_update) { struct ref *ref; for (ref = remote_refs; ref; ref = ref->next) { + int force_ref_update = ref->force || force_update; + if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!send_mirror) @@ -1297,34 +1319,55 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, continue; } - /* This part determines what can overwrite what. - * The rules are: + /* + * The below logic determines whether an individual + * refspec A:B can be pushed. The push will succeed + * if any of the following are true: * - * (0) you can always use --force or +A:B notation to - * selectively force individual ref pairs. + * (1) the remote reference B does not exist * - * (1) if the old thing does not exist, it is OK. + * (2) the remote reference B is being removed (i.e., + * pushing :B where no source is specified) * - * (2) if you do not have the old thing, you are not allowed - * to overwrite it; you would not know what you are losing - * otherwise. + * (3) the update meets all fast-forwarding criteria: * - * (3) if both new and old are commit-ish, and new is a - * descendant of old, it is OK. + * (a) the destination is not under refs/tags/ + * (b) the old is a commit + * (c) the new is a descendant of the old * - * (4) regardless of all of the above, removing :B is - * always allowed. + * NOTE: We must actually have the old object in + * order to overwrite it in the remote reference, + * and the new object must be commit-ish. These are + * implied by (b) and (c) respectively. + * + * (4) it is forced using the +A:B notation, or by + * passing the --force argument */ - ref->nonfastforward = - !ref->deletion && - !is_null_sha1(ref->old_sha1) && - (!has_sha1_file(ref->old_sha1) - || !ref_newer(ref->new_sha1, ref->old_sha1)); + ref->not_forwardable = !is_forwardable(ref); - if (ref->nonfastforward && !ref->force && !force_update) { - ref->status = REF_STATUS_REJECT_NONFASTFORWARD; - continue; + ref->update = + !ref->deletion && + !is_null_sha1(ref->old_sha1); + + if (ref->update) { + ref->nonfastforward = + !has_sha1_file(ref->old_sha1) + || !ref_newer(ref->new_sha1, ref->old_sha1); + + if (ref->not_forwardable) { + ref->requires_force = 1; + if (!force_ref_update) { + ref->status = REF_STATUS_REJECT_ALREADY_EXISTS; + continue; + } + } else if (ref->nonfastforward) { + ref->requires_force = 1; + if (!force_ref_update) { + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + continue; + } + } } } } diff --git a/revision.c b/revision.c index 95d21e6472..d7562ee500 100644 --- a/revision.c +++ b/revision.c @@ -13,6 +13,7 @@ #include "decorate.h" #include "log-tree.h" #include "string-list.h" +#include "mailmap.h" volatile show_early_output_fn_t show_early_output; @@ -2219,6 +2220,51 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit) return 0; } +static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap) +{ + char *person, *endp; + size_t len, namelen, maillen; + const char *name; + const char *mail; + struct ident_split ident; + + person = strstr(buf->buf, what); + if (!person) + return 0; + + person += strlen(what); + endp = strchr(person, '\n'); + if (!endp) + return 0; + + len = endp - person; + + if (split_ident_line(&ident, person, len)) + return 0; + + mail = ident.mail_begin; + maillen = ident.mail_end - ident.mail_begin; + name = ident.name_begin; + namelen = ident.name_end - ident.name_begin; + + if (map_user(mailmap, &mail, &maillen, &name, &namelen)) { + struct strbuf namemail = STRBUF_INIT; + + strbuf_addf(&namemail, "%.*s <%.*s>", + (int)namelen, name, (int)maillen, mail); + + strbuf_splice(buf, ident.name_begin - buf->buf, + ident.mail_end - ident.name_begin + 1, + namemail.buf, namemail.len); + + strbuf_release(&namemail); + + return 1; + } + + return 0; +} + static int commit_match(struct commit *commit, struct rev_info *opt) { int retval; @@ -2237,6 +2283,14 @@ static int commit_match(struct commit *commit, struct rev_info *opt) if (buf.len) strbuf_addstr(&buf, commit->buffer); + if (opt->grep_filter.header_list && opt->mailmap) { + if (!buf.len) + strbuf_addstr(&buf, commit->buffer); + + commit_rewrite_person(&buf, "\nauthor ", opt->mailmap); + commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap); + } + /* Append "fake" message parts as needed */ if (opt->show_notes) { if (!buf.len) diff --git a/revision.h b/revision.h index 059bfff812..5da09ee3ef 100644 --- a/revision.h +++ b/revision.h @@ -135,6 +135,7 @@ struct rev_info { const char *mime_boundary; const char *patch_suffix; int numbered_files; + int reroll_count; char *message_id; struct string_list *ref_message_ids; const char *add_signoff; @@ -143,6 +144,7 @@ struct rev_info { const char *subject_prefix; int no_inline; int show_log_size; + struct string_list *mailmap; /* Filter by commit log message */ struct grep_opt grep_filter; diff --git a/send-pack.c b/send-pack.c index f50dfd9f48..1c375f0a28 100644 --- a/send-pack.c +++ b/send-pack.c @@ -229,6 +229,7 @@ int send_pack(struct send_pack_args *args, /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_UPTODATE: continue; default: diff --git a/sequencer.c b/sequencer.c index 22604902aa..aef5e8a017 100644 --- a/sequencer.c +++ b/sequencer.c @@ -186,14 +186,15 @@ static int error_dirty_index(struct replay_opts *opts) return -1; } -static int fast_forward_to(const unsigned char *to, const unsigned char *from) +static int fast_forward_to(const unsigned char *to, const unsigned char *from, + int unborn) { struct ref_lock *ref_lock; read_cache(); if (checkout_fast_forward(from, to, 1)) exit(1); /* the callee should have complained already */ - ref_lock = lock_any_ref_for_update("HEAD", from, 0); + ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0); return write_ref_sha1(ref_lock, to, "cherry-pick"); } @@ -390,7 +391,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) struct commit_message msg = { NULL, NULL, NULL, NULL, NULL }; char *defmsg = NULL; struct strbuf msgbuf = STRBUF_INIT; - int res; + int res, unborn = 0; if (opts->no_commit) { /* @@ -402,9 +403,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) if (write_cache_as_tree(head, 0, NULL)) die (_("Your index file is unmerged.")); } else { - if (get_sha1("HEAD", head)) - return error(_("You do not have a valid HEAD")); - if (index_differs_from("HEAD", 0)) + unborn = get_sha1("HEAD", head); + if (unborn) + hashcpy(head, EMPTY_TREE_SHA1_BIN); + if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0)) return error_dirty_index(opts); } discard_cache(); @@ -435,8 +437,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) else parent = commit->parents->item; - if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head)) - return fast_forward_to(commit->object.sha1, head); + if (opts->allow_ff && + ((parent && !hashcmp(parent->object.sha1, head)) || + (!parent && unborn))) + return fast_forward_to(commit->object.sha1, head, unborn); if (parent && parse_commit(parent) < 0) /* TRANSLATORS: The first %s will be "revert" or @@ -1,5 +1,6 @@ #include "cache.h" #include "dir.h" +#include "string-list.h" static int inside_git_dir = -1; static int inside_work_tree = -1; @@ -621,16 +622,38 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_ } /* + * A "string_list_each_func_t" function that canonicalizes an entry + * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or + * discards it if unusable. + */ +static int canonicalize_ceiling_entry(struct string_list_item *item, + void *unused) +{ + char *ceil = item->string; + const char *real_path; + + if (!*ceil || !is_absolute_path(ceil)) + return 0; + real_path = real_path_if_valid(ceil); + if (!real_path) + return 0; + free(item->string); + item->string = xstrdup(real_path); + return 1; +} + +/* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. */ static const char *setup_git_directory_gently_1(int *nongit_ok) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); + struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; static char cwd[PATH_MAX+1]; const char *gitdirenv, *ret; char *gitfile; - int len, offset, offset_parent, ceil_offset; + int len, offset, offset_parent, ceil_offset = -1; dev_t current_device = 0; int one_filesystem = 1; @@ -655,7 +678,14 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) if (gitdirenv) return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); - ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); + if (env_ceiling_dirs) { + string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); + filter_string_list(&ceiling_dirs, 0, + canonicalize_ceiling_entry, NULL); + ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs); + string_list_clear(&ceiling_dirs, 0); + } + if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) ceil_offset = 1; @@ -425,6 +425,32 @@ void strbuf_add_lines(struct strbuf *out, const char *prefix, strbuf_complete_line(out); } +void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) +{ + while (*s) { + size_t len = strcspn(s, "\"<>&"); + strbuf_add(buf, s, len); + s += len; + switch (*s) { + case '"': + strbuf_addstr(buf, """); + break; + case '<': + strbuf_addstr(buf, "<"); + break; + case '>': + strbuf_addstr(buf, ">"); + break; + case '&': + strbuf_addstr(buf, "&"); + break; + case 0: + return; + } + s++; + } +} + static int is_rfc3986_reserved(char ch) { switch (ch) { @@ -136,6 +136,12 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size); +/* + * Append s to sb, with the characters '<', '>', '&' and '"' converted + * into XML entities. + */ +extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s); + static inline void strbuf_complete_line(struct strbuf *sb) { if (sb->len && sb->buf[sb->len - 1] != '\n') diff --git a/string-list.c b/string-list.c index 397e6cfa7d..aabb25ef4c 100644 --- a/string-list.c +++ b/string-list.c @@ -7,10 +7,11 @@ static int get_entry_index(const struct string_list *list, const char *string, int *exact_match) { int left = -1, right = list->nr; + compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; while (left + 1 < right) { int middle = (left + right) / 2; - int compare = strcmp(string, list->items[middle].string); + int compare = cmp(string, list->items[middle].string); if (compare < 0) right = middle; else if (compare > 0) @@ -96,8 +97,9 @@ void string_list_remove_duplicates(struct string_list *list, int free_util) { if (list->nr > 1) { int src, dst; + compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; for (src = dst = 1; src < list->nr; src++) { - if (!strcmp(list->items[dst - 1].string, list->items[src].string)) { + if (!cmp(list->items[dst - 1].string, list->items[src].string)) { if (list->strdup_strings) free(list->items[src].string); if (free_util) @@ -145,26 +147,6 @@ void string_list_remove_empty_items(struct string_list *list, int free_util) { filter_string_list(list, free_util, item_is_not_empty, NULL); } -char *string_list_longest_prefix(const struct string_list *prefixes, - const char *string) -{ - int i, max_len = -1; - char *retval = NULL; - - for (i = 0; i < prefixes->nr; i++) { - char *prefix = prefixes->items[i].string; - if (!prefixcmp(string, prefix)) { - int len = strlen(prefix); - if (len > max_len) { - retval = prefix; - max_len = len; - } - } - } - - return retval; -} - void string_list_clear(struct string_list *list, int free_util) { if (list->items) { @@ -230,15 +212,20 @@ struct string_list_item *string_list_append(struct string_list *list, list->strdup_strings ? xstrdup(string) : (char *)string); } +/* Yuck */ +static compare_strings_fn compare_for_qsort; + +/* Only call this from inside sort_string_list! */ static int cmp_items(const void *a, const void *b) { const struct string_list_item *one = a; const struct string_list_item *two = b; - return strcmp(one->string, two->string); + return compare_for_qsort(one->string, two->string); } void sort_string_list(struct string_list *list) { + compare_for_qsort = list->cmp ? list->cmp : strcmp; qsort(list->items, list->nr, sizeof(*list->items), cmp_items); } @@ -246,8 +233,10 @@ struct string_list_item *unsorted_string_list_lookup(struct string_list *list, const char *string) { int i; + compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; + for (i = 0; i < list->nr; i++) - if (!strcmp(string, list->items[i].string)) + if (!cmp(string, list->items[i].string)) return list->items + i; return NULL; } diff --git a/string-list.h b/string-list.h index c50b0d0dea..de6769c92d 100644 --- a/string-list.h +++ b/string-list.h @@ -5,10 +5,14 @@ struct string_list_item { char *string; void *util; }; + +typedef int (*compare_strings_fn)(const char *, const char *); + struct string_list { struct string_list_item *items; unsigned int nr, alloc; unsigned int strdup_strings:1; + compare_strings_fn cmp; /* NULL uses strcmp() */ }; #define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 } @@ -45,15 +49,6 @@ void filter_string_list(struct string_list *list, int free_util, */ void string_list_remove_empty_items(struct string_list *list, int free_util); -/* - * Return the longest string in prefixes that is a prefix (in the - * sense of prefixcmp()) of string, or NULL if no such prefix exists. - * This function does not require the string_list to be sorted (it - * does a linear search). - */ -char *string_list_longest_prefix(const struct string_list *prefixes, const char *string); - - /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); int string_list_find_insert_index(const struct string_list *list, const char *string, diff --git a/t/Makefile b/t/Makefile index 5c6de8169b..1923cc104b 100644 --- a/t/Makefile +++ b/t/Makefile @@ -17,6 +17,7 @@ TEST_LINT ?= test-lint-duplicates test-lint-executable # Shell quote; SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) +PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh)) @@ -44,7 +45,7 @@ clean-except-prove-cache: clean: clean-except-prove-cache $(RM) .prove -test-lint: test-lint-duplicates test-lint-executable +test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax test-lint-duplicates: @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \ @@ -56,6 +57,9 @@ test-lint-executable: test -z "$$bad" || { \ echo >&2 "non-executable tests:" $$bad; exit 1; } +test-lint-shell-syntax: + @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) + aggregate-results-and-cleanup: $(T) $(MAKE) aggregate-results $(MAKE) clean @@ -88,7 +92,7 @@ test-results: mkdir -p test-results test-results/git-smoke.tar.gz: test-results - $(PERL_PATH) ./harness \ + '$(PERL_PATH_SQ)' ./harness \ --archive="test-results/git-smoke.tar.gz" \ $(T) diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl new file mode 100755 index 0000000000..8b5a71dc05 --- /dev/null +++ b/t/check-non-portable-shell.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +# Test t0000..t9999.sh for non portable shell scripts +# This script can be called with one or more filenames as parameters + +use strict; +use warnings; + +my $exit_code=0; + +sub err { + my $msg = shift; + print "$ARGV:$.: error: $msg: $_\n"; + $exit_code = 1; +} + +while (<>) { + chomp; + /^\s*sed\s+-i/ and err 'sed -i is not portable'; + /^\s*echo\s+-n/ and err 'echo -n is not portable (please use printf)'; + /^\s*declare\s+/ and err 'arrays/declare not portable'; + /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)'; + /test\s+[^=]*==/ and err '"test a == b" is not portable (please use =)'; + # this resets our $. for each file + close ARGV if eof; +} +exit $exit_code; diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 562cf41cad..cefe33d6d1 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -45,39 +45,176 @@ test_expect_failure 'pretend we have a known breakage' ' false ' -test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' " - mkdir passing-todo && - (cd passing-todo && - cat >passing-todo.sh <<-EOF && - #!$SHELL_PATH - - test_description='A passing TODO test +run_sub_test_lib_test () { + name="$1" descr="$2" # stdin is the body of the test code + mkdir "$name" && + ( + cd "$name" && + cat >"$name.sh" <<-EOF && + #!$SHELL_PATH + + test_description='$descr (run in sub test-lib) + + This is run in a sub test-lib so that we do not get incorrect + passing metrics + ' + + # Point to the t/test-lib.sh, which isn't in ../ as usual + . "\$TEST_DIRECTORY"/test-lib.sh + EOF + cat >>"$name.sh" && + chmod +x "$name.sh" && + export TEST_DIRECTORY && + ./"$name.sh" >out 2>err + ) +} - This is run in a sub test-lib so that we do not get incorrect - passing metrics - ' +check_sub_test_lib_test () { + name="$1" # stdin is the expected output from the test + ( + cd "$name" && + ! test -s err && + sed -e 's/^> //' -e 's/Z$//' >expect && + test_cmp expect out + ) +} + +test_expect_success 'pretend we have a fully passing test suite' " + run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF && + for i in 1 2 3 + do + test_expect_success \"passing test #\$i\" 'true' + done + test_done + EOF + check_sub_test_lib_test full-pass <<-\\EOF + > ok 1 - passing test #1 + > ok 2 - passing test #2 + > ok 3 - passing test #3 + > # passed all 3 test(s) + > 1..3 + EOF +" - # Point to the t/test-lib.sh, which isn't in ../ as usual - TEST_DIRECTORY=\"$TEST_DIRECTORY\" - . \"\$TEST_DIRECTORY\"/test-lib.sh +test_expect_success 'pretend we have a partially passing test suite' " + test_must_fail run_sub_test_lib_test \ + partial-pass '2/3 tests passing' <<-\\EOF && + test_expect_success 'passing test #1' 'true' + test_expect_success 'failing test #2' 'false' + test_expect_success 'passing test #3' 'true' + test_done + EOF + check_sub_test_lib_test partial-pass <<-\\EOF + > ok 1 - passing test #1 + > not ok 2 - failing test #2 + # false + > ok 3 - passing test #3 + > # failed 1 among 3 test(s) + > 1..3 + EOF +" - test_expect_failure 'pretend we have fixed a known breakage' ' - : - ' +test_expect_success 'pretend we have a known breakage' " + run_sub_test_lib_test failing-todo 'A failing TODO test' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_failure 'pretend we have a known breakage' 'false' + test_done + EOF + check_sub_test_lib_test failing-todo <<-\\EOF + > ok 1 - passing test + > not ok 2 - pretend we have a known breakage # TODO known breakage + > # still have 1 known breakage(s) + > # passed all remaining 1 test(s) + > 1..2 + EOF +" +test_expect_success 'pretend we have fixed a known breakage' " + run_sub_test_lib_test passing-todo 'A passing TODO test' <<-\\EOF && + test_expect_failure 'pretend we have fixed a known breakage' 'true' test_done EOF - chmod +x passing-todo.sh && - ./passing-todo.sh >out 2>err && - ! test -s err && - sed -e 's/^> //' >expect <<-\\EOF && - > ok 1 - pretend we have fixed a known breakage # TODO known breakage - > # fixed 1 known breakage(s) - > # passed all 1 test(s) + check_sub_test_lib_test passing-todo <<-\\EOF + > ok 1 - pretend we have fixed a known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) > 1..1 EOF - test_cmp expect out) " + +test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' " + run_sub_test_lib_test partially-passing-todos \ + '2 TODO tests, one passing' <<-\\EOF && + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_success 'pretend we have a passing test' 'true' + test_expect_failure 'pretend we have fixed another known breakage' 'true' + test_done + EOF + check_sub_test_lib_test partially-passing-todos <<-\\EOF + > not ok 1 - pretend we have a known breakage # TODO known breakage + > ok 2 - pretend we have a passing test + > ok 3 - pretend we have fixed another known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > # still have 1 known breakage(s) + > # passed all remaining 1 test(s) + > 1..3 + EOF +" + +test_expect_success 'pretend we have a pass, fail, and known breakage' " + test_must_fail run_sub_test_lib_test \ + mixed-results1 'mixed results #1' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_success 'failing test' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_done + EOF + check_sub_test_lib_test mixed-results1 <<-\\EOF + > ok 1 - passing test + > not ok 2 - failing test + > # false + > not ok 3 - pretend we have a known breakage # TODO known breakage + > # still have 1 known breakage(s) + > # failed 1 among remaining 2 test(s) + > 1..3 + EOF +" + +test_expect_success 'pretend we have a mix of all possible results' " + test_must_fail run_sub_test_lib_test \ + mixed-results2 'mixed results #2' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'failing test' 'false' + test_expect_success 'failing test' 'false' + test_expect_success 'failing test' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_failure 'pretend we have fixed a known breakage' 'true' + test_done + EOF + check_sub_test_lib_test mixed-results2 <<-\\EOF + > ok 1 - passing test + > ok 2 - passing test + > ok 3 - passing test + > ok 4 - passing test + > not ok 5 - failing test + > # false + > not ok 6 - failing test + > # false + > not ok 7 - failing test + > # false + > not ok 8 - pretend we have a known breakage # TODO known breakage + > not ok 9 - pretend we have a known breakage # TODO known breakage + > ok 10 - pretend we have fixed a known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > # still have 2 known breakage(s) + > # failed 3 among remaining 7 test(s) + > 1..10 + EOF +" + test_set_prereq HAVEIT haveit=no test_expect_success HAVEIT 'test runs if prerequisite is satisfied' ' @@ -159,19 +296,8 @@ then fi test_expect_success 'tests clean up even on failures' " - mkdir failing-cleanup && - ( - cd failing-cleanup && - - cat >failing-cleanup.sh <<-EOF && - #!$SHELL_PATH - - test_description='Failing tests with cleanup commands' - - # Point to the t/test-lib.sh, which isn't in ../ as usual - TEST_DIRECTORY=\"$TEST_DIRECTORY\" - . \"\$TEST_DIRECTORY\"/test-lib.sh - + test_must_fail run_sub_test_lib_test \ + failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF && test_expect_success 'tests clean up even after a failure' ' touch clean-after-failure && test_when_finished rm clean-after-failure && @@ -181,29 +307,21 @@ test_expect_success 'tests clean up even on failures' " test_when_finished \"(exit 2)\" ' test_done - EOF - - chmod +x failing-cleanup.sh && - test_must_fail ./failing-cleanup.sh >out 2>err && - ! test -s err && - ! test -f \"trash directory.failing-cleanup/clean-after-failure\" && - sed -e 's/Z$//' -e 's/^> //' >expect <<-\\EOF && - > not ok - 1 tests clean up even after a failure + check_sub_test_lib_test failing-cleanup <<-\\EOF + > not ok 1 - tests clean up even after a failure > # Z > # touch clean-after-failure && > # test_when_finished rm clean-after-failure && > # (exit 1) > # Z - > not ok - 2 failure to clean up causes the test to fail + > not ok 2 - failure to clean up causes the test to fail > # Z > # test_when_finished \"(exit 2)\" > # Z > # failed 2 among 2 test(s) > 1..2 EOF - test_cmp expect out - ) " ################################################################ diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index 807b8b88e2..43b25137e9 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -206,6 +206,43 @@ test_expect_success 'patterns starting with exclamation' ' attr_check "!f" foo ' +test_expect_success '"**" test' ' + echo "**/f foo=bar" >.gitattributes && + cat <<\EOF >expect && +f: foo: bar +a/f: foo: bar +a/b/f: foo: bar +a/b/c/f: foo: bar +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + +test_expect_success '"**" with no slashes test' ' + echo "a**f foo=bar" >.gitattributes && + git check-attr foo -- "f" >actual && + cat <<\EOF >expect && +f: foo: unspecified +af: foo: bar +axf: foo: bar +a/f: foo: unspecified +a/b/f: foo: unspecified +a/b/c/f: foo: unspecified +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "af" >>actual 2>err && + git check-attr foo -- "axf" >>actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + test_expect_success 'setup bare' ' git clone --bare . bare.git && cd bare.git diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh index ec6c1b3f8a..5378787e1b 100755 --- a/t/t0024-crlf-archive.sh +++ b/t/t0024-crlf-archive.sh @@ -3,7 +3,12 @@ test_description='respect crlf in git archive' . ./test-lib.sh -UNZIP=${UNZIP:-unzip} +GIT_UNZIP=${GIT_UNZIP:-unzip} + +test_lazy_prereq UNZIP ' + "$GIT_UNZIP" -v + test $? -ne 127 +' test_expect_success setup ' @@ -26,18 +31,11 @@ test_expect_success 'tar archive' ' ' -"$UNZIP" -v >/dev/null 2>&1 -if [ $? -eq 127 ]; then - say "Skipping ZIP test, because unzip was not found" -else - test_set_prereq UNZIP -fi - test_expect_success UNZIP 'zip archive' ' git archive --format=zip HEAD >test.zip && - ( mkdir unzipped && cd unzipped && unzip ../test.zip ) && + ( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) && test_cmp sample unzipped/sample diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 4ef2345982..09a42a428e 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -93,47 +93,32 @@ norm_path /d1/s1//../s2/../../d2 /d2 POSIX norm_path /d1/.../d2 /d1/.../d2 POSIX norm_path /d1/..././../d2 /d1/d2 POSIX -ancestor / "" -1 ancestor / / -1 -ancestor /foo "" -1 -ancestor /foo : -1 -ancestor /foo ::. -1 -ancestor /foo ::..:: -1 ancestor /foo / 0 ancestor /foo /fo -1 ancestor /foo /foo -1 -ancestor /foo /foo/ -1 ancestor /foo /bar -1 -ancestor /foo /bar/ -1 ancestor /foo /foo/bar -1 -ancestor /foo /foo:/bar/ -1 -ancestor /foo /foo/:/bar/ -1 -ancestor /foo /foo::/bar/ -1 -ancestor /foo /:/foo:/bar/ 0 -ancestor /foo /foo:/:/bar/ 0 -ancestor /foo /:/bar/:/foo 0 -ancestor /foo/bar "" -1 +ancestor /foo /foo:/bar -1 +ancestor /foo /:/foo:/bar 0 +ancestor /foo /foo:/:/bar 0 +ancestor /foo /:/bar:/foo 0 ancestor /foo/bar / 0 ancestor /foo/bar /fo -1 -ancestor /foo/bar foo -1 ancestor /foo/bar /foo 4 -ancestor /foo/bar /foo/ 4 ancestor /foo/bar /foo/ba -1 ancestor /foo/bar /:/fo 0 ancestor /foo/bar /foo:/foo/ba 4 ancestor /foo/bar /bar -1 -ancestor /foo/bar /bar/ -1 -ancestor /foo/bar /fo: -1 -ancestor /foo/bar :/fo -1 -ancestor /foo/bar /foo:/bar/ 4 -ancestor /foo/bar /:/foo:/bar/ 4 -ancestor /foo/bar /foo:/:/bar/ 4 -ancestor /foo/bar /:/bar/:/fo 0 -ancestor /foo/bar /:/bar/ 0 -ancestor /foo/bar .:/foo/. 4 -ancestor /foo/bar .:/foo/.:.: 4 -ancestor /foo/bar /foo/./:.:/bar 4 -ancestor /foo/bar .:/bar -1 +ancestor /foo/bar /fo -1 +ancestor /foo/bar /foo:/bar 4 +ancestor /foo/bar /:/foo:/bar 4 +ancestor /foo/bar /foo:/:/bar 4 +ancestor /foo/bar /:/bar:/fo 0 +ancestor /foo/bar /:/bar 0 +ancestor /foo/bar /foo 4 +ancestor /foo/bar /foo:/bar 4 +ancestor /foo/bar /bar -1 test_expect_success 'strip_path_suffix' ' test c:/msysgit = $(test-path-utils strip_path_suffix \ diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh index 41c8826a74..dbfc05ebdc 100755 --- a/t/t0063-string-list.sh +++ b/t/t0063-string-list.sh @@ -17,14 +17,6 @@ test_split () { " } -test_longest_prefix () { - test "$(test-string-list longest_prefix "$1" "$2")" = "$3" -} - -test_no_longest_prefix () { - test_must_fail test-string-list longest_prefix "$1" "$2" -} - test_split "foo:bar:baz" ":" "-1" <<EOF 3 [0]: "foo" @@ -96,26 +88,4 @@ test_expect_success "test remove_duplicates" ' test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)" ' -test_expect_success "test longest_prefix" ' - test_no_longest_prefix - '' && - test_no_longest_prefix - x && - test_longest_prefix "" x "" && - test_longest_prefix x x x && - test_longest_prefix "" foo "" && - test_longest_prefix : foo "" && - test_longest_prefix f foo f && - test_longest_prefix foo foobar foo && - test_longest_prefix foo foo foo && - test_no_longest_prefix bar foo && - test_no_longest_prefix bar:bar foo && - test_no_longest_prefix foobar foo && - test_longest_prefix foo:bar foo foo && - test_longest_prefix foo:bar bar bar && - test_longest_prefix foo::bar foo foo && - test_longest_prefix foo:foobar foo foo && - test_longest_prefix foobar:foo foo foo && - test_longest_prefix foo: bar "" && - test_longest_prefix :foo bar "" -' - test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 08aa24ca15..d730734fde 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -237,4 +237,35 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' ' ) ' +test_expect_success 'fsck notices "." and ".." in trees' ' + ( + git init dots && + cd dots && + blob=$(echo foo | git hash-object -w --stdin) && + tab=$(printf "\\t") && + git mktree <<-EOF && + 100644 blob $blob$tab. + 100644 blob $blob$tab.. + EOF + git fsck 2>out && + cat out && + grep "warning.*\\." out + ) +' + +test_expect_success 'fsck notices ".git" in trees' ' + ( + git init dotgit && + cd dotgit && + blob=$(echo foo | git hash-object -w --stdin) && + tab=$(printf "\\t") && + git mktree <<-EOF && + 100644 blob $blob$tab.git + EOF + git fsck 2>out && + cat out && + grep "warning.*\\.git" out + ) +' + test_done diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh index d709ecf8df..4969edb314 100755 --- a/t/t1505-rev-parse-last.sh +++ b/t/t1505-rev-parse-last.sh @@ -32,32 +32,24 @@ test_expect_success 'setup' ' # # and 'side' should be the last branch -test_rev_equivalent () { - - git rev-parse "$1" > expect && - git rev-parse "$2" > output && - test_cmp expect output - -} - test_expect_success '@{-1} works' ' - test_rev_equivalent side @{-1} + test_cmp_rev side @{-1} ' test_expect_success '@{-1}~2 works' ' - test_rev_equivalent side~2 @{-1}~2 + test_cmp_rev side~2 @{-1}~2 ' test_expect_success '@{-1}^2 works' ' - test_rev_equivalent side^2 @{-1}^2 + test_cmp_rev side^2 @{-1}^2 ' test_expect_success '@{-1}@{1} works' ' - test_rev_equivalent side@{1} @{-1}@{1} + test_cmp_rev side@{1} @{-1}@{1} ' test_expect_success '@{-2} works' ' - test_rev_equivalent master @{-2} + test_cmp_rev master @{-2} ' test_expect_success '@{-3} fails' ' diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index dc2f0458fd..efb7ebc91f 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -220,4 +220,22 @@ test_expect_success 'pattern matches prefix completely' ' test_cmp expect actual ' +test_expect_success 'ls-files with "**" patterns' ' + cat <<\EOF >expect && +a.1 +one/a.1 +one/two/a.1 +three/a.1 +EOF + git ls-files -o -i --exclude "**/a.1" >actual + test_cmp expect actual +' + + +test_expect_success 'ls-files with "**" patterns and no slashes' ' + : >expect && + git ls-files -o -i --exclude "one**a.1" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh new file mode 100755 index 0000000000..af54c83111 --- /dev/null +++ b/t/t3070-wildmatch.sh @@ -0,0 +1,195 @@ +#!/bin/sh + +test_description='wildmatch tests' + +. ./test-lib.sh + +match() { + if [ $1 = 1 ]; then + test_expect_success "wildmatch: match '$3' '$4'" " + test-wildmatch wildmatch '$3' '$4' + " + else + test_expect_success "wildmatch: no match '$3' '$4'" " + ! test-wildmatch wildmatch '$3' '$4' + " + fi + if [ $2 = 1 ]; then + test_expect_success "fnmatch: match '$3' '$4'" " + test-wildmatch fnmatch '$3' '$4' + " + elif [ $2 = 0 ]; then + test_expect_success "fnmatch: no match '$3' '$4'" " + ! test-wildmatch fnmatch '$3' '$4' + " +# else +# test_expect_success BROKEN_FNMATCH "fnmatch: '$3' '$4'" " +# ! test-wildmatch fnmatch '$3' '$4' +# " + fi +} + +# Basic wildmat features +match 1 1 foo foo +match 0 0 foo bar +match 1 1 '' "" +match 1 1 foo '???' +match 0 0 foo '??' +match 1 1 foo '*' +match 1 1 foo 'f*' +match 0 0 foo '*f' +match 1 1 foo '*foo*' +match 1 1 foobar '*ob*a*r*' +match 1 1 aaaaaaabababab '*ab' +match 1 1 'foo*' 'foo\*' +match 0 0 foobar 'foo\*bar' +match 1 1 'f\oo' 'f\\oo' +match 1 1 ball '*[al]?' +match 0 0 ten '[ten]' +match 0 1 ten '**[!te]' +match 0 0 ten '**[!ten]' +match 1 1 ten 't[a-g]n' +match 0 0 ten 't[!a-g]n' +match 1 1 ton 't[!a-g]n' +match 1 1 ton 't[^a-g]n' +match 1 x 'a]b' 'a[]]b' +match 1 x a-b 'a[]-]b' +match 1 x 'a]b' 'a[]-]b' +match 0 x aab 'a[]-]b' +match 1 x aab 'a[]a-]b' +match 1 1 ']' ']' + +# Extended slash-matching features +match 0 0 'foo/baz/bar' 'foo*bar' +match 0 0 'foo/baz/bar' 'foo**bar' +match 0 1 'foobazbar' 'foo**bar' +match 1 1 'foo/baz/bar' 'foo/**/bar' +match 1 0 'foo/baz/bar' 'foo/**/**/bar' +match 1 0 'foo/b/a/z/bar' 'foo/**/bar' +match 1 0 'foo/b/a/z/bar' 'foo/**/**/bar' +match 1 0 'foo/bar' 'foo/**/bar' +match 1 0 'foo/bar' 'foo/**/**/bar' +match 0 0 'foo/bar' 'foo?bar' +match 0 0 'foo/bar' 'foo[/]bar' +match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' +match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' +match 1 0 'foo' '**/foo' +match 1 x 'XXX/foo' '**/foo' +match 1 0 'bar/baz/foo' '**/foo' +match 0 0 'bar/baz/foo' '*/foo' +match 0 0 'foo/bar/baz' '**/bar*' +match 1 0 'deep/foo/bar/baz' '**/bar/*' +match 0 0 'deep/foo/bar/baz/' '**/bar/*' +match 1 0 'deep/foo/bar/baz/' '**/bar/**' +match 0 0 'deep/foo/bar' '**/bar/*' +match 1 0 'deep/foo/bar/' '**/bar/**' +match 0 0 'foo/bar/baz' '**/bar**' +match 1 0 'foo/bar/baz/x' '*/bar/**' +match 0 0 'deep/foo/bar/baz/x' '*/bar/**' +match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*' + +# Various additional tests +match 0 0 'acrt' 'a[c-c]st' +match 1 1 'acrt' 'a[c-c]rt' +match 0 0 ']' '[!]-]' +match 1 x 'a' '[!]-]' +match 0 0 '' '\' +match 0 x '\' '\' +match 0 x 'XXX/\' '*/\' +match 1 x 'XXX/\' '*/\\' +match 1 1 'foo' 'foo' +match 1 1 '@foo' '@foo' +match 0 0 'foo' '@foo' +match 1 1 '[ab]' '\[ab]' +match 1 1 '[ab]' '[[]ab]' +match 1 x '[ab]' '[[:]ab]' +match 0 x '[ab]' '[[::]ab]' +match 1 x '[ab]' '[[:digit]ab]' +match 1 x '[ab]' '[\[:]ab]' +match 1 1 '?a?b' '\??\?b' +match 1 1 'abc' '\a\b\c' +match 0 0 'foo' '' +match 1 0 'foo/bar/baz/to' '**/t[o]' + +# Character class tests +match 1 x 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]' +match 0 x 'a' '[[:digit:][:upper:][:space:]]' +match 1 x 'A' '[[:digit:][:upper:][:space:]]' +match 1 x '1' '[[:digit:][:upper:][:space:]]' +match 0 x '1' '[[:digit:][:upper:][:spaci:]]' +match 1 x ' ' '[[:digit:][:upper:][:space:]]' +match 0 x '.' '[[:digit:][:upper:][:space:]]' +match 1 x '.' '[[:digit:][:punct:][:space:]]' +match 1 x '5' '[[:xdigit:]]' +match 1 x 'f' '[[:xdigit:]]' +match 1 x 'D' '[[:xdigit:]]' +match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' +match 1 x '5' '[a-c[:digit:]x-z]' +match 1 x 'b' '[a-c[:digit:]x-z]' +match 1 x 'y' '[a-c[:digit:]x-z]' +match 0 x 'q' '[a-c[:digit:]x-z]' + +# Additional tests, including some malformed wildmats +match 1 x ']' '[\\-^]' +match 0 0 '[' '[\\-^]' +match 1 x '-' '[\-_]' +match 1 x ']' '[\]]' +match 0 0 '\]' '[\]]' +match 0 0 '\' '[\]]' +match 0 0 'ab' 'a[]b' +match 0 x 'a[]b' 'a[]b' +match 0 x 'ab[' 'ab[' +match 0 0 'ab' '[!' +match 0 0 'ab' '[-' +match 1 1 '-' '[-]' +match 0 0 '-' '[a-' +match 0 0 '-' '[!a-' +match 1 x '-' '[--A]' +match 1 x '5' '[--A]' +match 1 1 ' ' '[ --]' +match 1 1 '$' '[ --]' +match 1 1 '-' '[ --]' +match 0 0 '0' '[ --]' +match 1 x '-' '[---]' +match 1 x '-' '[------]' +match 0 0 'j' '[a-e-n]' +match 1 x '-' '[a-e-n]' +match 1 x 'a' '[!------]' +match 0 0 '[' '[]-a]' +match 1 x '^' '[]-a]' +match 0 0 '^' '[!]-a]' +match 1 x '[' '[!]-a]' +match 1 1 '^' '[a^bc]' +match 1 x '-b]' '[a-]b]' +match 0 0 '\' '[\]' +match 1 1 '\' '[\\]' +match 0 0 '\' '[!\\]' +match 1 1 'G' '[A-\\]' +match 0 0 'aaabbb' 'b*a' +match 0 0 'aabcaa' '*ba*' +match 1 1 ',' '[,]' +match 1 1 ',' '[\\,]' +match 1 1 '\' '[\\,]' +match 1 1 '-' '[,-.]' +match 0 0 '+' '[,-.]' +match 0 0 '-.]' '[,-.]' +match 1 1 '2' '[\1-\3]' +match 1 1 '3' '[\1-\3]' +match 0 0 '4' '[\1-\3]' +match 1 1 '\' '[[-\]]' +match 1 1 '[' '[[-\]]' +match 1 1 ']' '[[-\]]' +match 0 0 '-' '[[-\]]' + +# Test recursion and the abort code (use "wildtest -i" to see iteration counts) +match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t' +match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t' + +test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 32fdc9938e..8462be1db6 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -29,12 +29,6 @@ Initial setup: . "$TEST_DIRECTORY"/lib-rebase.sh -test_cmp_rev () { - git rev-parse --verify "$1" >expect.rev && - git rev-parse --verify "$2" >actual.rev && - test_cmp expect.rev actual.rev -} - set_fake_editor # WARNING: Modifications to the initial repository can change the SHA ID used diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 34c86e5de6..6f489e20ee 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -100,4 +100,13 @@ test_expect_success 'revert forbidden on dirty working tree' ' ' +test_expect_success 'chery-pick on unborn branch' ' + git checkout --orphan unborn && + git rm --cached -r . && + rm -rf * && + git cherry-pick initial && + git diff --quiet initial && + ! test_cmp_rev initial HEAD +' + test_done diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh index 51ca391e47..373aad623c 100755 --- a/t/t3506-cherry-pick-ff.sh +++ b/t/t3506-cherry-pick-ff.sh @@ -105,4 +105,12 @@ test_expect_success 'cherry pick a root commit with --ff' ' test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1" ' +test_expect_success 'chery-pick --ff on unborn branch' ' + git checkout --orphan unborn && + git rm --cached -r . && + rm -rf * && + git cherry-pick --ff first && + test_cmp_rev first HEAD +' + test_done diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index c82f7210c4..223b98433c 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -11,12 +11,6 @@ test_description='test cherry-pick and revert with conflicts . ./test-lib.sh -test_cmp_rev () { - git rev-parse --verify "$1" >expect.rev && - git rev-parse --verify "$2" >actual.rev && - test_cmp expect.rev actual.rev -} - pristine_detach () { git checkout -f "$1^0" && git read-tree -u --reset HEAD && diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh index 340afc760d..4e7136b837 100755 --- a/t/t3508-cherry-pick-many-commits.sh +++ b/t/t3508-cherry-pick-many-commits.sh @@ -5,15 +5,11 @@ test_description='test cherry-picking many commits' . ./test-lib.sh check_head_differs_from() { - head=$(git rev-parse --verify HEAD) && - arg=$(git rev-parse --verify "$1") && - test "$head" != "$arg" + ! test_cmp_rev HEAD "$1" } check_head_equals() { - head=$(git rev-parse --verify HEAD) && - arg=$(git rev-parse --verify "$1") && - test "$head" = "$arg" + test_cmp_rev HEAD "$1" } test_expect_success setup ' diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index b5fb527b2e..7b7a89dbd5 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -24,12 +24,6 @@ pristine_detach () { git clean -d -f -f -q -x } -test_cmp_rev () { - git rev-parse --verify "$1" >expect.rev && - git rev-parse --verify "$2" >actual.rev && - test_cmp expect.rev actual.rev -} - test_expect_success setup ' git config advice.detachedhead false && echo unrelated >unrelated && diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 90fd598c74..7fa3647514 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -271,6 +271,22 @@ test_expect_success 'multiple files' ' ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch ' +test_expect_success 'reroll count' ' + rm -fr patches && + git format-patch -o patches --cover-letter --reroll-count 4 master..side >list && + ! grep -v "^patches/v4-000[0-3]-" list && + sed -n -e "/^Subject: /p" $(cat list) >subjects && + ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects +' + +test_expect_success 'reroll count (-v)' ' + rm -fr patches && + git format-patch -o patches --cover-letter -v4 master..side >list && + ! grep -v "^patches/v4-000[0-3]-" list && + sed -n -e "/^Subject: /p" $(cat list) >subjects && + ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects +' + check_threading () { expect="$1" && shift && @@ -963,4 +979,46 @@ test_expect_success 'format patch ignores color.ui' ' test_cmp expect actual ' +test_expect_success 'cover letter using branch description (1)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter master >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (2)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (3)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter ^master rebuild-1 >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (4)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter master.. >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (5)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter -2 HEAD >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (6)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter -2 >actual && + grep hello actual >/dev/null +' + test_done diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 1f182f612c..842b7549ec 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -149,6 +149,104 @@ test_expect_success 'No mailmap files, but configured' ' test_cmp expect actual ' +test_expect_success 'setup mailmap blob tests' ' + git checkout -b map && + test_when_finished "git checkout master" && + cat >just-bugs <<-\EOF && + Blob Guy <bugs@company.xx> + EOF + cat >both <<-\EOF && + Blob Guy <author@example.com> + Blob Guy <bugs@company.xx> + EOF + git add just-bugs both && + git commit -m "my mailmaps" && + echo "Repo Guy <author@example.com>" >.mailmap && + echo "Internal Guy <author@example.com>" >internal.map +' + +test_expect_success 'mailmap.blob set' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Repo Guy (1): + initial + + EOF + git -c mailmap.blob=map:just-bugs shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob overrides .mailmap' ' + cat >expect <<-\EOF && + Blob Guy (2): + initial + second + + EOF + git -c mailmap.blob=map:both shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.file overrides mailmap.blob' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Internal Guy (1): + initial + + EOF + git \ + -c mailmap.blob=map:both \ + -c mailmap.file=internal.map \ + shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob can be missing' ' + cat >expect <<-\EOF && + Repo Guy (1): + initial + + nick1 (1): + second + + EOF + git -c mailmap.blob=map:nonexistent shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob defaults to off in non-bare repo' ' + git init non-bare && + ( + cd non-bare && + test_commit one .mailmap "Fake Name <author@example.com>" && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual && + rm .mailmap && + echo " 1 A U Thor" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' ' + git clone --bare non-bare bare && + ( + cd bare && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'cleanup after mailmap.blob tests' ' + rm -f .mailmap +' + # Extended mailmap configurations should give us the following output for shortlog cat >expect <<\EOF A U Thor <author@example.com> (1): @@ -239,6 +337,62 @@ test_expect_success 'Log output (complex mapping)' ' test_cmp expect actual ' +cat >expect <<\EOF +Author: CTO <cto@company.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Other Author <other@author.xx> +Author: Other Author <other@author.xx> +Author: Some Dude <some@dude.xx> +Author: A U Thor <author@example.com> +EOF + +test_expect_success 'Log output with --use-mailmap' ' + git log --use-mailmap | grep Author >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +Author: CTO <cto@company.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Other Author <other@author.xx> +Author: Other Author <other@author.xx> +Author: Some Dude <some@dude.xx> +Author: A U Thor <author@example.com> +EOF + +test_expect_success 'Log output with log.mailmap' ' + git -c log.mailmap=True log | grep Author >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +EOF + +test_expect_success 'Grep author with --use-mailmap' ' + git log --use-mailmap --author Santa | grep Author >actual && + test_cmp expect actual +' +cat >expect <<\EOF +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +EOF + +test_expect_success 'Grep author with log.mailmap' ' + git -c log.mailmap=True log --author Santa | grep Author >actual && + test_cmp expect actual +' + +>expect + +test_expect_success 'Only grep replaced author with --use-mailmap' ' + git log --use-mailmap --author "<cto@coompany.xx>" >actual && + test_cmp expect actual +' + # git blame cat >expect <<\EOF ^OBJI (A U Thor DATE 1) one diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh index 46c3fe76d3..d0b2a457b8 100755 --- a/t/t4300-merge-tree.sh +++ b/t/t4300-merge-tree.sh @@ -254,4 +254,48 @@ EXPECTED test_cmp expected actual ' +test_expect_success 'turn file to tree' ' + git reset --hard initial && + rm initial-file && + mkdir initial-file && + test_commit "turn-file-to-tree" "initial-file/ONE" "CCC" && + git merge-tree initial initial turn-file-to-tree >actual && + cat >expect <<-\EOF && + added in remote + their 100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 initial-file/ONE + @@ -0,0 +1 @@ + +CCC + removed in remote + base 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file + our 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file + @@ -1 +0,0 @@ + -initial + EOF + test_cmp expect actual +' + +test_expect_success 'turn tree to file' ' + git reset --hard initial && + mkdir dir && + test_commit "add-tree" "dir/path" "AAA" && + test_commit "add-another-tree" "dir/another" "BBB" && + rm -fr dir && + test_commit "make-file" "dir" "CCC" && + git merge-tree add-tree add-another-tree make-file >actual && + cat >expect <<-\EOF && + added in local + our 100644 ba629238ca89489f2b350e196ca445e09d8bb834 dir/another + removed in remote + base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path + our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path + @@ -1 +0,0 @@ + -AAA + added in remote + their 100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 dir + @@ -0,0 +1 @@ + +CCC + EOF + test_cmp expect actual +' + test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index ecf00edab2..e7c240fc1f 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -25,32 +25,11 @@ commit id embedding: ' . ./test-lib.sh -UNZIP=${UNZIP:-unzip} GZIP=${GZIP:-gzip} GUNZIP=${GUNZIP:-gzip -d} SUBSTFORMAT=%H%n -check_zip() { - zipfile=$1.zip - listfile=$1.lst - dir=$1 - dir_with_prefix=$dir/$2 - - test_expect_success UNZIP " extract ZIP archive" " - (mkdir $dir && cd $dir && $UNZIP ../$zipfile) - " - - test_expect_success UNZIP " validate filenames" " - (cd ${dir_with_prefix}a && find .) | sort >$listfile && - test_cmp a.lst $listfile - " - - test_expect_success UNZIP " validate file contents" " - diff -r a ${dir_with_prefix}a - " -} - test_expect_success \ 'populate workdir' \ 'mkdir a b c && @@ -201,62 +180,12 @@ test_expect_success \ test_cmp a/substfile2 g/prefix/a/substfile2 ' -$UNZIP -v >/dev/null 2>&1 -if [ $? -eq 127 ]; then - say "Skipping ZIP tests, because unzip was not found" -else - test_set_prereq UNZIP -fi - -test_expect_success \ - 'git archive --format=zip' \ - 'git archive --format=zip HEAD >d.zip' - -check_zip d - -test_expect_success \ - 'git archive --format=zip in a bare repo' \ - '(cd bare.git && git archive --format=zip HEAD) >d1.zip' - -test_expect_success \ - 'git archive --format=zip vs. the same in a bare repo' \ - 'test_cmp d.zip d1.zip' - -test_expect_success 'git archive --format=zip with --output' \ - 'git archive --format=zip --output=d2.zip HEAD && - test_cmp d.zip d2.zip' - -test_expect_success 'git archive with --output, inferring format' ' - git archive --output=d3.zip HEAD && - test_cmp d.zip d3.zip -' - test_expect_success 'git archive with --output, override inferred format' ' git archive --format=tar --output=d4.zip HEAD && test_cmp b.tar d4.zip ' test_expect_success \ - 'git archive --format=zip with prefix' \ - 'git archive --format=zip --prefix=prefix/ HEAD >e.zip' - -check_zip e prefix/ - -test_expect_success 'git archive -0 --format=zip on large files' ' - test_config core.bigfilethreshold 1 && - git archive -0 --format=zip HEAD >large.zip -' - -check_zip large - -test_expect_success 'git archive --format=zip on large files' ' - test_config core.bigfilethreshold 1 && - git archive --format=zip HEAD >large-compressed.zip -' - -check_zip large-compressed - -test_expect_success \ 'git archive --list outside of a git repo' \ 'GIT_DIR=some/non-existing/directory git archive --list' diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh new file mode 100755 index 0000000000..7cfe9ca3da --- /dev/null +++ b/t/t5003-archive-zip.sh @@ -0,0 +1,131 @@ +#!/bin/sh + +test_description='git archive --format=zip test' + +. ./test-lib.sh +GIT_UNZIP=${GIT_UNZIP:-unzip} + +SUBSTFORMAT=%H%n + +test_lazy_prereq UNZIP ' + "$GIT_UNZIP" -v + test $? -ne 127 +' + +test_lazy_prereq UNZIP_SYMLINKS ' + ( + mkdir unzip-symlinks && + cd unzip-symlinks && + "$GIT_UNZIP" "$TEST_DIRECTORY"/t5003/infozip-symlinks.zip && + test -h symlink + ) +' + +check_zip() { + zipfile=$1.zip + listfile=$1.lst + dir=$1 + dir_with_prefix=$dir/$2 + + test_expect_success UNZIP " extract ZIP archive" ' + (mkdir $dir && cd $dir && "$GIT_UNZIP" ../$zipfile) + ' + + test_expect_success UNZIP " validate filenames" " + (cd ${dir_with_prefix}a && find .) | sort >$listfile && + test_cmp a.lst $listfile + " + + test_expect_success UNZIP " validate file contents" " + diff -r a ${dir_with_prefix}a + " +} + +test_expect_success \ + 'populate workdir' \ + 'mkdir a b c && + echo simple textfile >a/a && + mkdir a/bin && + cp /bin/sh a/bin && + printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 && + printf "A not substituted O" >a/substfile2 && + (p=long_path_to_a_file && cd a && + for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && + echo text >file_with_long_path) +' + +test_expect_success SYMLINKS,UNZIP_SYMLINKS 'add symlink' ' + ln -s a a/symlink_to_a +' + +test_expect_success 'prepare file list' ' + (cd a && find .) | sort >a.lst +' + +test_expect_success \ + 'add ignored file' \ + 'echo ignore me >a/ignored && + echo ignored export-ignore >.git/info/attributes' + +test_expect_success \ + 'add files to repository' \ + 'find a -type f | xargs git update-index --add && + find a -type l | xargs git update-index --add && + treeid=`git write-tree` && + echo $treeid >treeid && + git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ + git commit-tree $treeid </dev/null)' + +test_expect_success \ + 'create bare clone' \ + 'git clone --bare . bare.git && + cp .git/info/attributes bare.git/info/attributes' + +test_expect_success \ + 'remove ignored file' \ + 'rm a/ignored' + +test_expect_success \ + 'git archive --format=zip' \ + 'git archive --format=zip HEAD >d.zip' + +check_zip d + +test_expect_success \ + 'git archive --format=zip in a bare repo' \ + '(cd bare.git && git archive --format=zip HEAD) >d1.zip' + +test_expect_success \ + 'git archive --format=zip vs. the same in a bare repo' \ + 'test_cmp d.zip d1.zip' + +test_expect_success 'git archive --format=zip with --output' \ + 'git archive --format=zip --output=d2.zip HEAD && + test_cmp d.zip d2.zip' + +test_expect_success 'git archive with --output, inferring format' ' + git archive --output=d3.zip HEAD && + test_cmp d.zip d3.zip +' + +test_expect_success \ + 'git archive --format=zip with prefix' \ + 'git archive --format=zip --prefix=prefix/ HEAD >e.zip' + +check_zip e prefix/ + +test_expect_success 'git archive -0 --format=zip on large files' ' + test_config core.bigfilethreshold 1 && + git archive -0 --format=zip HEAD >large.zip +' + +check_zip large + +test_expect_success 'git archive --format=zip on large files' ' + test_config core.bigfilethreshold 1 && + git archive --format=zip HEAD >large-compressed.zip +' + +check_zip large-compressed + +test_done diff --git a/t/t5003/infozip-symlinks.zip b/t/t5003/infozip-symlinks.zip Binary files differnew file mode 100644 index 0000000000..065728c631 --- /dev/null +++ b/t/t5003/infozip-symlinks.zip diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index b5417cc951..60093728fe 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -368,7 +368,7 @@ test_expect_success 'push with colon-less refspec (2)' ' git branch -D frotz fi && git tag -f frotz && - git push testrepo frotz && + git push -f testrepo frotz && check_push_result $the_commit tags/frotz && check_push_result $the_first_commit heads/frotz @@ -929,6 +929,48 @@ test_expect_success 'push into aliased refs (inconsistent)' ' ) ' +test_expect_success 'push requires --force to update lightweight tag' ' + mk_test heads/master && + mk_child child1 && + mk_child child2 && + ( + cd child1 && + git tag Tag && + git push ../child2 Tag && + git push ../child2 Tag && + >file1 && + git add file1 && + git commit -m "file1" && + git tag -f Tag && + test_must_fail git push ../child2 Tag && + git push --force ../child2 Tag && + git tag -f Tag && + test_must_fail git push ../child2 Tag HEAD~ && + git push --force ../child2 Tag + ) +' + +test_expect_success 'push requires --force to update annotated tag' ' + mk_test heads/master && + mk_child child1 && + mk_child child2 && + ( + cd child1 && + git tag -a -m "message 1" Tag && + git push ../child2 Tag:refs/tmp/Tag && + git push ../child2 Tag:refs/tmp/Tag && + >file1 && + git add file1 && + git commit -m "file1" && + git tag -f -a -m "message 2" Tag && + test_must_fail git push ../child2 Tag:refs/tmp/Tag && + git push --force ../child2 Tag:refs/tmp/Tag && + git tag -f -a -m "message 3" Tag HEAD~ && + test_must_fail git push ../child2 Tag:refs/tmp/Tag && + git push --force ../child2 Tag:refs/tmp/Tag + ) +' + test_expect_success 'push --porcelain' ' mk_empty && echo >.git/foo "To testrepo" && diff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-testpy.sh index e7dc668cef..1e683d4220 100755 --- a/t/t5800-remote-helpers.sh +++ b/t/t5800-remote-testpy.sh @@ -3,12 +3,12 @@ # Copyright (c) 2010 Sverre Rabbelier # -test_description='Test remote-helper import and export commands' +test_description='Test python remote-helper framework' . ./test-lib.sh if ! test_have_prereq PYTHON ; then - skip_all='skipping git-remote-hg tests, python not available' + skip_all='skipping python remote-helper tests, python not available' test_done fi @@ -17,7 +17,7 @@ import sys if sys.hexversion < 0x02040000: sys.exit(1) ' || { - skip_all='skipping git-remote-hg tests, python version < 2.4' + skip_all='skipping python remote-helper tests, python version < 2.4' test_done } @@ -38,12 +38,12 @@ test_expect_success 'setup repository' ' ' test_expect_success 'cloning from local repo' ' - git clone "testgit::${PWD}/server" localclone && + git clone "testpy::${PWD}/server" localclone && test_cmp public/file localclone/file ' test_expect_success 'cloning from remote repo' ' - git clone "testgit::file://${PWD}/server" clone && + git clone "testpy::file://${PWD}/server" clone && test_cmp public/file clone/file ' @@ -73,11 +73,11 @@ test_expect_success 'pushing to local repo' ' ' # Generally, skip this test. It demonstrates a now-fixed race in -# git-remote-testgit, but is too slow to leave in for general use. +# git-remote-testpy, but is too slow to leave in for general use. : test_expect_success 'racily pushing to local repo' ' test_when_finished "rm -rf server2 localclone2" && cp -R server server2 && - git clone "testgit::${PWD}/server2" localclone2 && + git clone "testpy::${PWD}/server2" localclone2 && (cd localclone2 && echo content >>file && git commit -a -m three && @@ -145,4 +145,25 @@ test_expect_failure 'push new branch with old:new refspec' ' compare_refs clone HEAD server refs/heads/new-refspec ' +test_expect_success 'proper failure checks for fetching' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 && + export GIT_REMOTE_TESTGIT_FAILURE && + cd localclone && + test_must_fail git fetch 2>&1 | \ + grep "Error while running fast-import" + ) +' + +# We sleep to give fast-export a chance to catch the SIGPIPE +test_expect_failure 'proper failure checks for pushing' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 && + export GIT_REMOTE_TESTGIT_FAILURE && + GIT_REMOTE_TESTGIT_SLEEPY=1 && + export GIT_REMOTE_TESTGIT_SLEEPY && + cd localclone && + test_must_fail git push --all 2>&1 | \ + grep "Error while running fast-export" + ) +' + test_done diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh new file mode 100755 index 0000000000..f387027c05 --- /dev/null +++ b/t/t5801-remote-helpers.sh @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2010 Sverre Rabbelier +# + +test_description='Test remote-helper import and export commands' + +. ./test-lib.sh + +if ! type "${BASH-bash}" >/dev/null 2>&1; then + skip_all='skipping remote-testgit tests, bash not available' + test_done +fi + +compare_refs() { + git --git-dir="$1/.git" rev-parse --verify $2 >expect && + git --git-dir="$3/.git" rev-parse --verify $4 >actual && + test_cmp expect actual +} + +test_expect_success 'setup repository' ' + git init server && + (cd server && + echo content >file && + git add file && + git commit -m one) +' + +test_expect_success 'cloning from local repo' ' + git clone "testgit::${PWD}/server" local && + test_cmp server/file local/file +' + +test_expect_success 'create new commit on remote' ' + (cd server && + echo content >>file && + git commit -a -m two) +' + +test_expect_success 'pulling from local repo' ' + (cd local && git pull) && + test_cmp server/file local/file +' + +test_expect_success 'pushing to local repo' ' + (cd local && + echo content >>file && + git commit -a -m three && + git push) && + compare_refs local HEAD server HEAD +' + +test_expect_success 'fetch new branch' ' + (cd server && + git reset --hard && + git checkout -b new && + echo content >>file && + git commit -a -m five + ) && + (cd local && + git fetch origin new + ) && + compare_refs server HEAD local FETCH_HEAD +' + +test_expect_success 'fetch multiple branches' ' + (cd local && + git fetch + ) && + compare_refs server master local refs/remotes/origin/master && + compare_refs server new local refs/remotes/origin/new +' + +test_expect_success 'push when remote has extra refs' ' + (cd local && + git reset --hard origin/master && + echo content >>file && + git commit -a -m six && + git push + ) && + compare_refs local master server master +' + +test_expect_success 'push new branch by name' ' + (cd local && + git checkout -b new-name && + echo content >>file && + git commit -a -m seven && + git push origin new-name + ) && + compare_refs local HEAD server refs/heads/new-name +' + +test_expect_failure 'push new branch with old:new refspec' ' + (cd local && + git push origin new-name:new-refspec + ) && + compare_refs local HEAD server refs/heads/new-refspec +' + +test_expect_success 'cloning without refspec' ' + GIT_REMOTE_TESTGIT_REFSPEC="" \ + git clone "testgit::${PWD}/server" local2 && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling without refspecs' ' + (cd local2 && + git reset --hard && + GIT_REMOTE_TESTGIT_REFSPEC="" git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing without refspecs' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m ten && + GIT_REMOTE_TESTGIT_REFSPEC="" git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling with straight refspec' ' + (cd local2 && + GIT_REMOTE_TESTGIT_REFSPEC="*:*" git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing with straight refspec' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m eleven && + GIT_REMOTE_TESTGIT_REFSPEC="*:*" git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling without marks' ' + (cd local2 && + GIT_REMOTE_TESTGIT_NO_MARKS=1 git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing without marks' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m twelve && + GIT_REMOTE_TESTGIT_NO_MARKS=1 git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'push all with existing object' ' + (cd local && + git branch dup2 master && + git push origin --all + ) && + compare_refs local dup2 server dup2 +' + +test_expect_success 'push ref with existing object' ' + (cd local && + git branch dup master && + git push origin dup + ) && + compare_refs local dup server dup +' + +test_done diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index f94f0c48e6..3fc3b74c8e 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -3,6 +3,7 @@ test_description='git rev-list --pretty=format test' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-terminal.sh test_tick test_expect_success 'setup' ' @@ -11,12 +12,24 @@ touch foo && git add foo && git commit -m "added foo" && ' # usage: test_format name format_string <expected_output -test_format() { +test_format () { cat >expect.$1 test_expect_success "format $1" " -git rev-list --pretty=format:'$2' master >output.$1 && -test_cmp expect.$1 output.$1 -" + git rev-list --pretty=format:'$2' master >output.$1 && + test_cmp expect.$1 output.$1 + " +} + +# Feed to --format to provide predictable colored sequences. +AUTO_COLOR='%C(auto,red)foo%C(auto,reset)' +has_color () { + printf '\033[31mfoo\033[m\n' >expect && + test_cmp expect "$1" +} + +has_no_color () { + echo foo >expect && + test_cmp expect "$1" } test_format percent %%h <<'EOF' @@ -124,6 +137,48 @@ commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 [1;31;43mfoo[m EOF +test_expect_success '%C(auto) does not enable color by default' ' + git log --format=$AUTO_COLOR -1 >actual && + has_no_color actual +' + +test_expect_success '%C(auto) enables colors for color.diff' ' + git -c color.diff=always log --format=$AUTO_COLOR -1 >actual && + has_color actual +' + +test_expect_success '%C(auto) enables colors for color.ui' ' + git -c color.ui=always log --format=$AUTO_COLOR -1 >actual && + has_color actual +' + +test_expect_success '%C(auto) respects --color' ' + git log --format=$AUTO_COLOR -1 --color >actual && + has_color actual +' + +test_expect_success '%C(auto) respects --no-color' ' + git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual && + has_no_color actual +' + +test_expect_success TTY '%C(auto) respects --color=auto (stdout is tty)' ' + ( + TERM=vt100 && export TERM && + test_terminal \ + git log --format=$AUTO_COLOR -1 --color=auto >actual && + has_color actual + ) +' + +test_expect_success '%C(auto) respects --color=auto (stdout not tty)' ' + ( + TERM=vt100 && export TERM && + git log --format=$AUTO_COLOR -1 --color=auto >actual && + has_no_color actual + ) +' + cat >commit-msg <<'EOF' Test printing of complex bodies diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 72e28ee535..3e0e15fb3e 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -676,9 +676,7 @@ test_expect_success 'bisect fails if tree is broken on trial commit' ' check_same() { echo "Checking $1 is the same as $2" && - git rev-parse "$1" > expected.same && - git rev-parse "$2" > expected.actual && - test_cmp expected.same expected.actual + test_cmp_rev "$1" "$2" } test_expect_success 'bisect: --no-checkout - start commit bad' ' diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh new file mode 100755 index 0000000000..39ef61994f --- /dev/null +++ b/t/t6130-pathspec-noglob.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +test_description='test globbing (and noglob) of pathspec limiting' +. ./test-lib.sh + +test_expect_success 'create commits with glob characters' ' + test_commit unrelated bar && + test_commit vanilla foo && + # insert file "f*" in the commit, but in a way that avoids + # the name "f*" in the worktree, because it is not allowed + # on Windows (the tests below do not depend on the presence + # of the file in the worktree) + git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:foo)" "f*" && + test_tick && + git commit -m star && + test_commit bracket "f[o][o]" +' + +test_expect_success 'vanilla pathspec matches literally' ' + echo vanilla >expect && + git log --format=%s -- foo >actual && + test_cmp expect actual +' + +test_expect_success 'star pathspec globs' ' + cat >expect <<-\EOF && + bracket + star + vanilla + EOF + git log --format=%s -- "f*" >actual && + test_cmp expect actual +' + +test_expect_success 'bracket pathspec globs and matches literal brackets' ' + cat >expect <<-\EOF && + bracket + vanilla + EOF + git log --format=%s -- "f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (vanilla)' ' + echo vanilla >expect && + git --literal-pathspecs log --format=%s -- foo >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (star)' ' + echo star >expect && + git --literal-pathspecs log --format=%s -- "f*" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (bracket)' ' + echo bracket >expect && + git --literal-pathspecs log --format=%s -- "f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob environment variable works' ' + echo star >expect && + GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh new file mode 100755 index 0000000000..0da1214bcc --- /dev/null +++ b/t/t7061-wtstatus-ignore.sh @@ -0,0 +1,146 @@ +#!/bin/sh + +test_description='git-status ignored files' + +. ./test-lib.sh + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +?? untracked/ +EOF + +test_expect_success 'status untracked directory with --ignored' ' + echo "ignored" >.gitignore && + mkdir untracked && + : >untracked/ignored && + : >untracked/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +?? untracked/uncommitted +!! untracked/ignored +EOF + +test_expect_success 'status untracked directory with --ignored -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! ignored/ +EOF + +test_expect_success 'status ignored directory with --ignore' ' + rm -rf untracked && + mkdir ignored && + : >ignored/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! ignored/uncommitted +EOF + +test_expect_success 'status ignored directory with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! untracked-ignored/ +EOF + +test_expect_success 'status untracked directory with ignored files with --ignore' ' + rm -rf ignored && + mkdir untracked-ignored && + mkdir untracked-ignored/test && + : >untracked-ignored/ignored && + : >untracked-ignored/test/ignored && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! untracked-ignored/ignored +!! untracked-ignored/test/ignored +EOF + +test_expect_success 'status untracked directory with ignored files with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status ignored tracked directory with --ignore' ' + rm -rf untracked-ignored && + mkdir tracked && + : >tracked/committed && + git add tracked/committed && + git commit -m. && + echo "tracked" >.gitignore && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status ignored tracked directory with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/ +EOF + +test_expect_success 'status ignored tracked directory and uncommitted file with --ignore' ' + : >tracked/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/uncommitted +EOF + +test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index de7d45352e..2683cba7e3 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -133,6 +133,7 @@ test_expect_success 'submodule add --branch' ' ( cd addtest && git submodule add -b initial "$submodurl" submod-branch && + test "initial" = "$(git config -f .gitmodules submodule.submod-branch.branch)" && git submodule init ) && diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index feaec6cdf4..4975ec07ce 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -135,6 +135,37 @@ test_expect_success 'submodule update --force forcibly checks out submodules' ' ) ' +test_expect_success 'submodule update --remote should fetch upstream changes' ' + (cd submodule && + echo line4 >> file && + git add file && + test_tick && + git commit -m "upstream line4" + ) && + (cd super && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline)" + ) +' + +test_expect_success 'local config should override .gitmodules branch' ' + (cd submodule && + git checkout -b test-branch && + echo line5 >> file && + git add file && + test_tick && + git commit -m "upstream line5" && + git checkout master + ) && + (cd super && + git config submodule.submodule.branch test-branch && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline test-branch)" + ) +' + test_expect_success 'submodule update --rebase staying on master' ' (cd super/submodule && git checkout master diff --git a/t/t7500/add-content-and-comment b/t/t7500/add-content-and-comment new file mode 100755 index 0000000000..c4dccff13a --- /dev/null +++ b/t/t7500/add-content-and-comment @@ -0,0 +1,5 @@ +#!/bin/sh +echo "commit message" >> "$1" +echo "# comment" >> "$1" +exit 0 + diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 1a5cb6983c..b1c7648386 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -4,6 +4,15 @@ test_description='git commit porcelain-ish' . ./test-lib.sh +commit_msg_is () { + expect=commit_msg_is.expect + actual=commit_msg_is.actual + + printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual && + printf "%s" "$1" >$expect && + test_i18ncmp $expect $actual +} + # Arguments: [<prefix] [<commit message>] [<commit options>] check_summary_oneline() { test_tick && @@ -168,7 +177,7 @@ test_expect_success 'verbose respects diff config' ' git config --unset color.diff ' -test_expect_success 'cleanup commit messages (verbatim,-t)' ' +test_expect_success 'cleanup commit messages (verbatim option,-t)' ' echo >>negative && { echo;echo "# text";echo; } >expect && @@ -178,7 +187,7 @@ test_expect_success 'cleanup commit messages (verbatim,-t)' ' ' -test_expect_success 'cleanup commit messages (verbatim,-F)' ' +test_expect_success 'cleanup commit messages (verbatim option,-F)' ' echo >>negative && git commit --cleanup=verbatim -F expect -a && @@ -187,7 +196,7 @@ test_expect_success 'cleanup commit messages (verbatim,-F)' ' ' -test_expect_success 'cleanup commit messages (verbatim,-m)' ' +test_expect_success 'cleanup commit messages (verbatim option,-m)' ' echo >>negative && git commit --cleanup=verbatim -m "$(cat expect)" -a && @@ -196,7 +205,7 @@ test_expect_success 'cleanup commit messages (verbatim,-m)' ' ' -test_expect_success 'cleanup commit messages (whitespace,-F)' ' +test_expect_success 'cleanup commit messages (whitespace option,-F)' ' echo >>negative && { echo;echo "# text";echo; } >text && @@ -207,7 +216,7 @@ test_expect_success 'cleanup commit messages (whitespace,-F)' ' ' -test_expect_success 'cleanup commit messages (strip,-F)' ' +test_expect_success 'cleanup commit messages (strip option,-F)' ' echo >>negative && { echo;echo "# text";echo sample;echo; } >text && @@ -218,7 +227,7 @@ test_expect_success 'cleanup commit messages (strip,-F)' ' ' -test_expect_success 'cleanup commit messages (strip,-F,-e)' ' +test_expect_success 'cleanup commit messages (strip option,-F,-e)' ' echo >>negative && { echo;echo sample;echo; } >text && @@ -231,10 +240,71 @@ echo "sample # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit." >expect -test_expect_success 'cleanup commit messages (strip,-F,-e): output' ' +test_expect_success 'cleanup commit messages (strip option,-F,-e): output' ' test_i18ncmp expect actual ' +test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' ' + test_must_fail git commit --cleanup=non-existent +' + +test_expect_success 'cleanup commit message (fail on invalid cleanup mode configuration)' ' + test_must_fail git -c commit.cleanup=non-existent commit +' + +test_expect_success 'cleanup commit message (no config and no option uses default)' ' + echo content >>file && + git add file && + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git commit --no-status && + commit_msg_is "commit message" +' + +test_expect_success 'cleanup commit message (option overrides default)' ' + echo content >>file && + git add file && + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git commit --cleanup=whitespace --no-status && + commit_msg_is "commit message # comment" +' + +test_expect_success 'cleanup commit message (config overrides default)' ' + echo content >>file && + git add file && + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git -c commit.cleanup=whitespace commit --no-status && + commit_msg_is "commit message # comment" +' + +test_expect_success 'cleanup commit message (option overrides config)' ' + echo content >>file && + git add file && + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git -c commit.cleanup=whitespace commit --cleanup=default && + commit_msg_is "commit message" +' + +test_expect_success 'cleanup commit message (default, -m)' ' + echo content >>file && + git add file && + git commit -m "message #comment " && + commit_msg_is "message #comment" +' + +test_expect_success 'cleanup commit message (whitespace option, -m)' ' + echo content >>file && + git add file && + git commit --cleanup=whitespace --no-status -m "message #comment " && + commit_msg_is "message #comment" +' + +test_expect_success 'cleanup commit message (whitespace config, -m)' ' + echo content >>file && + git add file && + git -c commit.cleanup=whitespace commit --no-status -m "message #comment " && + commit_msg_is "message #comment" +' + test_expect_success 'message shows author when it is not equal to committer' ' echo >>negative && git commit -e -m "sample" -a && diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 3e821f958b..9320b4f94c 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -303,7 +303,7 @@ test_expect_success 'dropping tag of filtered out object' ' ( cd limit-by-paths && git fast-export --tag-of-filtered-object=drop mytag -- there > output && - test_cmp output expected + test_cmp expected output ) ' @@ -320,7 +320,7 @@ test_expect_success 'rewriting tag of filtered out object' ' ( cd limit-by-paths && git fast-export --tag-of-filtered-object=rewrite mytag -- there > output && - test_cmp output expected + test_cmp expected output ) ' @@ -351,7 +351,7 @@ test_expect_failure 'no exact-ref revisions included' ' ( cd limit-by-paths && git fast-export master~2..master~1 > output && - test_cmp output expected + test_cmp expected output ) ' @@ -440,4 +440,63 @@ test_expect_success 'fast-export quotes pathnames' ' ) ' +test_expect_success 'test bidirectionality' ' + >marks-cur && + >marks-new && + git init marks-test && + git fast-export --export-marks=marks-cur --import-marks=marks-cur --branches | \ + git --git-dir=marks-test/.git fast-import --export-marks=marks-new --import-marks=marks-new && + (cd marks-test && + git reset --hard && + echo Wohlauf > file && + git commit -a -m "back in time") && + git --git-dir=marks-test/.git fast-export --export-marks=marks-new --import-marks=marks-new --branches | \ + git fast-import --export-marks=marks-cur --import-marks=marks-cur +' + +cat > expected << EOF +blob +mark :13 +data 5 +bump + +commit refs/heads/master +mark :14 +author A U Thor <author@example.com> 1112912773 -0700 +committer C O Mitter <committer@example.com> 1112912773 -0700 +data 5 +bump +from :12 +M 100644 :13 file + +EOF + +test_expect_success 'avoid uninteresting refs' ' + > tmp-marks && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > /dev/null && + git tag v1.0 && + git branch uninteresting && + echo bump > file && + git commit -a -m bump && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks ^uninteresting ^v1.0 master > actual && + test_cmp expected actual +' + +cat > expected << EOF +reset refs/heads/master +from :14 + +EOF + +test_expect_success 'refs are updated even if no commits need to be exported' ' + > tmp-marks && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > /dev/null && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > actual && + test_cmp expected actual +' + test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 22a4f8fb64..fa62d010f6 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -602,6 +602,13 @@ test_cmp() { $GIT_TEST_CMP "$@" } +# Tests that its two parameters refer to the same revision +test_cmp_rev () { + git rev-parse --verify "$1" >expect.rev && + git rev-parse --verify "$2" >actual.rev && + test_cmp expect.rev actual.rev +} + # Print a sequence of numbers or letters in increasing order. This is # similar to GNU seq(1), but the latter might not be available # everywhere (and does not do letters). It may be used like: diff --git a/t/test-lib.sh b/t/test-lib.sh index f50f8341d4..1a6c4ab08c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -85,7 +85,8 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e ' .*_TEST PROVE VALGRIND - PERF_AGGREGATING_LATER + UNZIP + PERF_ )); my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env); print join("\n", @vars); @@ -128,6 +129,7 @@ fi unset CDPATH unset GREP_OPTIONS +unset UNZIP case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in 1|2|true) @@ -212,11 +214,13 @@ then error) tput bold; tput setaf 1;; # bold red skip) - tput bold; tput setaf 2;; # bold green + tput setaf 4;; # blue + warn) + tput setaf 3;; # brown/yellow pass) - tput setaf 2;; # green + tput setaf 2;; # green info) - tput setaf 3;; # brown + tput setaf 6;; # cyan *) test -n "$quiet" && return;; esac @@ -298,7 +302,7 @@ test_ok_ () { test_failure_ () { test_failure=$(($test_failure + 1)) - say_color error "not ok - $test_count $1" + say_color error "not ok $test_count - $1" shift echo "$@" | sed -e 's/^/# /' test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } @@ -306,12 +310,12 @@ test_failure_ () { test_known_broken_ok_ () { test_fixed=$(($test_fixed+1)) - say_color "" "ok $test_count - $@ # TODO known breakage" + say_color error "ok $test_count - $@ # TODO known breakage vanished" } test_known_broken_failure_ () { test_broken=$(($test_broken+1)) - say_color skip "not ok $test_count - $@ # TODO known breakage" + say_color warn "not ok $test_count - $@ # TODO known breakage" } test_debug () { @@ -404,13 +408,18 @@ test_done () { if test "$test_fixed" != 0 then - say_color pass "# fixed $test_fixed known breakage(s)" + say_color error "# $test_fixed known breakage(s) vanished; please update test(s)" fi if test "$test_broken" != 0 then - say_color error "# still have $test_broken known breakage(s)" - msg="remaining $(($test_count-$test_broken)) test(s)" + say_color warn "# still have $test_broken known breakage(s)" + fi + if test "$test_broken" != 0 || test "$test_fixed" != 0 + then + test_remaining=$(( $test_count - $test_broken - $test_fixed )) + msg="remaining $test_remaining test(s)" else + test_remaining=$test_count msg="$test_count test(s)" fi case "$test_failure" in @@ -424,7 +433,7 @@ test_done () { if test $test_external_has_tap -eq 0 then - if test $test_count -gt 0 + if test $test_remaining -gt 0 then say_color pass "# passed all $msg" fi @@ -615,7 +624,7 @@ for skp in $GIT_SKIP_TESTS do case "$this_test" in $skp) - say_color skip >&3 "skipping test $this_test altogether" + say_color info >&3 "skipping test $this_test altogether" skip_all="skip all tests in $this_test" test_done esac diff --git a/test-path-utils.c b/test-path-utils.c index 3bc20e91da..0092cbf354 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -1,4 +1,32 @@ #include "cache.h" +#include "string-list.h" + +/* + * A "string_list_each_func_t" function that normalizes an entry from + * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason, + * die with an explanation. + */ +static int normalize_ceiling_entry(struct string_list_item *item, void *unused) +{ + const char *ceil = item->string; + int len = strlen(ceil); + char buf[PATH_MAX+1]; + + if (len == 0) + die("Empty path is not supported"); + if (len > PATH_MAX) + die("Path \"%s\" is too long", ceil); + if (!is_absolute_path(ceil)) + die("Path \"%s\" is not absolute", ceil); + if (normalize_path_copy(buf, ceil) < 0) + die("Path \"%s\" could not be normalized", ceil); + len = strlen(buf); + if (len > 1 && buf[len-1] == '/') + die("Normalized path \"%s\" ended with slash", buf); + free(item->string); + item->string = xstrdup(buf); + return 1; +} int main(int argc, char **argv) { @@ -30,7 +58,28 @@ int main(int argc, char **argv) } if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { - int len = longest_ancestor_length(argv[2], argv[3]); + int len; + struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; + char *path = xstrdup(argv[2]); + + /* + * We have to normalize the arguments because under + * Windows, bash mangles arguments that look like + * absolute POSIX paths or colon-separate lists of + * absolute POSIX paths into DOS paths (e.g., + * "/foo:/foo/bar" might be converted to + * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"), + * whereas longest_ancestor_length() requires paths + * that use forward slashes. + */ + if (normalize_path_copy(path, path)) + die("Path \"%s\" could not be normalized", argv[2]); + string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1); + filter_string_list(&ceiling_dirs, 0, + normalize_ceiling_entry, NULL); + len = longest_ancestor_length(path, &ceiling_dirs); + string_list_clear(&ceiling_dirs, 0); + free(path); printf("%d\n", len); return 0; } diff --git a/test-string-list.c b/test-string-list.c index 4693295a98..00ce6c9a12 100644 --- a/test-string-list.c +++ b/test-string-list.c @@ -97,26 +97,6 @@ int main(int argc, char **argv) return 0; } - if (argc == 4 && !strcmp(argv[1], "longest_prefix")) { - /* arguments: <colon-separated-prefixes>|- <string> */ - struct string_list prefixes = STRING_LIST_INIT_DUP; - int retval; - const char *prefix_string = argv[2]; - const char *string = argv[3]; - const char *match; - - parse_string_list(&prefixes, prefix_string); - match = string_list_longest_prefix(&prefixes, string); - if (match) { - printf("%s\n", match); - retval = 0; - } - else - retval = 1; - string_list_clear(&prefixes, 0); - return retval; - } - fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/test-wildmatch.c b/test-wildmatch.c new file mode 100644 index 0000000000..e384c8edb1 --- /dev/null +++ b/test-wildmatch.c @@ -0,0 +1,22 @@ +#include "cache.h" +#include "wildmatch.h" + +int main(int argc, char **argv) +{ + int i; + for (i = 2; i < argc; i++) { + if (argv[i][0] == '/') + die("Forward slash is not allowed at the beginning of the\n" + "pattern because Windows does not like it. Use `XXX/' instead."); + else if (!strncmp(argv[i], "XXX/", 4)) + argv[i] += 3; + } + if (!strcmp(argv[1], "wildmatch")) + return !!wildmatch(argv[3], argv[2], 0); + else if (!strcmp(argv[1], "iwildmatch")) + return !!wildmatch(argv[3], argv[2], FNM_CASEFOLD); + else if (!strcmp(argv[1], "fnmatch")) + return !!fnmatch(argv[3], argv[2], FNM_PATHNAME); + else + return 1; +} diff --git a/transport-helper.c b/transport-helper.c index 4713b69302..965b778cb3 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -661,6 +661,11 @@ static void push_update_ref_status(struct strbuf *buf, free(msg); msg = NULL; } + else if (!strcmp(msg, "already exists")) { + status = REF_STATUS_REJECT_ALREADY_EXISTS; + free(msg); + msg = NULL; + } } if (*ref) @@ -720,6 +725,7 @@ static int push_refs_with_push(struct transport *transport, /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_UPTODATE: continue; default: diff --git a/transport.c b/transport.c index 9932f402df..2673d273ff 100644 --- a/transport.c +++ b/transport.c @@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain) const char *msg; strcpy(quickref, status_abbrev(ref->old_sha1)); - if (ref->nonfastforward) { + if (ref->requires_force) { strcat(quickref, "..."); type = '+'; msg = "forced update"; @@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast-forward", porcelain); break; + case REF_STATUS_REJECT_ALREADY_EXISTS: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "already exists", porcelain); + break; case REF_STATUS_REMOTE_REJECT: print_ref_status('!', "[remote rejected]", ref, ref->deletion ? NULL : ref->peer_ref, @@ -714,7 +718,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i } void transport_print_push_status(const char *dest, struct ref *refs, - int verbose, int porcelain, int *nonfastforward) + int verbose, int porcelain, unsigned int *reject_reasons) { struct ref *ref; int n = 0; @@ -733,18 +737,19 @@ void transport_print_push_status(const char *dest, struct ref *refs, if (ref->status == REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); - *nonfastforward = 0; + *reject_reasons = 0; for (ref = refs; ref; ref = ref->next) { if (ref->status != REF_STATUS_NONE && ref->status != REF_STATUS_UPTODATE && ref->status != REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); - if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD && - *nonfastforward != NON_FF_HEAD) { + if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) { if (!strcmp(head, ref->name)) - *nonfastforward = NON_FF_HEAD; + *reject_reasons |= REJECT_NON_FF_HEAD; else - *nonfastforward = NON_FF_OTHER; + *reject_reasons |= REJECT_NON_FF_OTHER; + } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) { + *reject_reasons |= REJECT_ALREADY_EXISTS; } } } @@ -1031,9 +1036,9 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing) int transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags, - int *nonfastforward) + unsigned int *reject_reasons) { - *nonfastforward = 0; + *reject_reasons = 0; transport_verify_remote_names(refspec_nr, refspec); if (transport->push) { @@ -1099,7 +1104,7 @@ int transport_push(struct transport *transport, if (!quiet || err) transport_print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, - nonfastforward); + reject_reasons); if (flags & TRANSPORT_PUSH_SET_UPSTREAM) set_upstreams(transport, remote_refs, pretend); diff --git a/transport.h b/transport.h index 4a61c0c3f2..bfd2df5823 100644 --- a/transport.h +++ b/transport.h @@ -140,11 +140,13 @@ int transport_set_option(struct transport *transport, const char *name, void transport_set_verbosity(struct transport *transport, int verbosity, int force_progress); -#define NON_FF_HEAD 1 -#define NON_FF_OTHER 2 +#define REJECT_NON_FF_HEAD 0x01 +#define REJECT_NON_FF_OTHER 0x02 +#define REJECT_ALREADY_EXISTS 0x04 + int transport_push(struct transport *connection, int refspec_nr, const char **refspec, int flags, - int * nonfastforward); + unsigned int * reject_reasons); const struct ref *transport_get_remote_refs(struct transport *transport); @@ -170,7 +172,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v int transport_refs_pushed(struct ref *ref); void transport_print_push_status(const char *dest, struct ref *refs, - int verbose, int porcelain, int *nonfastforward); + int verbose, int porcelain, unsigned int *reject_reasons); typedef void alternate_ref_fn(const struct ref *, void *); extern void for_each_alternate_ref(alternate_ref_fn, void *); diff --git a/tree-walk.c b/tree-walk.c index 3f54c02d76..6e30ef9d04 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -573,6 +573,54 @@ static int match_dir_prefix(const char *base, } /* + * Perform matching on the leading non-wildcard part of + * pathspec. item->nowildcard_len must be greater than zero. Return + * non-zero if base is matched. + */ +static int match_wildcard_base(const struct pathspec_item *item, + const char *base, int baselen, + int *matched) +{ + const char *match = item->match; + /* the wildcard part is not considered in this function */ + int matchlen = item->nowildcard_len; + + if (baselen) { + int dirlen; + /* + * Return early if base is longer than the + * non-wildcard part but it does not match. + */ + if (baselen >= matchlen) { + *matched = matchlen; + return !strncmp(base, match, matchlen); + } + + dirlen = matchlen; + while (dirlen && match[dirlen - 1] != '/') + dirlen--; + + /* + * Return early if base is shorter than the + * non-wildcard part but it does not match. Note that + * base ends with '/' so we are sure it really matches + * directory + */ + if (strncmp(base, match, baselen)) + return 0; + *matched = baselen; + } else + *matched = 0; + /* + * we could have checked entry against the non-wildcard part + * that is not in base and does similar never_interesting + * optimization as in match_entry. For now just be happy with + * base comparison. + */ + return entry_interesting; +} + +/* * Is a tree entry interesting given the pathspec we have? * * Pre-condition: either baselen == base_offset (i.e. empty path) @@ -602,7 +650,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, const struct pathspec_item *item = ps->items+i; const char *match = item->match; const char *base_str = base->buf + base_offset; - int matchlen = item->len; + int matchlen = item->len, matched = 0; if (baselen >= matchlen) { /* If it doesn't match, move along... */ @@ -626,8 +674,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, &never_interesting)) return entry_interesting; - if (item->use_wildcard) { - if (!fnmatch(match + baselen, entry->path, 0)) + if (item->nowildcard_len < item->len) { + if (!git_fnmatch(match + baselen, entry->path, + item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + item->nowildcard_len - baselen)) return entry_interesting; /* @@ -642,17 +692,34 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, } match_wildcards: - if (!item->use_wildcard) + if (item->nowildcard_len == item->len) continue; + if (item->nowildcard_len && + !match_wildcard_base(item, base_str, baselen, &matched)) + return entry_not_interesting; + /* * Concatenate base and entry->path into one and do * fnmatch() on it. + * + * While we could avoid concatenation in certain cases + * [1], which saves a memcpy and potentially a + * realloc, it turns out not worth it. Measurement on + * linux-2.6 does not show any clear improvements, + * partly because of the nowildcard_len optimization + * in git_fnmatch(). Avoid micro-optimizations here. + * + * [1] if match_wildcard_base() says the base + * directory is already matched, we only need to match + * the rest, which is shorter so _in theory_ faster. */ strbuf_add(base, entry->path, pathlen); - if (!fnmatch(match, base->buf + base_offset, 0)) { + if (!git_fnmatch(match, base->buf + base_offset, + item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + item->nowildcard_len)) { strbuf_setlen(base, base_offset + baselen); return entry_interesting; } diff --git a/unpack-trees.c b/unpack-trees.c index 6d9636623a..0e1a196ace 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -837,7 +837,8 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, { struct cache_entry **cache_end; int dtype = DT_DIR; - int ret = excluded_from_list(prefix, prefix_len, basename, &dtype, el); + int ret = is_excluded_from_list(prefix, prefix_len, + basename, &dtype, el); prefix[prefix_len++] = '/'; @@ -856,7 +857,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, * with ret (iow, we know in advance the incl/excl * decision for the entire directory), clear flag here without * calling clear_ce_flags_1(). That function will call - * the expensive excluded_from_list() on every entry. + * the expensive is_excluded_from_list() on every entry. */ return clear_ce_flags_1(cache, cache_end - cache, prefix, prefix_len, @@ -939,7 +940,8 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr, /* Non-directory */ dtype = ce_to_dtype(ce); - ret = excluded_from_list(ce->name, ce_namelen(ce), name, &dtype, el); + ret = is_excluded_from_list(ce->name, ce_namelen(ce), + name, &dtype, el); if (ret < 0) ret = defval; if (ret > 0) @@ -1152,7 +1154,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o->dst_index = o->result; done: - free_excludes(&el); + clear_exclude_list(&el); if (o->path_exclude_check) { path_exclude_check_clear(o->path_exclude_check); free(o->path_exclude_check); @@ -1373,7 +1375,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype, return 0; if (o->dir && - path_excluded(o->path_exclude_check, name, -1, &dtype)) + is_path_excluded(o->path_exclude_check, name, -1, &dtype)) /* * ce->name is explicitly excluded, so it is Ok to * overwrite it. @@ -1834,7 +1836,7 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o) if (old && same(old, a)) { int update = 0; - if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) { + if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) { struct stat st; if (lstat(old->name, &st) || ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE)) diff --git a/upload-pack.c b/upload-pack.c index 6142421ea1..95d83135ae 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -603,6 +603,8 @@ static void receive_needs(void) object = parse_object(sha1); if (!object) die("did not find object for %s", line); + if (object->type != OBJ_COMMIT) + die("invalid shallow object %s", sha1_to_hex(sha1)); object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); continue; @@ -130,6 +130,7 @@ void NORETURN die_errno(const char *fmt, ...) va_end(params); } +#undef error int error(const char *err, ...) { va_list params; diff --git a/wildmatch.c b/wildmatch.c new file mode 100644 index 0000000000..2d3ed84364 --- /dev/null +++ b/wildmatch.c @@ -0,0 +1,235 @@ +/* +** Do shell-style pattern matching for ?, \, [], and * characters. +** It is 8bit clean. +** +** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +** Rich $alz is now <rsalz@bbn.com>. +** +** Modified by Wayne Davison to special-case '/' matching, to make '**' +** work differently than '*', and to fix the character-class code. +*/ + +#include "cache.h" +#include "wildmatch.h" + +typedef unsigned char uchar; + +/* What character marks an inverted character class? */ +#define NEGATE_CLASS '!' +#define NEGATE_CLASS2 '^' + +#define FALSE 0 +#define TRUE 1 + +#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \ + && *(class) == *(litmatch) \ + && strncmp((char*)class, litmatch, len) == 0) + +#if defined STDC_HEADERS || !defined isascii +# define ISASCII(c) 1 +#else +# define ISASCII(c) isascii(c) +#endif + +#ifdef isblank +# define ISBLANK(c) (ISASCII(c) && isblank(c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif + +#ifdef isgraph +# define ISGRAPH(c) (ISASCII(c) && isgraph(c)) +#else +# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c)) +#endif + +#define ISPRINT(c) (ISASCII(c) && isprint(c)) +#define ISDIGIT(c) (ISASCII(c) && isdigit(c)) +#define ISALNUM(c) (ISASCII(c) && isalnum(c)) +#define ISALPHA(c) (ISASCII(c) && isalpha(c)) +#define ISCNTRL(c) (ISASCII(c) && iscntrl(c)) +#define ISLOWER(c) (ISASCII(c) && islower(c)) +#define ISPUNCT(c) (ISASCII(c) && ispunct(c)) +#define ISSPACE(c) (ISASCII(c) && isspace(c)) +#define ISUPPER(c) (ISASCII(c) && isupper(c)) +#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) + +/* Match pattern "p" against "text" */ +static int dowild(const uchar *p, const uchar *text, int force_lower_case) +{ + uchar p_ch; + const uchar *pattern = p; + + for ( ; (p_ch = *p) != '\0'; text++, p++) { + int matched, match_slash, negated; + uchar t_ch, prev_ch; + if ((t_ch = *text) == '\0' && p_ch != '*') + return ABORT_ALL; + if (force_lower_case && ISUPPER(t_ch)) + t_ch = tolower(t_ch); + if (force_lower_case && ISUPPER(p_ch)) + p_ch = tolower(p_ch); + switch (p_ch) { + case '\\': + /* Literal match with following character. Note that the test + * in "default" handles the p[1] == '\0' failure case. */ + p_ch = *++p; + /* FALLTHROUGH */ + default: + if (t_ch != p_ch) + return NOMATCH; + continue; + case '?': + /* Match anything but '/'. */ + if (t_ch == '/') + return NOMATCH; + continue; + case '*': + if (*++p == '*') { + const uchar *prev_p = p - 2; + while (*++p == '*') {} + if ((prev_p < pattern || *prev_p == '/') && + (*p == '\0' || *p == '/' || + (p[0] == '\\' && p[1] == '/'))) { + /* + * Assuming we already match 'foo/' and are at + * <star star slash>, just assume it matches + * nothing and go ahead match the rest of the + * pattern with the remaining string. This + * helps make foo/<*><*>/bar (<> because + * otherwise it breaks C comment syntax) match + * both foo/bar and foo/a/bar. + */ + if (p[0] == '/' && + dowild(p + 1, text, force_lower_case) == MATCH) + return MATCH; + match_slash = TRUE; + } else + return ABORT_MALFORMED; + } else + match_slash = FALSE; + if (*p == '\0') { + /* Trailing "**" matches everything. Trailing "*" matches + * only if there are no more slash characters. */ + if (!match_slash) { + if (strchr((char*)text, '/') != NULL) + return NOMATCH; + } + return MATCH; + } + while (1) { + if (t_ch == '\0') + break; + if ((matched = dowild(p, text, force_lower_case)) != NOMATCH) { + if (!match_slash || matched != ABORT_TO_STARSTAR) + return matched; + } else if (!match_slash && t_ch == '/') + return ABORT_TO_STARSTAR; + t_ch = *++text; + } + return ABORT_ALL; + case '[': + p_ch = *++p; +#ifdef NEGATE_CLASS2 + if (p_ch == NEGATE_CLASS2) + p_ch = NEGATE_CLASS; +#endif + /* Assign literal TRUE/FALSE because of "matched" comparison. */ + negated = p_ch == NEGATE_CLASS? TRUE : FALSE; + if (negated) { + /* Inverted character class. */ + p_ch = *++p; + } + prev_ch = 0; + matched = FALSE; + do { + if (!p_ch) + return ABORT_ALL; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) + return ABORT_ALL; + if (t_ch == p_ch) + matched = TRUE; + } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { + p_ch = *++p; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) + return ABORT_ALL; + } + if (t_ch <= p_ch && t_ch >= prev_ch) + matched = TRUE; + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (p_ch == '[' && p[1] == ':') { + const uchar *s; + int i; + for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ + if (!p_ch) + return ABORT_ALL; + i = p - s - 1; + if (i < 0 || p[-1] != ':') { + /* Didn't find ":]", so treat like a normal set. */ + p = s - 2; + p_ch = '['; + if (t_ch == p_ch) + matched = TRUE; + continue; + } + if (CC_EQ(s,i, "alnum")) { + if (ISALNUM(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "alpha")) { + if (ISALPHA(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "blank")) { + if (ISBLANK(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "cntrl")) { + if (ISCNTRL(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "digit")) { + if (ISDIGIT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "graph")) { + if (ISGRAPH(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "lower")) { + if (ISLOWER(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "print")) { + if (ISPRINT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "punct")) { + if (ISPUNCT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "space")) { + if (ISSPACE(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "upper")) { + if (ISUPPER(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "xdigit")) { + if (ISXDIGIT(t_ch)) + matched = TRUE; + } else /* malformed [:class:] string */ + return ABORT_ALL; + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (t_ch == p_ch) + matched = TRUE; + } while (prev_ch = p_ch, (p_ch = *++p) != ']'); + if (matched == negated || t_ch == '/') + return NOMATCH; + continue; + } + } + + return *text ? NOMATCH : MATCH; +} + +/* Match the "pattern" against the "text" string. */ +int wildmatch(const char *pattern, const char *text, int flags) +{ + return dowild((const uchar*)pattern, (const uchar*)text, + flags & FNM_CASEFOLD ? 1 :0); +} diff --git a/wildmatch.h b/wildmatch.h new file mode 100644 index 0000000000..984a38cdc2 --- /dev/null +++ b/wildmatch.h @@ -0,0 +1,9 @@ +/* wildmatch.h */ + +#define ABORT_MALFORMED 2 +#define NOMATCH 1 +#define MATCH 0 +#define ABORT_ALL -1 +#define ABORT_TO_STARSTAR -2 + +int wildmatch(const char *pattern, const char *text, int flags); diff --git a/wt-status.c b/wt-status.c index 2a9658bad4..d7cfe8f31c 100644 --- a/wt-status.c +++ b/wt-status.c @@ -516,7 +516,9 @@ static void wt_status_collect_untracked(struct wt_status *s) if (s->show_ignored_files) { dir.nr = 0; - dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES; + dir.flags = DIR_SHOW_IGNORED; + if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) + dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; |