diff options
96 files changed, 3568 insertions, 1110 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index 6346a75dda..551325604e 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -1,9 +1,9 @@ MAN1_TXT= \ $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ $(wildcard git-*.txt)) \ - gitk.txt git.txt + gitk.txt gitweb.txt git.txt MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt \ - gitrepository-layout.txt + gitrepository-layout.txt gitweb.conf.txt MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \ gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \ gitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt diff --git a/Documentation/RelNotes/1.7.7.1.txt b/Documentation/RelNotes/1.7.7.1.txt index 02d7c02a00..fecfac8a16 100644 --- a/Documentation/RelNotes/1.7.7.1.txt +++ b/Documentation/RelNotes/1.7.7.1.txt @@ -4,6 +4,26 @@ Git v1.7.7.1 Release Notes Fixes since v1.7.7 ------------------ + * On some BSD systems, adding +s bit on directories is detrimental + (it is not necessary on BSD to begin with). The installation + procedure has been updated to take this into account. + + * After incorrectly written third-party tools store a tag object in + HEAD, git diagnosed it as a repository corruption and refused to + proceed in order to avoid spreading the damage. We now gracefully + recover from such a situation by pretending as if the commit that + is pointed at by the tag were in HEAD. + + * "git apply --whitespace=error" did not bother to report the exact + line number in the patch that introduced new blank lines at the end + of the file. + + * "git apply --index" did not check corrupted patch. + + * "git checkout $tree $directory/" resurrected paths locally removed or + modified only in the working tree in $directory/ that did not appear + in $directory of the given $tree. They should have been kept intact. + * "git diff $tree $path" used to apply the pathspec at the output stage, reading the whole tree, wasting resources. diff --git a/Documentation/RelNotes/1.7.8.txt b/Documentation/RelNotes/1.7.8.txt index 5500db2b71..886dfbfaa5 100644 --- a/Documentation/RelNotes/1.7.8.txt +++ b/Documentation/RelNotes/1.7.8.txt @@ -4,16 +4,16 @@ Git v1.7.8 Release Notes (draft) Updates since v1.7.7 -------------------- + * Some git-svn, git-gui and msysgit updates. + + * Updates to bash completion scripts. + * The build procedure has been taught to take advantage of computed dependency automatically when the complier supports it. * The date parser now accepts timezone designators that lack minutes part and also has a colon between "hh:mm". - * On some BSD systems, adding +s bit on directories is detrimental - (it is not necessary on BSD to begin with). The installation - procedure has been updated to take this into account. - * The contents of the /etc/mailname file, if exists, is used as the default value of the hostname part of the committer/author e-mail. @@ -36,6 +36,9 @@ Updates since v1.7.7 * "git diff" learned "--minimal" option to spend extra cycles to come up with a minimal patch output. + * "git diff" learned "--function-context" option to show the whole + function as context that was affected by a change. + * "git fetch" learned to honor transfer.fsckobjects configuration to validate the objects that were received from the other end, just like "git receive-pack" (the receiving end of "git push") does. @@ -59,8 +62,16 @@ Updates since v1.7.7 files in the working tree, so that matches in new but not yet added files do not get missed. + * The recursive merge backend no longer looks for meaningless + existing merges in submodules unless in the outermost merge. + + * "git log" and friends learned "--children" option. + * "git ls-remote" learned to respond to "-h"(elp) requests. + * "git merge" learned the "--edit" option to allow users to edit the + merge commit log message. + * "git send-email" learned to respond to "-h"(elp) requests. * "git send-email" allows the value given to sendemail.aliasfile to begin @@ -89,6 +100,8 @@ Updates since v1.7.7 * "gitweb" leaked unescaped control characters from syntax hiliter outputs. + * "gitweb" now has its own manual pages. + Also contains other documentation updates and minor code cleanups. @@ -108,23 +121,31 @@ included in this release. a corrupt packet. (merge 6cdf022 sp/smart-http-failure later to maint). + * HTTP transport did not use pushurl correctly, and also did not tell + what host it is trying to authenticate with when asking for + credentials. + (merge deba493 jk/http-auth later to maint). + * Adding many refs to the local repository in one go (e.g. "git fetch" that fetches many tags) and looking up a ref by name in a repository with too many refs were unnecessarily slow. (merge 17d68a54d jp/get-ref-dir-unsorted later to maint). - * After incorrectly written third-party tools store a tag object in - HEAD, git diagnosed it as a repository corruption and refused to - proceed in order to avoid spreading the damage. We now gracefully - recover from such a situation by pretending as if the commit that - is pointed at by the tag were in HEAD. - (merge baf18fc nd/maint-autofix-tag-in-head later to maint). + * Report from "git commit" on untracked files was confused under + core.ignorecase option. + (merge 2548183b jk/name-hash-dirent later to maint). + + * The attribute mechanism did not use case insensitive match when + core.ignorecase was set. + (merge 6eba621 bc/attr-ignore-case later to maint). - * "git apply --whitespace=error" did not bother to report the exact - line number in the patch that introduced new blank lines at the end - of the file. - (merge 8557263 jc/apply-blank-at-eof-fix later to maint). + * "git bisect" did not notice when it failed to update the working tree + to the next commit to be tested. + (merge 1acf11717 js/bisect-no-checkout later to maint). + * "git config --bool --get-regexp" failed to separate the variable name + and its value "true" when the variable is defined without "= true". + (merge 880e3cc mm/maint-config-explicit-bool-display later to maint). * "git remote rename $a $b" were not careful to match the remote name against $a (i.e. source side of the remote nickname). @@ -141,13 +162,26 @@ included in this release. the file that may not even have any conflict. (merge 6d9990a jm/mergetool-pathspec later to maint). + * "git pull" and "git rebase" did not work well even when GIT_WORK_TREE is + set correctly with GIT_DIR if the current directory is outside the working + tree. + (merge 035b5bf jk/pull-rebase-with-work-tree later to maint). + + " "git push" on the receiving end used to call post-receive and post-update + hooks for attempted removal of non-existing refs. + (merge 160b81ed ph/push-to-delete-nothing later to maint). + + * "git send-email" did not honor the configured hostname when restarting + the HELO/EHLO exchange after switching TLS on. + (merge 155b940 md/smtp-tls-hello-again later to maint). + * "gitweb" used to produce a non-working link while showing the contents of a blob, when JavaScript actions are enabled. (merge 2b07ff3ff ps/gitweb-js-with-lineno later to maint). --- exec >/var/tmp/1 -O=v1.7.7-289-gb73c683 +O=v1.7.7-418-g40d6987 echo O=$(git describe --always master) git log --first-parent --oneline --reverse ^$O master echo diff --git a/Documentation/config.txt b/Documentation/config.txt index 03296b7eb8..9c81fffc61 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -45,9 +45,10 @@ lines. Variables may belong directly to a section or to a given subsection. You can have `[section]` if you have `[section "subsection"]`, but you don't need to. -There is also a case insensitive alternative `[section.subsection]` syntax. -In this syntax, subsection names follow the same restrictions as for section -names. +There is also a deprecated `[section.subsection]` syntax. With this +syntax, the subsection name is converted to lower-case and is also +compared case sensitively. These subsection names follow the same +restrictions as section names. All the other lines (and the remainder of the line after the section header) are recognized as setting variables, in the form @@ -1071,6 +1072,23 @@ All gitcvs variables except for 'gitcvs.usecrlfattr' and is one of "ext" and "pserver") to make them apply only for the given access method. +gitweb.category:: +gitweb.description:: +gitweb.owner:: +gitweb.url:: + See linkgit:gitweb[1] for description. + +gitweb.avatar:: +gitweb.blame:: +gitweb.grep:: +gitweb.highlight:: +gitweb.patches:: +gitweb.pickaxe:: +gitweb.remote_heads:: +gitweb.showsizes:: +gitweb.snapshot:: + See linkgit:gitweb.conf[5] for description. + grep.lineNumber:: If set to true, enable '-n' option by default. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 5c53bdba94..08b581f040 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -408,6 +408,10 @@ endif::git-format-patch[] Show the context between diff hunks, up to the specified number of lines, thereby fusing hunks that are close to each other. +-W:: +--function-context:: + Show whole surrounding functions of changes. + ifndef::git-format-patch[] --exit-code:: Make the program exit with codes similar to diff(1). diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 69a1e4af9e..31b28fc29f 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -161,6 +161,16 @@ the facility of inet daemon to achieve the same before spawning repository configuration. By default, all the services are overridable. +--informative-errors:: +--no-informative-errors:: + When informative errors are turned on, git-daemon will report + more verbose errors to the client, differentiating conditions + like "no such repository" from "repository not exported". This + is more convenient for clients, but may leak information about + the existence of unexported repositories. When informative + errors are not enabled, all errors report "access denied" to the + client. The default is --no-informative-errors. + <directory>:: A directory to add to the whitelist of allowed directories. Unless --strict-paths is specified this will also include subdirectories diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt index ea95c90460..f3eef510f2 100644 --- a/Documentation/git-instaweb.txt +++ b/Documentation/git-instaweb.txt @@ -84,6 +84,10 @@ If the configuration variable 'instaweb.browser' is not set, 'web.browser' will be used instead if it is defined. See linkgit:git-web{litdd}browse[1] for more information about this. +SEE ALSO +-------- +linkgit:gitweb[1] + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index f977e8780b..34ee785064 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -234,6 +234,14 @@ svn:mergeinfo property in the SVN repository when possible. Currently, this can only be done when dcommitting non-fast-forward merges where all parents but the first have already been pushed into SVN. +--interactive;; + Ask the user to confirm that a patch set should actually be sent to SVN. + For each patch, one may answer "yes" (accept this patch), "no" (discard this + patch), "all" (accept all patches), or "quit". + + + 'git svn dcommit' returns immediately if answer if "no" or "quit", without + commiting anything to SVN. + 'branch':: Create a branch in the SVN repository. diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt new file mode 100644 index 0000000000..4ca3e27dc9 --- /dev/null +++ b/Documentation/gitweb.conf.txt @@ -0,0 +1,884 @@ +gitweb.conf(5) +============== + +NAME +---- +gitweb.conf - Gitweb (git web interface) configuration file + +SYNOPSIS +-------- +/etc/gitweb.conf, /etc/gitweb-common.conf, $GITWEBDIR/gitweb_config.perl + +DESCRIPTION +----------- + +The gitweb CGI script for viewing Git repositories over the web uses a +perl script fragment as its configuration file. You can set variables +using "`our $variable = value`"; text from a "#" character until the +end of a line is ignored. See *perlsyn*(1) for details. + +An example: + + # gitweb configuration file for http://git.example.org + # + our $projectroot = "/srv/git"; # FHS recommendation + our $site_name = 'Example.org >> Repos'; + + +The configuration file is used to override the default settings that +were built into gitweb at the time the 'gitweb.cgi' script was generated. + +While one could just alter the configuration settings in the gitweb +CGI itself, those changes would be lost upon upgrade. Configuration +settings might also be placed into a file in the same directory as the +CGI script with the default name 'gitweb_config.perl' -- allowing +one to have multiple gitweb instances with different configurations by +the use of symlinks. + +Note that some configuration can be controlled on per-repository rather than +gitweb-wide basis: see "Per-repository gitweb configuration" subsection on +linkgit:gitweb[1] manpage. + + +DISCUSSION +---------- +Gitweb reads configuration data from the following sources in the +following order: + + * built-in values (some set during build stage), + + * common system-wide configuration file (defaults to + '/etc/gitweb-common.conf'), + + * either per-instance configuration file (defaults to 'gitweb_config.perl' + in the same directory as the installed gitweb), or if it does not exists + then fallback system-wide configuration file (defaults to '/etc/gitweb.conf'). + +Values obtained in later configuration files override values obtained earlier +in the above sequence. + +Locations of the common system-wide configuration file, the fallback +system-wide configuration file and the per-instance configuration file +are defined at compile time using build-time Makefile configuration +variables, respectively `GITWEB_CONFIG_COMMON`, `GITWEB_CONFIG_SYSTEM` +and `GITWEB_CONFIG`. + +You can also override locations of gitweb configuration files during +runtime by setting the following environment variables: +`GITWEB_CONFIG_COMMON`, `GITWEB_CONFIG_SYSTEM` and `GITWEB_CONFIG` +to a non-empty value. + + +The syntax of the configuration files is that of Perl, since these files are +handled by sourcing them as fragments of Perl code (the language that +gitweb itself is written in). Variables are typically set using the +`our` qualifier (as in "`our $variable = <value>;`") to avoid syntax +errors if a new version of gitweb no longer uses a variable and therefore +stops declaring it. + +You can include other configuration file using read_config_file() +subroutine. For example, one might want to put gitweb configuration +related to access control for viewing repositories via Gitolite (one +of git repository management tools) in a separate file, e.g. in +'/etc/gitweb-gitolite.conf'. To include it, put + +-------------------------------------------------- +read_config_file("/etc/gitweb-gitolite.conf"); +-------------------------------------------------- + +somewhere in gitweb configuration file used, e.g. in per-installation +gitweb configuration file. Note that read_config_file() checks itself +that the file it reads exists, and does nothing if it is not found. +It also handles errors in included file. + + +The default configuration with no configuration file at all may work +perfectly well for some installations. Still, a configuration file is +useful for customizing or tweaking the behavior of gitweb in many ways, and +some optional features will not be present unless explicitly enabled using +the configurable `%features` variable (see also "Configuring gitweb +features" section below). + + +CONFIGURATION VARIABLES +----------------------- +Some configuration variables have their default values (embedded in the CGI +script) set during building gitweb -- if that is the case, this fact is put +in their description. See gitweb's 'INSTALL' file for instructions on building +and installing gitweb. + + +Location of repositories +~~~~~~~~~~~~~~~~~~~~~~~~ +The configuration variables described below control how gitweb finds +git repositories, and how repositories are displayed and accessed. + +See also "Repositories" and later subsections in linkgit:gitweb[1] manpage. + +$projectroot:: + Absolute filesystem path which will be prepended to project path; + the path to repository is `$projectroot/$project`. Set to + `$GITWEB_PROJECTROOT` during installation. This variable has to be + set correctly for gitweb to find repositories. ++ +For example, if `$projectroot` is set to "/srv/git" by putting the following +in gitweb config file: ++ +---------------------------------------------------------------------------- +our $projectroot = "/srv/git"; +---------------------------------------------------------------------------- ++ +then ++ +------------------------------------------------ +http://git.example.com/gitweb.cgi?p=foo/bar.git +------------------------------------------------ ++ +and its path_info based equivalent ++ +------------------------------------------------ +http://git.example.com/gitweb.cgi/foo/bar.git +------------------------------------------------ ++ +will map to the path '/srv/git/foo/bar.git' on the filesystem. + +$projects_list:: + Name of a plain text file listing projects, or a name of directory + to be scanned for projects. ++ +Project list files should list one project per line, with each line +having the following format ++ +----------------------------------------------------------------------------- +<URI-encoded filesystem path to repository> SP <URI-encoded repository owner> +----------------------------------------------------------------------------- ++ +The default value of this variable is determined by the `GITWEB_LIST` +makefile variable at installation time. If this variable is empty, gitweb +will fall back to scanning the `$projectroot` directory for repositories. + +$project_maxdepth:: + If `$projects_list` variable is unset, gitweb will recursively + scan filesystem for git repositories. The `$project_maxdepth` + is used to limit traversing depth, relative to `$projectroot` + (starting point); it means that directories which are further + from `$projectroot` than `$project_maxdepth` will be skipped. ++ +It is purely performance optimization, originally intended for MacOS X, +where recursive directory traversal is slow. Gitweb follows symbolic +links, but it detects cycles, ignoring any duplicate files and directories. ++ +The default value of this variable is determined by the build-time +configuration variable `GITWEB_PROJECT_MAXDEPTH`, which defaults to +2007. + +$export_ok:: + Show repository only if this file exists (in repository). Only + effective if this variable evaluates to true. Can be set when + building gitweb by setting `GITWEB_EXPORT_OK`. This path is + relative to `GIT_DIR`. git-daemon[1] uses 'git-daemon-export-ok', + unless started with `--export-all`. By default this variable is + not set, which means that this feature is turned off. + +$export_auth_hook:: + Function used to determine which repositories should be shown. + This subroutine should take one parameter, the full path to + a project, and if it returns true, that project will be included + in the projects list and can be accessed through gitweb as long + as it fulfills the other requirements described by $export_ok, + $projects_list, and $projects_maxdepth. Example: ++ +---------------------------------------------------------------------------- +our $export_auth_hook = sub { return -e "$_[0]/git-daemon-export-ok"; }; +---------------------------------------------------------------------------- ++ +though the above might be done by using `$export_ok` instead ++ +---------------------------------------------------------------------------- +our $export_ok = "git-daemon-export-ok"; +---------------------------------------------------------------------------- ++ +If not set (default), it means that this feature is disabled. ++ +See also more involved example in "Controlling access to git repositories" +subsection on linkgit:gitweb[1] manpage. + +$strict_export:: + Only allow viewing of repositories also shown on the overview page. + This for example makes `$gitweb_export_ok` file decide if repository is + available and not only if it is shown. If `$gitweb_list` points to + file with list of project, only those repositories listed would be + available for gitweb. Can be set during building gitweb via + `GITWEB_STRICT_EXPORT`. By default this variable is not set, which + means that you can directly access those repositories that are hidden + from projects list page (e.g. the are not listed in the $projects_list + file). + + +Finding files +~~~~~~~~~~~~~ +The following configuration variables tell gitweb where to find files. +The values of these variables are paths on the filesystem. + +$GIT:: + Core git executable to use. By default set to `$GIT_BINDIR/git`, which + in turn is by default set to `$(bindir)/git`. If you use git installed + from a binary package, you should usually set this to "/usr/bin/git". + This can just be "git" if your web server has a sensible PATH; from + security point of view it is better to use absolute path to git binary. + If you have multiple git versions installed it can be used to choose + which one to use. Must be (correctly) set for gitweb to be able to + work. + +$mimetypes_file:: + File to use for (filename extension based) guessing of MIME types before + trying '/etc/mime.types'. *NOTE* that this path, if relative, is taken + as relative to the current git repository, not to CGI script. If unset, + only '/etc/mime.types' is used (if present on filesystem). If no mimetypes + file is found, mimetype guessing based on extension of file is disabled. + Unset by default. + +$highlight_bin:: + Path to the highlight executable to use (it must be the one from + http://www.andre-simon.de[] due to assumptions about parameters and output). + By default set to 'highlight'; set it to full path to highlight + executable if it is not installed on your web server's PATH. + Note that 'highlight' feature must be set for gitweb to actually + use syntax hightlighting. ++ +*NOTE*: if you want to add support for new file type (supported by +"highlight" but not used by gitweb), you need to modify `%highlight_ext` +or `%highlight_basename`, depending on whether you detect type of file +based on extension (for example "sh") or on its basename (for example +"Makefile"). The keys of these hashes are extension and basename, +respectively, and value for given key is name of syntax to be passed via +`--syntax <syntax>` to highlighter. ++ +For example if repositories you are hosting use "phtml" extension for +PHP files, and you want to have correct syntax-highlighting for those +files, you can add the following to gitweb configuration: ++ +--------------------------------------------------------- +our %highlight_ext; +$highlight_ext{'phtml'} = 'php'; +--------------------------------------------------------- + + +Links and their targets +~~~~~~~~~~~~~~~~~~~~~~~ +The configuration variables described below configure some of gitweb links: +their target and their look (text or image), and where to find page +prerequisites (stylesheet, favicon, images, scripts). Usually they are left +at their default values, with the possible exception of `@stylesheets` +variable. + +@stylesheets:: + List of URIs of stylesheets (relative to the base URI of a page). You + might specify more than one stylesheet, for example to use "gitweb.css" + as base with site specific modifications in a separate stylesheet + to make it easier to upgrade gitweb. For example, you can add + a `site` stylesheet by putting ++ +---------------------------------------------------------------------------- +push @stylesheets, "gitweb-site.css"; +---------------------------------------------------------------------------- ++ +in the gitweb config file. Those values that are relative paths are +relative to base URI of gitweb. ++ +This list should contain the URI of gitweb's standard stylesheet. The default +URI of gitweb stylesheet can be set at build time using the `GITWEB_CSS` +makefile variable. Its default value is 'static/gitweb.css' +(or 'static/gitweb.min.css' if the `CSSMIN` variable is defined, +i.e. if CSS minifier is used during build). ++ +*Note*: there is also a legacy `$stylesheet` configuration variable, which was +used by older gitweb. If `$stylesheet` variable is defined, only CSS stylesheet +given by this variable is used by gitweb. + +$logo:: + Points to the location where you put 'git-logo.png' on your web + server, or to be more the generic URI of logo, 72x27 size). This image + is displayed in the top right corner of each gitweb page and used as + a logo for the Atom feed. Relative to the base URI of gitweb (as a path). + Can be adjusted when building gitweb using `GITWEB_LOGO` variable + By default set to 'static/git-logo.png'. + +$favicon:: + Points to the location where you put 'git-favicon.png' on your web + server, or to be more the generic URI of favicon, which will be served + as "image/png" type. Web browsers that support favicons (website icons) + may display them in the browser's URL bar and next to the site name in + bookmarks. Relative to the base URI of gitweb. Can be adjusted at + build time using `GITWEB_FAVICON` variable. + By default set to 'static/git-favicon.png'. + +$javascript:: + Points to the location where you put 'gitweb.js' on your web server, + or to be more generic the URI of JavaScript code used by gitweb. + Relative to the base URI of gitweb. Can be set at build time using + the `GITWEB_JS` build-time configuration variable. ++ +The default value is either 'static/gitweb.js', or 'static/gitweb.min.js' if +the `JSMIN` build variable was defined, i.e. if JavaScript minifier was used +at build time. *Note* that this single file is generated from multiple +individual JavaScript "modules". + +$home_link:: + Target of the home link on the top of all pages (the first part of view + "breadcrumbs"). By default it is set to the absolute URI of a current page + (to the value of `$my_uri` variable, or to "/" if `$my_uri` is undefined + or is an empty string). + +$home_link_str:: + Label for the "home link" at the top of all pages, leading to `$home_link` + (usually the main gitweb page, which contains the projects list). It is + used as the first component of gitweb's "breadcrumb trail": + `<home link> / <project> / <action>`. Can be set at build time using + the `GITWEB_HOME_LINK_STR` variable. By default it is set to "projects", + as this link leads to the list of projects. Other popular choice it to + set it to the name of site. + +$logo_url:: +$logo_label:: + URI and label (title) for the Git logo link (or your site logo, + if you chose to use different logo image). By default, these both + refer to git homepage, http://git-scm.com[]; in the past, they pointed + to git documentation at http://www.kernel.org[]. + + +Changing gitweb's look +~~~~~~~~~~~~~~~~~~~~~~ +You can adjust how pages generated by gitweb look using the variables described +below. You can change the site name, add common headers and footers for all +pages, and add a description of this gitweb installation on its main page +(which is the projects list page), etc. + +$site_name:: + Name of your site or organization, to appear in page titles. Set it + to something descriptive for clearer bookmarks etc. If this variable + is not set or is, then gitweb uses the value of the `SERVER_NAME` + CGI environment variable, setting site name to "$SERVER_NAME Git", + or "Untitled Git" if this variable is not set (e.g. if running gitweb + as standalone script). ++ +Can be set using the `GITWEB_SITENAME` at build time. Unset by default. + +$site_header:: + Name of a file with HTML to be included at the top of each page. + Relative to the directory containing the 'gitweb.cgi' script. + Can be set using `GITWEB_SITE_HEADER` at build time. No default + value. + +$site_footer:: + Name of a file with HTML to be included at the bottom of each page. + Relative to the directory containing the 'gitweb.cgi' script. + Can be set using `GITWEB_SITE_FOOTER` at build time. No default + value. + +$home_text:: + Name of a HTML file which, if it exists, is included on the + gitweb projects overview page ("projects_list" view). Relative to + the directory containing the gitweb.cgi script. Default value + can be adjusted during build time using `GITWEB_HOMETEXT` variable. + By default set to 'indextext.html'. + +$projects_list_description_width:: + The width (in characters) of the "Description" column of the projects list. + Longer descriptions will be truncated (trying to cut at word boundary); + the full description is available in the 'title' attribute (usually shown on + mouseover). The default is 25, which might be too small if you + use long project descriptions. + +$default_projects_order:: + Default value of ordering of projects on projects list page, which + means the ordering used if you don't explicitly sort projects list + (if there is no "o" CGI query parameter in the URL). Valid values + are "none" (unsorted), "project" (projects are by project name, + i.e. path to repository relative to `$projectroot`), "descr" + (project description), "owner", and "age" (by date of most current + commit). ++ +Default value is "project". Unknown value means unsorted. + + +Changing gitweb's behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~ +These configuration variables control _internal_ gitweb behavior. + +$default_blob_plain_mimetype:: + Default mimetype for the blob_plain (raw) view, if mimetype checking + doesn't result in some other type; by default "text/plain". + Gitweb guesses mimetype of a file to display based on extension + of its filename, using `$mimetypes_file` (if set and file exists) + and '/etc/mime.types' files (see *mime.types*(5) manpage; only + filename extension rules are supported by gitweb). + +$default_text_plain_charset:: + Default charset for text files. If this is not set, the web server + configuration will be used. Unset by default. + +$fallback_encoding:: + Gitweb assumes this charset when a line contains non-UTF-8 characters. + The fallback decoding is used without error checking, so it can be even + "utf-8". The value must be a valid encoding; see the *Encoding::Supported*(3pm) + man page for a list. The default is "latin1", aka. "iso-8859-1". + +@diff_opts:: + Rename detection options for git-diff and git-diff-tree. The default is + (\'-M'); set it to (\'-C') or (\'-C', \'-C') to also detect copies, + or set it to () i.e. empty list if you don't want to have renames + detection. ++ +*Note* that rename and especially copy detection can be quite +CPU-intensive. Note also that non git tools can have problems with +patches generated with options mentioned above, especially when they +involve file copies (\'-C') or criss-cross renames (\'-B'). + + +Some optional features and policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Most of features are configured via `%feature` hash; however some of extra +gitweb features can be turned on and configured using variables described +below. This list beside configuration variables that control how gitweb +looks does contain variables configuring administrative side of gitweb +(e.g. cross-site scripting prevention; admittedly this as side effect +affects how "summary" pages look like, or load limiting). + +@git_base_url_list:: + List of git base URLs. These URLs are used to generate URLs + describing from where to fetch a project, which are shown on + project summary page. The full fetch URL is "`$git_base_url/$project`", + for each element of this list. You can set up multiple base URLs + (for example one for `git://` protocol, and one for `http://` + protocol). ++ +Note that per repository configuration can be set in '$GIT_DIR/cloneurl' +file, or as values of multi-value `gitweb.url` configuration variable in +project config. Per-repository configuration takes precedence over value +composed from `@git_base_url_list` elements and project name. ++ +You can setup one single value (single entry/item in this list) at build +time by setting the `GITWEB_BASE_URL` built-time configuration variable. +By default it is set to (), i.e. an empty list. This means that gitweb +would not try to create project URL (to fetch) from project name. + +$projects_list_group_categories:: + Whether to enables the grouping of projects by category on the project + list page. The category of a project is determined by the + `$GIT_DIR/category` file or the `gitweb.category` variable in each + repository's configuration. Disabled by default (set to 0). + +$project_list_default_category:: + Default category for projects for which none is specified. If this is + set to the empty string, such projects will remain uncategorized and + listed at the top, above categorized projects. Used only if project + categories are enabled, which means if `$projects_list_group_categories` + is true. By default set to "" (empty string). + +$prevent_xss:: + If true, some gitweb features are disabled to prevent content in + repositories from launching cross-site scripting (XSS) attacks. Set this + to true if you don't trust the content of your repositories. + False by default (set to 0). + +$maxload:: + Used to set the maximum load that we will still respond to gitweb queries. + If the server load exceeds this value then gitweb will return + "503 Service Unavailable" error. The server load is taken to be 0 + if gitweb cannot determine its value. Currently it works only on Linux, + where it uses '/proc/loadavg'; the load there is the number of active + tasks on the system -- processes that are actually running -- averaged + over the last minute. ++ +Set `$maxload` to undefined value (`undef`) to turn this feature off. +The default value is 300. + +$per_request_config:: + If this is set to code reference, it will be run once for each request. + You can set parts of configuration that change per session this way. + For example, one might use the following code in a gitweb configuration + file ++ +-------------------------------------------------------------------------------- +our $per_request_config = sub { + $ENV{GL_USER} = $cgi->remote_user || "gitweb"; +}; +-------------------------------------------------------------------------------- ++ +If `$per_request_config` is not a code reference, it is interpreted as boolean +value. If it is true gitweb will process config files once per request, +and if it is false gitweb will process config files only once, each time it +is executed. True by default (set to 1). ++ +*NOTE*: `$my_url`, `$my_uri`, and `$base_url` are overwritten with their default +values before every request, so if you want to change them, be sure to set +this variable to true or a code reference effecting the desired changes. ++ +This variable matters only when using persistent web environments that +serve multiple requests using single gitweb instance, like mod_perl, +FastCGI or Plackup. + + +Other variables +~~~~~~~~~~~~~~~ +Usually you should not need to change (adjust) any of configuration +variables described below; they should be automatically set by gitweb to +correct value. + + +$version:: + Gitweb version, set automatically when creating gitweb.cgi from + gitweb.perl. You might want to modify it if you are running modified + gitweb, for example ++ +--------------------------------------------------- +our $version .= " with caching"; +--------------------------------------------------- ++ +if you run modified version of gitweb with caching support. This variable +is purely informational, used e.g. in the "generator" meta header in HTML +header. + +$my_url:: +$my_uri:: + Full URL and absolute URL of the gitweb script; + in earlier versions of gitweb you might have need to set those + variables, but now there should be no need to do it. See + `$per_request_config` if you need to set them still. + +$base_url:: + Base URL for relative URLs in pages generated by gitweb, + (e.g. `$logo`, `$favicon`, `@stylesheets` if they are relative URLs), + needed and used '<base href="$base_url">' only for URLs with nonempty + PATH_INFO. Usually gitweb sets its value correctly, + and there is no need to set this variable, e.g. to $my_uri or "/". + See `$per_request_config` if you need to override it anyway. + + +CONFIGURING GITWEB FEATURES +--------------------------- +Many gitweb features can be enabled (or disabled) and configured using the +`%feature` hash. Names of gitweb features are keys of this hash. + +Each `%feature` hash element is a hash reference and has the following +structure: +---------------------------------------------------------------------- +"<feature_name>" => { + "sub" => <feature-sub (subroutine)>, + "override" => <allow-override (boolean)>, + "default" => [ <options>... ] +}, +---------------------------------------------------------------------- +Some features cannot be overridden per project. For those +features the structure of appropriate `%feature` hash element has a simpler +form: +---------------------------------------------------------------------- +"<feature_name>" => { + "override" => 0, + "default" => [ <options>... ] +}, +---------------------------------------------------------------------- +As one can see it lacks the \'sub' element. + +The meaning of each part of feature configuration is described +below: + +default:: + List (array reference) of feature parameters (if there are any), + used also to toggle (enable or disable) given feature. ++ +Note that it is currently *always* an array reference, even if +feature doesn't accept any configuration parameters, and \'default' +is used only to turn it on or off. In such case you turn feature on +by setting this element to `[1]`, and torn it off by setting it to +`[0]`. See also the passage about the "blame" feature in the "Examples" +section. ++ +To disable features that accept parameters (are configurable), you +need to set this element to empty list i.e. `[]`. + +override:: + If this field has a true value then the given feature is + overriddable, which means that it can be configured + (or enabled/disabled) on a per-repository basis. ++ +Usually given "<feature>" is configurable via the `gitweb.<feature>` +config variable in the per-repository git configuration file. ++ +*Note* that no feature is overriddable by default. + +sub:: + Internal detail of implementation. What is important is that + if this field is not present then per-repository override for + given feature is not supported. ++ +You wouldn't need to ever change it in gitweb config file. + + +Features in `%feature` +~~~~~~~~~~~~~~~~~~~~~~ +The gitweb features that are configurable via `%feature` hash are listed +below. This should be a complete list, but ultimately the authoritative +and complete list is in gitweb.cgi source code, with features described +in the comments. + +blame:: + Enable the "blame" and "blame_incremental" blob views, showing for + each line the last commit that modified it; see linkgit:git-blame[1]. + This can be very CPU-intensive and is therefore disabled by default. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.blame` configuration variable (boolean). + +snapshot:: + Enable and configure the "snapshot" action, which allows user to + download a compressed archive of any tree or commit, as produced + by linkgit:git-archive[1] and possibly additionally compressed. + This can potentially generate high traffic if you have large project. ++ +The value of \'default' is a list of names of snapshot formats, +defined in `%known_snapshot_formats` hash, that you wish to offer. +Supported formats include "tgz", "tbz2", "txz" (gzip/bzip2/xz +compressed tar archive) and "zip"; please consult gitweb sources for +a definitive list. By default only "tgz" is offered. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.blame` configuration variable, which contains +a comma separated list of formats or "none" to disable snapshots. +Unknown values are ignored. + +grep:: + Enable grep search, which lists the files in currently selected + tree (directory) containing the given string; see linkgit:git-grep[1]. + This can be potentially CPU-intensive, of course. Enabled by default. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.grep` configuration variable (boolean). + +pickaxe:: + Enable the so called pickaxe search, which will list the commits + that introduced or removed a given string in a file. This can be + practical and quite faster alternative to "blame" action, but it is + still potentially CPU-intensive. Enabled by default. ++ +The pickaxe search is described in linkgit:git-log[1] (the +description of `-S<string>` option, which refers to pickaxe entry in +linkgit:gitdiffcore[7] for more details). ++ +This feature can be configured on a per-repository basis by setting +repository's `gitweb.pickaxe` configuration variable (boolean). + +show-sizes:: + Enable showing size of blobs (ordinary files) in a "tree" view, in a + separate column, similar to what `ls -l` does; see description of + `-l` option in linkgit:git-ls-tree[1] manpage. This costs a bit of + I/O. Enabled by default. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.showsizes` configuration variable (boolean). + +patches:: + Enable and configure "patches" view, which displays list of commits in email + (plain text) output format; see also linkgit:git-format-patch[1]. + The value is the maximum number of patches in a patchset generated + in "patches" view. Set the 'default' field to a list containing single + item of or to an empty list to disable patch view, or to a list + containing a single negative number to remove any limit. + Default value is 16. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.patches` configuration variable (integer). + +avatar:: + Avatar support. When this feature is enabled, views such as + "shortlog" or "commit" will display an avatar associated with + the email of each committer and author. ++ +Currently available providers are *"gravatar"* and *"picon"*. +Only one provider at a time can be selected ('default' is one element list). +If an unknown provider is specified, the feature is disabled. +*Note* that some providers might require extra Perl packages to be +installed; see 'gitweb/INSTALL' for more details. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.avatar` configuration variable. ++ +See also `%avatar_size` with pixel sizes for icons and avatars +("default" is used for one-line like "log" and "shortlog", "double" +is used for two-line like "commit", "commitdiff" or "tag"). If the +default font sizes or lineheights are changed (e.g. via adding extra +CSS stylesheet in `@stylesheets`), it may be appropriate to change +these values. + +highlight:: + Server-side syntax highlight support in "blob" view. It requires + `$highlight_bin` program to be available (see the description of + this variable in the "Configuration variables" section above), + and therefore is disabled by default. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.highlight` configuration variable (boolean). + +remote_heads:: + Enable displaying remote heads (remote-tracking branches) in the "heads" + list. In most cases the list of remote-tracking branches is an + unnecessary internal private detail, and this feature is therefore + disabled by default. linkgit:git-instaweb[1], which is usually used + to browse local repositories, enables and uses this feature. ++ +This feature can be configured on a per-repository basis via +repository's `gitweb.remote_heads` configuration variable (boolean). + + +The remaining features cannot be overridden on a per project basis. + +search:: + Enable text search, which will list the commits which match author, + committer or commit text to a given string; see the description of + `--author`, `--committer` and `--grep` options in linkgit:git-log[1] + manpage. Enabled by default. ++ +Project specific override is not supported. + +forks:: + If this feature is enabled, gitweb considers projects in + subdirectories of project root (basename) to be forks of existing + projects. For each project `$projname.git`, projects in the + `$projname/` directory and its subdirectories will not be + shown in the main projects list. Instead, a \'+' mark is shown + next to `$projname`, which links to a "forks" view that lists all + the forks (all projects in `$projname/` subdirectory). Additionally + a "forks" view for a project is linked from project summary page. ++ +If the project list is taken from a file (`$projects_list` points to a +file), forks are only recognized if they are listed after the main project +in that file. ++ +Project specific override is not supported. + +actions:: + Insert custom links to the action bar of all project pages. This + allows you to link to third-party scripts integrating into gitweb. ++ +The "default" value consists of a list of triplets in the form +`("<label>", "<link>", "<position>")` where "position" is the label +after which to insert the link, "link" is a format string where `%n` +expands to the project name, `%f` to the project path within the +filesystem (i.e. "$projectroot/$project"), `%h` to the current hash +(\'h' gitweb parameter) and `%b` to the current hash base +(\'hb' gitweb parameter); `%%` expands to \'%'. ++ +For example, at the time this page was written, the http://repo.or.cz[] +git hosting site set it to the following to enable graphical log +(using the third party tool *git-browser*): ++ +---------------------------------------------------------------------- +$feature{'actions'}{'default'} = + [ ('graphiclog', '/git-browser/by-commit.html?r=%n', 'summary')]; +---------------------------------------------------------------------- ++ +This adds a link titled "graphiclog" after the "summary" link, leading to +`git-browser` script, passing `r=<project>` as a query parameter. ++ +Project specific override is not supported. + +timed:: + Enable displaying how much time and how many git commands it took to + generate and display each page in the page footer (at the bottom of + page). For example the footer might contain: "This page took 6.53325 + seconds and 13 git commands to generate." Disabled by default. ++ +Project specific override is not supported. + +javascript-timezone:: + Enable and configure the ability to change a common timezone for dates + in gitweb output via JavaScript. Dates in gitweb output include + authordate and committerdate in "commit", "commitdiff" and "log" + views, and taggerdate in "tag" view. Enabled by default. ++ +The value is a list of three values: a default timezone (for if the client +hasn't selected some other timezone and saved it in a cookie), a name of cookie +where to store selected timezone, and a CSS class used to mark up +dates for manipulation. If you want to turn this feature off, set "default" +to empty list: `[]`. ++ +Typical gitweb config files will only change starting (default) timezone, +and leave other elements at their default values: ++ +--------------------------------------------------------------------------- +$feature{'javascript-timezone'}{'default'}[0] = "utc"; +--------------------------------------------------------------------------- ++ +The example configuration presented here is guaranteed to be backwards +and forward compatible. ++ +Timezone values can be "local" (for local timezone that browser uses), "utc" +(what gitweb uses when JavaScript or this feature is disabled), or numerical +timezones in the form of "+/-HHMM", such as "+0200". ++ +Project specific override is not supported. + + +EXAMPLES +-------- + +To enable blame, pickaxe search, and snapshot support (allowing "tar.gz" and +"zip" snapshots), while allowing individual projects to turn them off, put +the following in your GITWEB_CONFIG file: + + $feature{'blame'}{'default'} = [1]; + $feature{'blame'}{'override'} = 1; + + $feature{'pickaxe'}{'default'} = [1]; + $feature{'pickaxe'}{'override'} = 1; + + $feature{'snapshot'}{'default'} = ['zip', 'tgz']; + $feature{'snapshot'}{'override'} = 1; + +If you allow overriding for the snapshot feature, you can specify which +snapshot formats are globally disabled. You can also add any command line +options you want (such as setting the compression level). For instance, you +can disable Zip compressed snapshots and set *gzip*(1) to run at level 6 by +adding the following lines to your gitweb configuration file: + + $known_snapshot_formats{'zip'}{'disabled'} = 1; + $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6']; + +ENVIRONMENT +----------- +The location of per-instance and system-wide configuration files can be +overridden using the following environment variables: + +GITWEB_CONFIG:: + Sets location of per-instance configuration file. +GITWEB_CONFIG_SYSTEM:: + Sets location of fallback system-wide configuration file. + This file is read only if per-instance one does not exist. +GITWEB_CONFIG_COMMON:: + Sets location of common system-wide configuration file. + + +FILES +----- +gitweb_config.perl:: + This is default name of per-instance configuration file. The + format of this file is described above. +/etc/gitweb.conf:: + This is default name of fallback system-wide configuration + file. This file is used only if per-instance configuration + variable is not found. +/etc/gitweb-common.conf:: + This is default name of common system-wide configuration + file. + + +SEE ALSO +-------- +linkgit:gitweb[1], linkgit:git-instaweb[1] + +'gitweb/README', 'gitweb/INSTALL' + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt new file mode 100644 index 0000000000..605a085326 --- /dev/null +++ b/Documentation/gitweb.txt @@ -0,0 +1,704 @@ +gitweb(1) +========= + +NAME +---- +gitweb - Git web interface (web frontend to Git repositories) + +SYNOPSIS +-------- +To get started with gitweb, run linkgit:git-instaweb[1] from a git repository. +This would configure and start your web server, and run web browser pointing to +gitweb. + + +DESCRIPTION +----------- +Gitweb provides a web interface to git repositories. It's features include: + +* Viewing multiple Git repositories with common root. +* Browsing every revision of the repository. +* Viewing the contents of files in the repository at any revision. +* Viewing the revision log of branches, history of files and directories, + see what was changed when, by who. +* Viewing the blame/annotation details of any file (if enabled). +* Generating RSS and Atom feeds of commits, for any branch. + The feeds are auto-discoverable in modern web browsers. +* Viewing everything that was changed in a revision, and step through + revisions one at a time, viewing the history of the repository. +* Finding commits which commit messages matches given search term. + +See http://git.kernel.org/?p=git/git.git;a=tree;f=gitweb[] or +http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code, +browsed using gitweb itself. + + +CONFIGURATION +------------- +Various aspects of gitweb's behavior can be controlled through the configuration +file 'gitweb_config.perl' or '/etc/gitweb.conf'. See the linkgit:gitweb.conf[5] +for details. + +Repositories +~~~~~~~~~~~~ +Gitweb can show information from one or more Git repositories. These +repositories have to be all on local filesystem, and have to share common +repository root, i.e. be all under a single parent repository (but see also +"Advanced web server setup" section, "Webserver configuration with multiple +projects' root" subsection). + +----------------------------------------------------------------------- +our $projectroot = '/path/to/parent/directory'; +----------------------------------------------------------------------- + +The default value for `$projectroot` is '/pub/git'. You can change it during +building gitweb via `GITWEB_PROJECTROOT` build configuration variable. + +By default all git repositories under `$projectroot` are visible and available +to gitweb. The list of projects is generated by default by scanning the +`$projectroot` directory for git repositories (for object databases to be +more exact; gitweb is not interested in a working area, and is best suited +to showing "bare" repositories). + +The name of repository in gitweb is path to it's `$GIT_DIR` (it's object +database) relative to `$projectroot`. Therefore the repository $repo can be +found at "$projectroot/$repo". + + +Projects list file format +~~~~~~~~~~~~~~~~~~~~~~~~~ +Instead of having gitweb find repositories by scanning filesystem +starting from $projectroot, you can provide a pre-generated list of +visible projects by setting `$projects_list` to point to a plain text +file with a list of projects (with some additional info). + +This file uses the following format: + +* One record (for project / repository) per line; does not support line +continuation (newline escaping). + +* Leading and trailing whitespace are ignored. + +* Whitespace separated fields; any run of whitespace can be used as field +separator (rules for Perl's "`split(" ", $line)`"). + +* Fields use modified URI encoding, defined in RFC 3986, section 2.1 +(Percent-Encoding), or rather "Query string encoding" (see +link:http://en.wikipedia.org/wiki/Query_string#URL_encoding[]), the difference +being that SP (" ") can be encoded as "{plus}" (and therefore "{plus}" has to be +also percent-encoded). ++ +Reserved characters are: "%" (used for encoding), "{plus}" (can be used to +encode SPACE), all whitespace characters as defined in Perl, including SP, +TAB and LF, (used to separate fields in a record). + +* Currently recognized fields are: +<repository path>:: + path to repository GIT_DIR, relative to `$projectroot` +<repository owner>:: + displayed as repository owner, preferably full name, or email, + or both + +You can generate the projects list index file using the project_index action +(the 'TXT' link on projects list page) directly from gitweb; see also +"Generating projects list using gitweb" section below. + +Example contents: +----------------------------------------------------------------------- +foo.git Joe+R+Hacker+<joe@example.com> +foo/bar.git O+W+Ner+<owner@example.org> +----------------------------------------------------------------------- + + +By default this file controls only which projects are *visible* on projects +list page (note that entries that do not point to correctly recognized git +repositories won't be displayed by gitweb). Even if a project is not +visible on projects list page, you can view it nevertheless by hand-crafting +a gitweb URL. By setting `$strict_export` configuration variable (see +linkgit:gitweb.conf[5]) to true value you can allow viewing only of +repositories also shown on the overview page (i.e. only projects explicitly +listed in projects list file will be accessible). + + +Generating projects list using gitweb +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We assume that GITWEB_CONFIG has its default Makefile value, namely +'gitweb_config.perl'. Put the following in 'gitweb_make_index.perl' file: +---------------------------------------------------------------------------- +read_config_file("gitweb_config.perl"); +$projects_list = $projectroot; +---------------------------------------------------------------------------- + +Then create the following script to get list of project in the format +suitable for GITWEB_LIST build configuration variable (or +`$projects_list` variable in gitweb config): + +---------------------------------------------------------------------------- +#!/bin/sh + +export GITWEB_CONFIG="gitweb_make_index.perl" +export GATEWAY_INTERFACE="CGI/1.1" +export HTTP_ACCEPT="*/*" +export REQUEST_METHOD="GET" +export QUERY_STRING="a=project_index" + +perl -- /var/www/cgi-bin/gitweb.cgi +---------------------------------------------------------------------------- + +Run this script and save its output to a file. This file could then be used +as projects list file, which means that you can set `$projects_list` to its +filename. + + +Controlling access to git repositories +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +By default all git repositories under `$projectroot` are visible and +available to gitweb. You can however configure how gitweb controls access +to repositories. + +* As described in "Projects list file format" section, you can control which +projects are *visible* by selectively including repositories in projects +list file, and setting `$projects_list` gitweb configuration variable to +point to it. With `$strict_export` set, projects list file can be used to +control which repositories are *available* as well. + +* You can configure gitweb to only list and allow viewing of the explicitly +exported repositories, via `$export_ok` variable in gitweb config file; see +linkgit:gitweb.conf[5] manpage. If it evaluates to true, gitweb shows +repositories only if this file named by `$export_ok` exists in its object +database (if directory has the magic file named `$export_ok`). ++ +For example linkgit:git-daemon[1] by default (unless `--export-all` option +is used) allows pulling only for those repositories that have +'git-daemon-export-ok' file. Adding ++ +-------------------------------------------------------------------------- +our $export_ok = "git-daemon-export-ok"; +-------------------------------------------------------------------------- ++ +makes gitweb show and allow access only to those repositories that can be +fetched from via `git://` protocol. + +* Finally, it is possible to specify an arbitrary perl subroutine that will +be called for each repository to determine if it can be exported. The +subroutine receives an absolute path to the project (repository) as its only +parameter (i.e. "$projectroot/$project"). ++ +For example, if you use mod_perl to run the script, and have dumb +HTTP protocol authentication configured for your repositories, you +can use the following hook to allow access only if the user is +authorized to read the files: ++ +-------------------------------------------------------------------------- +$export_auth_hook = sub { + use Apache2::SubRequest (); + use Apache2::Const -compile => qw(HTTP_OK); + my $path = "$_[0]/HEAD"; + my $r = Apache2::RequestUtil->request; + my $sub = $r->lookup_file($path); + return $sub->filename eq $path + && $sub->status == Apache2::Const::HTTP_OK; +}; +-------------------------------------------------------------------------- + + +Per-repository gitweb configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You can configure individual repositories shown in gitweb by creating file +in the 'GIT_DIR' of git repository, or by setting some repo configuration +variable (in 'GIT_DIR/config', see linkgit:git-config[1]). + +You can use the following files in repository: + +README.html:: + A html file (HTML fragment) which is included on the gitweb project + "summary" page inside `<div>` block element. You can use it for longer + description of a project, to provide links (for example to project's + homepage), etc. This is recognized only if XSS prevention is off + (`$prevent_xss` is false, see linkgit:gitweb.conf[5]); a way to include + a README safely when XSS prevention is on may be worked out in the + future. + +description (or `gitweb.description`):: + Short (shortened to `$projects_list_description_width` in the projects + list page, which is 25 characters by default; see + linkgit:gitweb.conf[5]) single line description of a project (of a + repository). Plain text file; HTML will be escaped. By default set to ++ +------------------------------------------------------------------------------- +Unnamed repository; edit this file to name it for gitweb. +------------------------------------------------------------------------------- ++ +from the template during repository creation, usually installed in +'/usr/share/git-core/templates/'. You can use the `gitweb.description` repo +configuration variable, but the file takes precedence. + +category (or `gitweb.category`):: + Singe line category of a project, used to group projects if + `$projects_list_group_categories` is enabled. By default (file and + configuration variable absent), uncategorized projects are put in the + `$project_list_default_category` category. You can use the + `gitweb.category` repo configuration variable, but the file takes + precedence. ++ +The configuration variables `$projects_list_group_categories` and +`$project_list_default_category` are described in linkgit:gitweb.conf[5] + +cloneurl (or multiple-valued `gitweb.url`):: + File with repository URL (used for clone and fetch), one per line. + Displayed in the project summary page. You can use multiple-valued + `gitweb.url` repository configuration variable for that, but the file + takes precedence. ++ +This is per-repository enhancement / version of global prefix-based +`@git_base_url_list` gitweb configuration variable (see +linkgit:gitweb.conf[5]). + +gitweb.owner:: + You can use the `gitweb.owner` repository configuration variable to set + repository's owner. It is displayed in the project list and summary + page. ++ +If it's not set, filesystem directory's owner is used (via GECOS field, +i.e. real name field from *getpwuid*(3)) if `$projects_list` is unset +(gitweb scans `$projectroot` for repositories); if `$projects_list` +points to file with list of repositories, then project owner defaults to +value from this file for given repository. + +various `gitweb.*` config variables (in config):: + Read description of `%feature` hash for detailed list, and descriptions. + See also "Configuring gitweb features" section in linkgit:gitweb.conf[5] + + +ACTIONS, AND URLS +----------------- +Gitweb can use path_info (component) based URLs, or it can pass all necessary +information via query parameters. The typical gitweb URLs are broken down in to +five components: + +----------------------------------------------------------------------- +.../gitweb.cgi/<repo>/<action>/<revision>:/<path>?<arguments> +----------------------------------------------------------------------- + +repo:: + The repository the action will be performed on. ++ +All actions except for those that list all available projects, +in whatever form, require this parameter. + +action:: + The action that will be run. Defaults to 'projects_list' if repo + is not set, and to 'summary' otherwise. + +revision:: + Revision shown. Defaults to HEAD. + +path:: + The path within the <repository> that the action is performed on, + for those actions that require it. + +arguments:: + Any arguments that control the behaviour of the action. + +Some actions require or allow to specify two revisions, and sometimes even two +pathnames. In most general form such path_info (component) based gitweb URL +looks like this: + +----------------------------------------------------------------------- +.../gitweb.cgi/<repo>/<action>/<revision_from>:/<path_from>..<revision_to>:/<path_to>?<arguments> +----------------------------------------------------------------------- + + +Each action is implemented as a subroutine, and must be present in %actions +hash. Some actions are disabled by default, and must be turned on via feature +mechanism. For example to enable 'blame' view add the following to gitweb +configuration file: + +----------------------------------------------------------------------- +$feature{'blame'}{'default'} = [1]; +----------------------------------------------------------------------- + + +Actions: +~~~~~~~~ +The standard actions are: + +project_list:: + Lists the available Git repositories. This is the default command if no + repository is specified in the URL. + +summary:: + Displays summary about given repository. This is the default command if + no action is specified in URL, and only repository is specified. + +heads:: +remotes:: + Lists all local or all remote-tracking branches in given repository. ++ +The latter is not available by default, unless configured. + +tags:: + List all tags (lightweight and annotated) in given repository. + +blob:: +tree:: + Shows the files and directories in a given repository path, at given + revision. This is default command if no action is specified in the URL, + and path is given. + +blob_plain:: + Returns the raw data for the file in given repository, at given path and + revision. Links to this action are marked 'raw'. + +blobdiff:: + Shows the difference between two revisions of the same file. + +blame:: +blame_incremental:: + Shows the blame (also called annotation) information for a file. On a + per line basis it shows the revision in which that line was last changed + and the user that committed the change. The incremental version (which + if configured is used automatically when JavaScript is enabled) uses + Ajax to incrementally add blame info to the contents of given file. ++ +This action is disabled by default for performance reasons. + +commit:: +commitdiff:: + Shows information about a specific commit in a repository. The 'commit' + view shows information about commit in more detail, the 'commitdiff' + action shows changeset for given commit. + +patch:: + Returns the commit in plain text mail format, suitable for applying with + linkgit:git-am[1]. + +tag:: + Display specific annotated tag (tag object). + +log:: +shortlog:: + Shows log information (commit message or just commit subject) for a + given branch (starting from given revision). ++ +The 'shortlog' view is more compact; it shows one commit per line. + +history:: + Shows history of the file or directory in a given repository path, + starting from given revision (defaults to HEAD, i.e. default branch). ++ +This view is similar to 'shortlog' view. + +rss:: +atom:: + Generates an RSS (or Atom) feed of changes to repository. + + +WEBSERVER CONFIGURATION +----------------------- +This section explains how to configure some common webservers to run gitweb. In +all cases, `/path/to/gitweb` in the examples is the directory you ran installed +gitweb in, and contains `gitweb_config.perl`. + +If you've configured a web server that isn't listed here for gitweb, please send +in the instructions so they can be included in a future release. + +Apache as CGI +~~~~~~~~~~~~~ +Apache must be configured to support CGI scripts in the directory in +which gitweb is installed. Let's assume that it is '/var/www/cgi-bin' +directory. + +----------------------------------------------------------------------- +ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" + +<Directory "/var/www/cgi-bin"> + Options Indexes FollowSymlinks ExecCGI + AllowOverride None + Order allow,deny + Allow from all +</Directory> +----------------------------------------------------------------------- + +With that configuration the full path to browse repositories would be: + + http://server/cgi-bin/gitweb.cgi + +Apache with mod_perl, via ModPerl::Registry +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You can use mod_perl with gitweb. You must install Apache::Registry +(for mod_perl 1.x) or ModPerl::Registry (for mod_perl 2.x) to enable +this support. + +Assuming that gitweb is installed to '/var/www/perl', the following +Apache configuration (for mod_perl 2.x) is suitable. + +----------------------------------------------------------------------- +Alias /perl "/var/www/perl" + +<Directory "/var/www/perl"> + SetHandler perl-script + PerlResponseHandler ModPerl::Registry + PerlOptions +ParseHeaders + Options Indexes FollowSymlinks +ExecCGI + AllowOverride None + Order allow,deny + Allow from all +</Directory> +----------------------------------------------------------------------- + +With that configuration the full path to browse repositories would be: + + http://server/perl/gitweb.cgi + +Apache with FastCGI +~~~~~~~~~~~~~~~~~~~ +Gitweb works with Apache and FastCGI. First you need to rename, copy +or symlink gitweb.cgi to gitweb.fcgi. Let's assume that gitweb is +installed in '/usr/share/gitweb' directory. The following Apache +configuration is suitable (UNTESTED!) + +----------------------------------------------------------------------- +FastCgiServer /usr/share/gitweb/gitweb.cgi +ScriptAlias /gitweb /usr/share/gitweb/gitweb.cgi + +Alias /gitweb/static /usr/share/gitweb/static +<Directory /usr/share/gitweb/static> + SetHandler default-handler +</Directory> +----------------------------------------------------------------------- + +With that configuration the full path to browse repositories would be: + + http://server/gitweb + + +ADVANCED WEB SERVER SETUP +------------------------- +All of those examples use request rewriting, and need `mod_rewrite` +(or equivalent; examples below are written for Apache). + +Single URL for gitweb and for fetching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you want to have one URL for both gitweb and your `http://` +repositories, you can configure Apache like this: + +----------------------------------------------------------------------- +<VirtualHost *:80> + ServerName git.example.org + DocumentRoot /pub/git + SetEnv GITWEB_CONFIG /etc/gitweb.conf + + # turning on mod rewrite + RewriteEngine on + + # make the front page an internal rewrite to the gitweb script + RewriteRule ^/$ /cgi-bin/gitweb.cgi + + # make access for "dumb clients" work + RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ \ + /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] +</VirtualHost> +----------------------------------------------------------------------- + +The above configuration expects your public repositories to live under +'/pub/git' and will serve them as `http://git.domain.org/dir-under-pub-git`, +both as cloneable GIT URL and as browseable gitweb interface. If you then +start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all` +then you can even use the `git://` URL with exactly the same path. + +Setting the environment variable `GITWEB_CONFIG` will tell gitweb to use the +named file (i.e. in this example '/etc/gitweb.conf') as a configuration for +gitweb. You don't really need it in above example; it is required only if +your configuration file is in different place than built-in (during +compiling gitweb) 'gitweb_config.perl' or '/etc/gitweb.conf'. See +linkgit:gitweb.conf[5] for details, especially information about precedence +rules. + +If you use the rewrite rules from the example you *might* also need +something like the following in your gitweb configuration file +('/etc/gitweb.conf' following example): +---------------------------------------------------------------------------- +@stylesheets = ("/some/absolute/path/gitweb.css"); +$my_uri = "/"; +$home_link = "/"; +$per_request_config = 1; +---------------------------------------------------------------------------- +Nowadays though gitweb should create HTML base tag when needed (to set base +URI for relative links), so it should work automatically. + + +Webserver configuration with multiple projects' root +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you want to use gitweb with several project roots you can edit your +Apache virtual host and gitweb configuration files in the following way. + +The virtual host configuration (in Apache configuration file) should look +like this: +-------------------------------------------------------------------------- +<VirtualHost *:80> + ServerName git.example.org + DocumentRoot /pub/git + SetEnv GITWEB_CONFIG /etc/gitweb.conf + + # turning on mod rewrite + RewriteEngine on + + # make the front page an internal rewrite to the gitweb script + RewriteRule ^/$ /cgi-bin/gitweb.cgi [QSA,L,PT] + + # look for a public_git folder in unix users' home + # http://git.example.org/~<user>/ + RewriteRule ^/\~([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \ + [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT] + + # http://git.example.org/+<user>/ + #RewriteRule ^/\+([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \ + [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT] + + # http://git.example.org/user/<user>/ + #RewriteRule ^/user/([^\/]+)/(gitweb.cgi)?$ /cgi-bin/gitweb.cgi \ + [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT] + + # defined list of project roots + RewriteRule ^/scm(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \ + [QSA,E=GITWEB_PROJECTROOT:/pub/scm/,L,PT] + RewriteRule ^/var(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \ + [QSA,E=GITWEB_PROJECTROOT:/var/git/,L,PT] + + # make access for "dumb clients" work + RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ \ + /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] +</VirtualHost> +-------------------------------------------------------------------------- + +Here actual project root is passed to gitweb via `GITWEB_PROJECT_ROOT` +environment variable from a web server, so you need to put the following +line in gitweb configuration file ('/etc/gitweb.conf' in above example): +-------------------------------------------------------------------------- +$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git"; +-------------------------------------------------------------------------- +*Note* that this requires to be set for each request, so either +`$per_request_config` must be false, or the above must be put in code +referenced by `$per_request_config`; + +These configurations enable two things. First, each unix user (`<user>`) of +the server will be able to browse through gitweb git repositories found in +'~/public_git/' with the following url: + + http://git.example.org/~<user>/ + +If you do not want this feature on your server just remove the second +rewrite rule. + +If you already use `mod_userdir` in your virtual host or you don't want to +use the \'~' as first character, just comment or remove the second rewrite +rule, and uncomment one of the following according to what you want. + +Second, repositories found in '/pub/scm/' and '/var/git/' will be accessible +through `http://git.example.org/scm/` and `http://git.example.org/var/`. +You can add as many project roots as you want by adding rewrite rules like +the third and the fourth. + + +PATH_INFO usage +~~~~~~~~~~~~~~~ +If you enable PATH_INFO usage in gitweb by putting +---------------------------------------------------------------------------- +$feature{'pathinfo'}{'default'} = [1]; +---------------------------------------------------------------------------- +in your gitweb configuration file, it is possible to set up your server so +that it consumes and produces URLs in the form + + http://git.example.com/project.git/shortlog/sometag + +i.e. without 'gitweb.cgi' part, by using a configuration such as the +following. This configuration assumes that '/var/www/gitweb' is the +DocumentRoot of your webserver, contains the gitweb.cgi script and +complementary static files (stylesheet, favicon, JavaScript): + +---------------------------------------------------------------------------- +<VirtualHost *:80> + ServerAlias git.example.com + + DocumentRoot /var/www/gitweb + + <Directory /var/www/gitweb> + Options ExecCGI + AddHandler cgi-script cgi + + DirectoryIndex gitweb.cgi + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^.* /gitweb.cgi/$0 [L,PT] + </Directory> +</VirtualHost> +---------------------------------------------------------------------------- +The rewrite rule guarantees that existing static files will be properly +served, whereas any other URL will be passed to gitweb as PATH_INFO +parameter. + +*Notice* that in this case you don't need special settings for +`@stylesheets`, `$my_uri` and `$home_link`, but you lose "dumb client" +access to your project .git dirs (described in "Single URL for gitweb and +for fetching" section). A possible workaround for the latter is the +following: in your project root dir (e.g. '/pub/git') have the projects +named *without* a .git extension (e.g. '/pub/git/project' instead of +'/pub/git/project.git') and configure Apache as follows: +---------------------------------------------------------------------------- +<VirtualHost *:80> + ServerAlias git.example.com + + DocumentRoot /var/www/gitweb + + AliasMatch ^(/.*?)(\.git)(/.*)?$ /pub/git$1$3 + <Directory /var/www/gitweb> + Options ExecCGI + AddHandler cgi-script cgi + + DirectoryIndex gitweb.cgi + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^.* /gitweb.cgi/$0 [L,PT] + </Directory> +</VirtualHost> +---------------------------------------------------------------------------- + +The additional AliasMatch makes it so that + + http://git.example.com/project.git + +will give raw access to the project's git dir (so that the project can be +cloned), while + + http://git.example.com/project + +will provide human-friendly gitweb access. + +This solution is not 100% bulletproof, in the sense that if some project has +a named ref (branch, tag) starting with 'git/', then paths such as + + http://git.example.com/project/command/abranch..git/abranch + +will fail with a 404 error. + + +BUGS +---- +Please report any bugs or feature requests to git@vger.kernel.org, +putting "gitweb" in the subject of email. + +SEE ALSO +-------- +linkgit:gitweb.conf[5], linkgit:git-instaweb[1] + +'gitweb/README', 'gitweb/INSTALL' + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index b613d4ed08..6bd0b041c3 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -7,6 +7,12 @@ With --no-commit perform the merge but pretend the merge failed and do not autocommit, to give the user a chance to inspect and further tweak the merge result before committing. +--edit:: +-e:: ++ + Invoke editor before committing successful merge to further + edit the default merge message. + --ff:: --no-ff:: Do not generate a merge commit if the merge resolved as @@ -11,6 +11,7 @@ #include "cache.h" #include "exec_cmd.h" #include "attr.h" +#include "dir.h" const char git_attr__true[] = "(builtin)true"; const char git_attr__false[] = "\0(builtin)false"; @@ -20,8 +21,6 @@ static const char git_attr__unknown[] = "(builtin)unknown"; #define ATTR__UNSET NULL #define ATTR__UNKNOWN git_attr__unknown -static const char *attributes_file; - /* This is a randomly chosen prime. */ #define HASHSIZE 257 @@ -494,14 +493,6 @@ static int git_attr_system(void) return !git_env_bool("GIT_ATTR_NOSYSTEM", 0); } -static int git_attr_config(const char *var, const char *value, void *dummy) -{ - if (!strcmp(var, "core.attributesfile")) - return git_config_pathname(&attributes_file, var, value); - - return 0; -} - static void bootstrap_attr_stack(void) { if (!attr_stack) { @@ -521,9 +512,8 @@ static void bootstrap_attr_stack(void) } } - git_config(git_attr_config, NULL); - if (attributes_file) { - elem = read_attr_from_file(attributes_file, 1); + if (git_attributes_file) { + elem = read_attr_from_file(git_attributes_file, 1); if (elem) { elem->origin = NULL; elem->prev = attr_stack; @@ -533,7 +523,7 @@ static void bootstrap_attr_stack(void) if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { elem = read_attr(GITATTRIBUTES_FILE, 1); - elem->origin = strdup(""); + elem->origin = xstrdup(""); elem->prev = attr_stack; attr_stack = elem; debug_push(elem); @@ -552,7 +542,6 @@ static void prepare_attr_stack(const char *path) { struct attr_stack *elem, *info; int dirlen, len; - struct strbuf pathbuf; const char *cp; cp = strrchr(path, '/'); @@ -561,8 +550,6 @@ static void prepare_attr_stack(const char *path) else dirlen = cp - path; - strbuf_init(&pathbuf, dirlen+2+strlen(GITATTRIBUTES_FILE)); - /* * At the bottom of the attribute stack is the built-in * set of attribute definitions, followed by the contents @@ -607,27 +594,28 @@ static void prepare_attr_stack(const char *path) * Read from parent directories and push them down */ if (!is_bare_repository() || direction == GIT_ATTR_INDEX) { - while (1) { - char *cp; + struct strbuf pathbuf = STRBUF_INIT; + while (1) { len = strlen(attr_stack->origin); if (dirlen <= len) break; - strbuf_reset(&pathbuf); - strbuf_add(&pathbuf, path, dirlen); + cp = memchr(path + len + 1, '/', dirlen - len - 1); + if (!cp) + cp = path + dirlen; + strbuf_add(&pathbuf, path, cp - path); strbuf_addch(&pathbuf, '/'); - cp = strchr(pathbuf.buf + len + 1, '/'); - strcpy(cp + 1, GITATTRIBUTES_FILE); + strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE); elem = read_attr(pathbuf.buf, 0); - *cp = '\0'; - elem->origin = strdup(pathbuf.buf); + strbuf_setlen(&pathbuf, cp - path); + elem->origin = strbuf_detach(&pathbuf, NULL); elem->prev = attr_stack; attr_stack = elem; debug_push(elem); } - } - strbuf_release(&pathbuf); + strbuf_release(&pathbuf); + } /* * Finally push the "info" one at the top of the stack. @@ -644,7 +632,7 @@ static int path_matches(const char *pathname, int pathlen, /* match basename */ const char *basename = strrchr(pathname, '/'); basename = basename ? basename + 1 : pathname; - return (fnmatch(pattern, basename, 0) == 0); + return (fnmatch_icase(pattern, basename, 0) == 0); } /* * match with FNM_PATHNAME; the pattern has base implicitly @@ -658,7 +646,7 @@ static int path_matches(const char *pathname, int pathlen, return 0; if (baselen != 0) baselen++; - return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0; + return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0; } static int macroexpand_one(int attr_nr, int rem); diff --git a/builtin/apply.c b/builtin/apply.c index 694f55dc5a..84a8a0b521 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1407,6 +1407,9 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc "%d leading pathname components (line %d)" , p_value, linenr); patch->old_name = patch->new_name = patch->def_name; } + if (!patch->is_delete && !patch->new_name) + die("git diff header lacks filename information " + "(line %d)", linenr); patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; diff --git a/builtin/check-attr.c b/builtin/check-attr.c index ded0d836d3..44c421eb0f 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -94,6 +94,8 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) struct git_attr_check *check; int cnt, i, doubledash, filei; + git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, check_attr_options, check_attr_usage, PARSE_OPT_KEEP_DASHDASH); diff --git a/builtin/checkout.c b/builtin/checkout.c index 49a547a0d5..2a80772425 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -72,7 +72,7 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, hashcpy(ce->sha1, sha1); memcpy(ce->name, base, baselen); memcpy(ce->name + baselen, pathname, len - baselen); - ce->ce_flags = create_ce_flags(len, 0); + ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE; ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; @@ -229,6 +229,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; + if (source_tree && !(ce->ce_flags & CE_UPDATE)) + continue; match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched); } @@ -267,6 +269,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, state.refresh_cache = 1; for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; + if (source_tree && !(ce->ce_flags & CE_UPDATE)) + continue; if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, NULL); diff --git a/builtin/config.c b/builtin/config.c index 0b4ecac855..0315ad76f8 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -99,6 +99,7 @@ static int show_config(const char *key_, const char *value_, void *cb) const char *vptr = value; int must_free_vptr = 0; int dup_error = 0; + int must_print_delim = 0; if (!use_key_regexp && strcmp(key_, key)) return 0; @@ -109,10 +110,8 @@ static int show_config(const char *key_, const char *value_, void *cb) return 0; if (show_keys) { - if (value_) - printf("%s%c", key_, key_delim); - else - printf("%s", key_); + printf("%s", key_); + must_print_delim = 1; } if (seen && !do_all) dup_error = 1; @@ -130,16 +129,23 @@ static int show_config(const char *key_, const char *value_, void *cb) } else if (types == TYPE_PATH) { git_config_pathname(&vptr, key_, value_); must_free_vptr = 1; + } else if (value_) { + vptr = value_; + } else { + /* Just show the key name */ + vptr = ""; + must_print_delim = 0; } - else - vptr = value_?value_:""; seen++; if (dup_error) { error("More than one value for the key %s: %s", key_, vptr); } - else + else { + if (must_print_delim) + printf("%c", key_delim); printf("%s%c", vptr, term); + } if (must_free_vptr) /* If vptr must be freed, it's a pointer to a * dynamically allocated buffer, it's safe to cast to diff --git a/builtin/fetch.c b/builtin/fetch.c index 7a4e41cca7..1adf6c176f 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -379,8 +379,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, url = xstrdup("foreign"); rm = ref_map; - if (check_everything_connected(iterate_ref_map, 0, &rm)) - return error(_("%s did not send all necessary objects\n"), url); + if (check_everything_connected(iterate_ref_map, 0, &rm)) { + rc = error(_("%s did not send all necessary objects\n"), url); + goto abort; + } for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; @@ -462,12 +464,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, fprintf(stderr, " %s\n", note); } } - free(url); - fclose(fp); + if (rc & STORE_REF_ERROR_DF_CONFLICT) error(_("some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting " "branches"), remote_name); + + abort: + free(url); + fclose(fp); return rc; } diff --git a/builtin/merge.c b/builtin/merge.c index 7d593097de..dffd5ec124 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -46,7 +46,7 @@ static const char * const builtin_merge_usage[] = { static int show_diffstat = 1, shortlog_len, squash; static int option_commit = 1, allow_fast_forward = 1; -static int fast_forward_only; +static int fast_forward_only, option_edit; static int allow_trivial = 1, have_message; static struct strbuf merge_msg; static struct commit_list *remoteheads; @@ -189,6 +189,8 @@ static struct option builtin_merge_options[] = { "create a single commit instead of doing a merge"), OPT_BOOLEAN(0, "commit", &option_commit, "perform a commit if the merge succeeds (default)"), + OPT_BOOLEAN('e', "edit", &option_edit, + "edit message before committing"), OPT_BOOLEAN(0, "ff", &allow_fast_forward, "allow fast-forward (default)"), OPT_BOOLEAN(0, "ff-only", &fast_forward_only, @@ -843,30 +845,54 @@ static void add_strategies(const char *string, unsigned attr) } -static void write_merge_msg(void) +static void write_merge_msg(struct strbuf *msg) { int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno(_("Could not open '%s' for writing"), git_path("MERGE_MSG")); - if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len) + if (write_in_full(fd, msg->buf, msg->len) != msg->len) die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG")); close(fd); } -static void read_merge_msg(void) +static void read_merge_msg(struct strbuf *msg) { - strbuf_reset(&merge_msg); - if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0) + strbuf_reset(msg); + if (strbuf_read_file(msg, git_path("MERGE_MSG"), 0) < 0) die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG")); } -static void run_prepare_commit_msg(void) +static void write_merge_state(void); +static void abort_commit(const char *err_msg) { - write_merge_msg(); + if (err_msg) + error("%s", err_msg); + fprintf(stderr, + _("Not committing merge; use 'git commit' to complete the merge.\n")); + write_merge_state(); + exit(1); +} + +static void prepare_to_commit(void) +{ + struct strbuf msg = STRBUF_INIT; + strbuf_addbuf(&msg, &merge_msg); + strbuf_addch(&msg, '\n'); + write_merge_msg(&msg); run_hook(get_index_file(), "prepare-commit-msg", git_path("MERGE_MSG"), "merge", NULL, NULL); - read_merge_msg(); + if (option_edit) { + if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) + abort_commit(NULL); + } + read_merge_msg(&msg); + stripspace(&msg, option_edit); + if (!msg.len) + abort_commit(_("Empty commit message.")); + strbuf_release(&merge_msg); + strbuf_addbuf(&merge_msg, &msg); + strbuf_release(&msg); } static int merge_trivial(struct commit *head) @@ -880,7 +906,7 @@ static int merge_trivial(struct commit *head) parent->next = xmalloc(sizeof(*parent->next)); parent->next->item = remoteheads->item; parent->next->next = NULL; - run_prepare_commit_msg(); + prepare_to_commit(); commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL); finish(head, result_commit, "In-index merge"); drop_save(); @@ -909,9 +935,9 @@ static int finish_automerge(struct commit *head, for (j = remoteheads; j; j = j->next) pptr = &commit_list_insert(j->item, pptr)->next; } - free_commit_list(remoteheads); strbuf_addch(&merge_msg, '\n'); - run_prepare_commit_msg(); + prepare_to_commit(); + free_commit_list(remoteheads); commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); finish(head, result_commit, buf.buf); @@ -1018,6 +1044,36 @@ static int setup_with_upstream(const char ***argv) return i; } +static void write_merge_state(void) +{ + int fd; + struct commit_list *j; + struct strbuf buf = STRBUF_INIT; + + for (j = remoteheads; j; j = j->next) + strbuf_addf(&buf, "%s\n", + sha1_to_hex(j->item->object.sha1)); + fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666); + if (fd < 0) + die_errno(_("Could not open '%s' for writing"), + git_path("MERGE_HEAD")); + if (write_in_full(fd, buf.buf, buf.len) != buf.len) + die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD")); + close(fd); + strbuf_addch(&merge_msg, '\n'); + write_merge_msg(&merge_msg); + fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) + die_errno(_("Could not open '%s' for writing"), + git_path("MERGE_MODE")); + strbuf_reset(&buf); + if (!allow_fast_forward) + strbuf_addf(&buf, "no-ff"); + if (write_in_full(fd, buf.buf, buf.len) != buf.len) + die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE")); + close(fd); +} + int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; @@ -1423,33 +1479,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (squash) finish(head_commit, NULL, NULL); - else { - int fd; - struct commit_list *j; - - for (j = remoteheads; j; j = j->next) - strbuf_addf(&buf, "%s\n", - sha1_to_hex(j->item->object.sha1)); - fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666); - if (fd < 0) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_HEAD")); - if (write_in_full(fd, buf.buf, buf.len) != buf.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD")); - close(fd); - strbuf_addch(&merge_msg, '\n'); - write_merge_msg(); - fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) - die_errno(_("Could not open '%s' for writing"), - git_path("MERGE_MODE")); - strbuf_reset(&buf); - if (!allow_fast_forward) - strbuf_addf(&buf, "no-ff"); - if (write_in_full(fd, buf.buf, buf.len) != buf.len) - die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE")); - close(fd); - } + else + write_merge_state(); if (merge_was_ok) { fprintf(stderr, _("Automatic merge went well; " diff --git a/builtin/mv.c b/builtin/mv.c index 40f33ca4d0..5efe6c5760 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -29,7 +29,11 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec, to_copy--; if (to_copy != length || base_name) { char *it = xmemdupz(result[i], to_copy); - result[i] = base_name ? strdup(basename(it)) : it; + if (base_name) { + result[i] = xstrdup(basename(it)); + free(it); + } else + result[i] = it; } } return get_pathspec(prefix, result); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 2b18de5dc3..ba3705d1de 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -804,6 +804,10 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, off_t offset = find_pack_entry_one(sha1, p); if (offset) { if (!found_pack) { + if (!is_pack_valid(p)) { + warning("packfile %s cannot be accessed", p->pack_name); + continue; + } found_offset = offset; found_pack = p; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index e2d3f94661..261b610d24 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -154,7 +154,8 @@ static void write_head_info(void) struct command { struct command *next; const char *error_string; - unsigned int skip_update; + unsigned int skip_update:1, + did_not_exist:1; unsigned char old_sha1[20]; unsigned char new_sha1[20]; char ref_name[FLEX_ARRAY]; /* more */ @@ -264,6 +265,7 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta struct receive_hook_feed_state { struct command *cmd; + int skip_broken; struct strbuf buf; }; @@ -272,7 +274,8 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) struct receive_hook_feed_state *state = state_; struct command *cmd = state->cmd; - while (cmd && cmd->error_string) + while (cmd && + state->skip_broken && (cmd->error_string || cmd->did_not_exist)) cmd = cmd->next; if (!cmd) return -1; /* EOF */ @@ -288,13 +291,15 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) return 0; } -static int run_receive_hook(struct command *commands, const char *hook_name) +static int run_receive_hook(struct command *commands, const char *hook_name, + int skip_broken) { struct receive_hook_feed_state state; int status; strbuf_init(&state.buf, 0); state.cmd = commands; + state.skip_broken = skip_broken; if (feed_receive_hook(&state, NULL, NULL)) return 0; state.cmd = commands; @@ -485,8 +490,13 @@ static const char *update(struct command *cmd) if (is_null_sha1(new_sha1)) { if (!parse_object(old_sha1)) { - rp_warning("Allowing deletion of corrupt ref."); old_sha1 = NULL; + if (ref_exists(name)) { + rp_warning("Allowing deletion of corrupt ref."); + } else { + rp_warning("Deleting a non-existent ref."); + cmd->did_not_exist = 1; + } } if (delete_ref(namespaced_name, old_sha1, 0)) { rp_error("failed to delete %s", name); @@ -517,7 +527,7 @@ static void run_update_post_hook(struct command *commands) struct child_process proc; for (argc = 0, cmd = commands; cmd; cmd = cmd->next) { - if (cmd->error_string) + if (cmd->error_string || cmd->did_not_exist) continue; argc++; } @@ -528,7 +538,7 @@ static void run_update_post_hook(struct command *commands) for (argc = 1, cmd = commands; cmd; cmd = cmd->next) { char *p; - if (cmd->error_string) + if (cmd->error_string || cmd->did_not_exist) continue; p = xmalloc(strlen(cmd->ref_name) + 1); strcpy(p, cmd->ref_name); @@ -672,7 +682,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro 0, &cmd)) set_connectivity_errors(commands); - if (run_receive_hook(commands, pre_receive_hook)) { + if (run_receive_hook(commands, pre_receive_hook, 0)) { for (cmd = commands; cmd; cmd = cmd->next) cmd->error_string = "pre-receive hook declined"; return; @@ -940,7 +950,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) unlink_or_warn(pack_lockfile); if (report_status) report(commands, unpack_status); - run_receive_hook(commands, post_receive_hook); + run_receive_hook(commands, post_receive_hook, 1); run_update_post_hook(commands); if (auto_gc) { const char *argv_gc_auto[] = { diff --git a/builtin/revert.c b/builtin/revert.c index 010508d571..87df70edc3 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -302,7 +302,7 @@ static void write_cherry_pick_head(struct commit *commit) strbuf_release(&buf); } -static void print_advice(void) +static void print_advice(int show_hint) { char *msg = getenv("GIT_CHERRY_PICK_HELP"); @@ -317,9 +317,11 @@ static void print_advice(void) return; } - advise("after resolving the conflicts, mark the corrected paths"); - advise("with 'git add <paths>' or 'git rm <paths>'"); - advise("and commit the result with 'git commit'"); + if (show_hint) { + advise("after resolving the conflicts, mark the corrected paths"); + advise("with 'git add <paths>' or 'git rm <paths>'"); + advise("and commit the result with 'git commit'"); + } } static void write_message(struct strbuf *msgbuf, const char *filename) @@ -564,8 +566,6 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); strbuf_addstr(&msgbuf, ")\n"); } - if (!opts->no_commit) - write_cherry_pick_head(commit); } if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REVERT) { @@ -586,13 +586,22 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) free_commit_list(remotes); } + /* + * If the merge was clean or if it failed due to conflict, we write + * CHERRY_PICK_HEAD for the subsequent invocation of commit to use. + * However, if the merge did not even start, then we don't want to + * write it at all. + */ + if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1)) + write_cherry_pick_head(commit); + if (res) { error(opts->action == REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), msg.subject); - print_advice(); + print_advice(res == 1); rerere(opts->allow_rerere_auto); } else { if (!opts->no_commit) @@ -23,50 +23,102 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name, list->nr++; } -/* returns an fd */ -int read_bundle_header(const char *path, struct bundle_header *header) +/* Eventually this should go to strbuf.[ch] */ +static int strbuf_readline_fd(struct strbuf *sb, int fd) { - char buffer[1024]; - int fd; - long fpos; - FILE *ffd = fopen(path, "rb"); + strbuf_reset(sb); + + while (1) { + char ch; + ssize_t len = xread(fd, &ch, 1); + if (len < 0) + return -1; + strbuf_addch(sb, ch); + if (ch == '\n') + break; + } + return 0; +} - if (!ffd) - return error("could not open '%s'", path); - if (!fgets(buffer, sizeof(buffer), ffd) || - strcmp(buffer, bundle_signature)) { - fclose(ffd); - return error("'%s' does not look like a v2 bundle file", path); +static int parse_bundle_header(int fd, struct bundle_header *header, + const char *report_path) +{ + struct strbuf buf = STRBUF_INIT; + int status = 0; + + /* The bundle header begins with the signature */ + if (strbuf_readline_fd(&buf, fd) || + strcmp(buf.buf, bundle_signature)) { + if (report_path) + error("'%s' does not look like a v2 bundle file", + report_path); + status = -1; + goto abort; } - while (fgets(buffer, sizeof(buffer), ffd) - && buffer[0] != '\n') { - int is_prereq = buffer[0] == '-'; - int offset = is_prereq ? 1 : 0; - int len = strlen(buffer); + + /* The bundle header ends with an empty line */ + while (!strbuf_readline_fd(&buf, fd) && + buf.len && buf.buf[0] != '\n') { unsigned char sha1[20]; - struct ref_list *list = is_prereq ? &header->prerequisites - : &header->references; - char delim; - - if (len && buffer[len - 1] == '\n') - buffer[len - 1] = '\0'; - if (get_sha1_hex(buffer + offset, sha1)) { - warning("unrecognized header: %s", buffer); - continue; + int is_prereq = 0; + + if (*buf.buf == '-') { + is_prereq = 1; + strbuf_remove(&buf, 0, 1); + } + strbuf_rtrim(&buf); + + /* + * Tip lines have object name, SP, and refname. + * Prerequisites have object name that is optionally + * followed by SP and subject line. + */ + if (get_sha1_hex(buf.buf, sha1) || + (40 <= buf.len && !isspace(buf.buf[40])) || + (!is_prereq && buf.len <= 40)) { + if (report_path) + error("unrecognized header: %s%s (%d)", + (is_prereq ? "-" : ""), buf.buf, (int)buf.len); + status = -1; + break; + } else { + if (is_prereq) + add_to_ref_list(sha1, "", &header->prerequisites); + else + add_to_ref_list(sha1, buf.buf + 41, &header->references); } - delim = buffer[40 + offset]; - if (!isspace(delim) && (delim != '\0' || !is_prereq)) - die ("invalid header: %s", buffer); - add_to_ref_list(sha1, isspace(delim) ? - buffer + 41 + offset : "", list); } - fpos = ftell(ffd); - fclose(ffd); - fd = open(path, O_RDONLY); + + abort: + if (status) { + close(fd); + fd = -1; + } + strbuf_release(&buf); + return fd; +} + +int read_bundle_header(const char *path, struct bundle_header *header) +{ + int fd = open(path, O_RDONLY); + if (fd < 0) return error("could not open '%s'", path); - lseek(fd, fpos, SEEK_SET); - return fd; + return parse_bundle_header(fd, header, path); +} + +int is_bundle(const char *path, int quiet) +{ + struct bundle_header header; + int fd = open(path, O_RDONLY); + + if (fd < 0) + return 0; + memset(&header, 0, sizeof(header)); + fd = parse_bundle_header(fd, &header, quiet ? NULL : path); + if (fd >= 0) + close(fd); + return (fd >= 0); } static int list_refs(struct ref_list *r, int argc, const char **argv) @@ -14,6 +14,7 @@ struct bundle_header { struct ref_list references; }; +int is_bundle(const char *path, int quiet); int read_bundle_header(const char *path, struct bundle_header *header); int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv); @@ -168,6 +168,7 @@ struct cache_entry { unsigned int ce_flags; unsigned char sha1[20]; struct cache_entry *next; + struct cache_entry *dir_next; char name[FLEX_ARRAY]; /* more */ }; @@ -589,6 +590,7 @@ extern int warn_ambiguous_refs; extern int shared_repository; extern const char *apply_default_whitespace; extern const char *apply_default_ignorewhitespace; +extern const char *git_attributes_file; extern int zlib_compression_level; extern int core_compression_level; extern int core_compression_seen; @@ -734,7 +736,7 @@ int safe_create_leading_directories(char *path); int safe_create_leading_directories_const(const char *path); int mkdir_in_gitdir(const char *path); extern char *expand_user_path(const char *path); -char *enter_repo(char *path, int strict); +const char *enter_repo(const char *path, int strict); static inline int is_absolute_path(const char *path) { return is_dir_sep(path[0]) || has_dos_drive_prefix(path); @@ -1055,6 +1057,7 @@ extern struct packed_git *add_packed_git(const char *, int, int); extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t); extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t); extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *); +extern int is_pack_valid(struct packed_git *); extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *); extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); diff --git a/command-list.txt b/command-list.txt index 95bf18cf06..a36ee9b015 100644 --- a/command-list.txt +++ b/command-list.txt @@ -130,5 +130,6 @@ git-upload-pack synchelpers git-var plumbinginterrogators git-verify-pack plumbinginterrogators git-verify-tag ancillaryinterrogators +gitweb ancillaryinterrogators git-whatchanged ancillaryinterrogators git-write-tree plumbingmanipulators diff --git a/compat/mingw.c b/compat/mingw.c index 6ef0cc4f99..efdc703257 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1183,7 +1183,7 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service, } ai->ai_addrlen = sizeof(struct sockaddr_in); if (hints && (hints->ai_flags & AI_CANONNAME)) - ai->ai_canonname = h ? strdup(h->h_name) : NULL; + ai->ai_canonname = h ? xstrdup(h->h_name) : NULL; else ai->ai_canonname = NULL; @@ -1321,6 +1321,13 @@ static void ensure_socket_initialization(void) initialized = 1; } +#undef gethostname +int mingw_gethostname(char *name, int namelen) +{ + ensure_socket_initialization(); + return gethostname(name, namelen); +} + #undef gethostbyname struct hostent *mingw_gethostbyname(const char *host) { diff --git a/compat/mingw.h b/compat/mingw.h index ce9dd980eb..fecf0d0776 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -190,6 +190,9 @@ char *mingw_getcwd(char *pointer, int len); char *mingw_getenv(const char *name); #define getenv mingw_getenv +int mingw_gethostname(char *host, int namelen); +#define gethostname mingw_gethostname + struct hostent *mingw_gethostbyname(const char *host); #define gethostbyname mingw_gethostbyname diff --git a/compat/qsort.c b/compat/qsort.c index d93dce2cf8..9574d537bd 100644 --- a/compat/qsort.c +++ b/compat/qsort.c @@ -55,7 +55,7 @@ void git_qsort(void *b, size_t n, size_t s, msort_with_tmp(b, n, s, cmp, buf); } else { /* It's somewhat large, so malloc it. */ - char *tmp = malloc(size); + char *tmp = xmalloc(size); msort_with_tmp(b, n, s, cmp, tmp); free(tmp); } diff --git a/compat/win32/syslog.c b/compat/win32/syslog.c index 42b95a9b51..d015e436d5 100644 --- a/compat/win32/syslog.c +++ b/compat/win32/syslog.c @@ -1,5 +1,4 @@ #include "../../git-compat-util.h" -#include "../../strbuf.h" static HANDLE ms_eventlog; @@ -16,13 +15,8 @@ void openlog(const char *ident, int logopt, int facility) void syslog(int priority, const char *fmt, ...) { - struct strbuf sb = STRBUF_INIT; - struct strbuf_expand_dict_entry dict[] = { - {"1", "% 1"}, - {NULL, NULL} - }; WORD logtype; - char *str; + char *str, *pos; int str_len; va_list ap; @@ -39,11 +33,24 @@ void syslog(int priority, const char *fmt, ...) } str = malloc(str_len + 1); + if (!str) { + warning("malloc failed: '%s'", strerror(errno)); + return; + } + va_start(ap, fmt); vsnprintf(str, str_len + 1, fmt, ap); va_end(ap); - strbuf_expand(&sb, str, strbuf_expand_dict_cb, &dict); - free(str); + + while ((pos = strstr(str, "%1")) != NULL) { + str = realloc(str, ++str_len + 1); + if (!str) { + warning("realloc failed: '%s'", strerror(errno)); + return; + } + memmove(pos + 2, pos + 1, strlen(pos)); + pos[1] = ' '; + } switch (priority) { case LOG_EMERG: @@ -66,7 +73,6 @@ void syslog(int priority, const char *fmt, ...) } ReportEventA(ms_eventlog, logtype, 0, 0, NULL, 1, 0, - (const char **)&sb.buf, NULL); - - strbuf_release(&sb); + (const char **)&str, NULL); + free(str); } @@ -491,6 +491,9 @@ static int git_default_core_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.attributesfile")) + return git_config_pathname(&git_attributes_file, var, value); + if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); return 0; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8648a36e7b..888e8e10cc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1259,12 +1259,9 @@ _git_commit () " "" "${cur##--cleanup=}" return ;; - --reuse-message=*) - __gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}" - return - ;; - --reedit-message=*) - __gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}" + --reuse-message=*|--reedit-message=*|\ + --fixup=*|--squash=*) + __gitcomp "$(__git_refs)" "" "${cur#*=}" return ;; --untracked-files=*) @@ -1278,7 +1275,7 @@ _git_commit () --dry-run --reuse-message= --reedit-message= --reset-author --file= --message= --template= --cleanup= --untracked-files --untracked-files= - --verbose --quiet + --verbose --quiet --fixup= --squash= " return esac @@ -1556,14 +1553,9 @@ _git_log () merge="--merge" fi case "$cur" in - --pretty=*) - __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) - " "" "${cur##--pretty=}" - return - ;; - --format=*) + --pretty=*|--format=*) __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) - " "" "${cur##--format=}" + " "" "${cur#*=}" return ;; --date=*) @@ -1671,11 +1663,9 @@ _git_notes () ;; esac ;; - add,--reuse-message=*|append,--reuse-message=*) - __gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}" - ;; + add,--reuse-message=*|append,--reuse-message=*|\ add,--reedit-message=*|append,--reedit-message=*) - __gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}" + __gitcomp "$(__git_refs)" "" "${cur#*=}" ;; add,--*|append,--*) __gitcomp '--file= --message= --reedit-message= @@ -1733,7 +1723,7 @@ _git_push () --*) __gitcomp " --all --mirror --tags --dry-run --force --verbose - --receive-pack= --repo= + --receive-pack= --repo= --set-upstream " return ;; @@ -2370,14 +2360,9 @@ _git_show () __git_has_doubledash && return case "$cur" in - --pretty=*) - __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) - " "" "${cur##--pretty=}" - return - ;; - --format=*) + --pretty=*|--format=*) __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases) - " "" "${cur##--format=}" + " "" "${cur#*=}" return ;; --*) @@ -20,6 +20,7 @@ static int log_syslog; static int verbose; static int reuseaddr; +static int informative_errors; static const char daemon_usage[] = "git daemon [--verbose] [--syslog] [--export-all]\n" @@ -108,11 +109,11 @@ static void NORETURN daemon_die(const char *err, va_list params) exit(1); } -static char *path_ok(char *directory) +static const char *path_ok(char *directory) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; - char *path; + const char *path; char *dir; dir = directory; @@ -247,6 +248,14 @@ static int git_daemon_config(const char *var, const char *value, void *cb) return 0; } +static int daemon_error(const char *dir, const char *msg) +{ + if (!informative_errors) + msg = "access denied or repository not exported"; + packet_write(1, "ERR %s: %s", msg, dir); + return -1; +} + static int run_service(char *dir, struct daemon_service *service) { const char *path; @@ -257,11 +266,11 @@ static int run_service(char *dir, struct daemon_service *service) if (!enabled && !service->overridable) { logerror("'%s': service not enabled.", service->name); errno = EACCES; - goto failed; + return daemon_error(dir, "service not enabled"); } if (!(path = path_ok(dir))) - goto failed; + return daemon_error(dir, "no such repository"); /* * Security on the cheap. @@ -277,7 +286,7 @@ static int run_service(char *dir, struct daemon_service *service) if (!export_all_trees && access("git-daemon-export-ok", F_OK)) { logerror("'%s': repository not exported.", path); errno = EACCES; - goto failed; + return daemon_error(dir, "repository not exported"); } if (service->overridable) { @@ -291,7 +300,7 @@ static int run_service(char *dir, struct daemon_service *service) logerror("'%s': service not enabled for '%s'", service->name, path); errno = EACCES; - goto failed; + return daemon_error(dir, "service not enabled"); } /* @@ -301,10 +310,6 @@ static int run_service(char *dir, struct daemon_service *service) signal(SIGTERM, SIG_IGN); return service->fn(); - -failed: - packet_write(1, "ERR %s: access denied", dir); - return -1; } static void copy_to_log(int fd) @@ -1208,6 +1213,14 @@ int main(int argc, char **argv) make_service_overridable(arg + 18, 0); continue; } + if (!prefixcmp(arg, "--informative-errors")) { + informative_errors = 1; + continue; + } + if (!prefixcmp(arg, "--no-informative-errors")) { + informative_errors = 0; + continue; + } if (!strcmp(arg, "--")) { ok_paths = &argv[i+1]; break; @@ -2169,6 +2169,8 @@ static void builtin_diff(const char *name_a, xecfg.ctxlen = o->context; xecfg.interhunkctxlen = o->interhunkcontext; xecfg.flags = XDL_EMIT_FUNCNAMES; + if (DIFF_OPT_TST(o, FUNCCONTEXT)) + xecfg.flags |= XDL_EMIT_FUNCCONTEXT; if (pe) xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); if (!diffopts) @@ -3536,6 +3538,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (opt_arg(arg, '\0', "inter-hunk-context", &options->interhunkcontext)) ; + else if (!strcmp(arg, "-W")) + DIFF_OPT_SET(options, FUNCCONTEXT); + else if (!strcmp(arg, "--function-context")) + DIFF_OPT_SET(options, FUNCCONTEXT); + else if (!strcmp(arg, "--no-function-context")) + DIFF_OPT_CLR(options, FUNCCONTEXT); else if ((argcount = parse_long_opt("output", av, &optarg))) { options->file = fopen(optarg, "w"); if (!options->file) @@ -79,6 +79,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data) #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26) #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27) #define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28) +#define DIFF_OPT_FUNCCONTEXT (1 << 29) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c index c3760cfefd..380a837b5b 100644 --- a/diffcore-pickaxe.c +++ b/diffcore-pickaxe.c @@ -8,6 +8,46 @@ #include "xdiff-interface.h" #include "kwset.h" +typedef int (*pickaxe_fn)(struct diff_filepair *p, struct diff_options *o, regex_t *regexp, kwset_t kws); + +static void pickaxe(struct diff_queue_struct *q, struct diff_options *o, + regex_t *regexp, kwset_t kws, pickaxe_fn fn) +{ + int i; + struct diff_queue_struct outq; + + DIFF_QUEUE_CLEAR(&outq); + + if (o->pickaxe_opts & DIFF_PICKAXE_ALL) { + /* Showing the whole changeset if needle exists */ + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + if (fn(p, o, regexp, kws)) + return; /* do not munge the queue */ + } + + /* + * Otherwise we will clear the whole queue by copying + * the empty outq at the end of this function, but + * first clear the current entries in the queue. + */ + for (i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); + } else { + /* Showing only the filepairs that has the needle */ + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + if (fn(p, o, regexp, kws)) + diff_q(&outq, p); + else + diff_free_filepair(p); + } + } + + free(q->queue); + *q = outq; +} + struct diffgrep_cb { regex_t *regexp; int hit; @@ -45,7 +85,8 @@ static void fill_one(struct diff_filespec *one, } } -static int diff_grep(struct diff_filepair *p, regex_t *regexp, struct diff_options *o) +static int diff_grep(struct diff_filepair *p, struct diff_options *o, + regex_t *regexp, kwset_t kws) { regmatch_t regmatch; struct userdiff_driver *textconv_one = NULL; @@ -95,12 +136,8 @@ static int diff_grep(struct diff_filepair *p, regex_t *regexp, struct diff_optio static void diffcore_pickaxe_grep(struct diff_options *o) { - struct diff_queue_struct *q = &diff_queued_diff; - int i, has_changes, err; + int err; regex_t regex; - struct diff_queue_struct outq; - outq.queue = NULL; - outq.nr = outq.alloc = 0; err = regcomp(®ex, o->pickaxe, REG_EXTENDED | REG_NEWLINE); if (err) { @@ -110,51 +147,21 @@ static void diffcore_pickaxe_grep(struct diff_options *o) die("invalid log-grep regex: %s", errbuf); } - if (o->pickaxe_opts & DIFF_PICKAXE_ALL) { - /* Showing the whole changeset if needle exists */ - for (i = has_changes = 0; !has_changes && i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (diff_grep(p, ®ex, o)) - has_changes++; - } - if (has_changes) - return; /* do not munge the queue */ - - /* - * Otherwise we will clear the whole queue by copying - * the empty outq at the end of this function, but - * first clear the current entries in the queue. - */ - for (i = 0; i < q->nr; i++) - diff_free_filepair(q->queue[i]); - } else { - /* Showing only the filepairs that has the needle */ - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (diff_grep(p, ®ex, o)) - diff_q(&outq, p); - else - diff_free_filepair(p); - } - } + pickaxe(&diff_queued_diff, o, ®ex, NULL, diff_grep); regfree(®ex); - - free(q->queue); - *q = outq; return; } -static unsigned int contains(struct diff_filespec *one, - const char *needle, unsigned long len, +static unsigned int contains(struct diff_filespec *one, struct diff_options *o, regex_t *regexp, kwset_t kws) { unsigned int cnt; unsigned long sz; const char *data; - if (diff_populate_filespec(one, 0)) + if (!o->pickaxe[0]) return 0; - if (!len) + if (diff_populate_filespec(one, 0)) return 0; sz = one->size; @@ -176,14 +183,15 @@ static unsigned int contains(struct diff_filespec *one, } else { /* Classic exact string match */ while (sz) { - size_t offset = kwsexec(kws, data, sz, NULL); + struct kwsmatch kwsm; + size_t offset = kwsexec(kws, data, sz, &kwsm); const char *found; if (offset == -1) break; else found = data + offset; - sz -= found - data + len; - data = found + len; + sz -= found - data + kwsm.size[0]; + data = found + kwsm.size[0]; cnt++; } } @@ -191,17 +199,31 @@ static unsigned int contains(struct diff_filespec *one, return cnt; } +static int has_changes(struct diff_filepair *p, struct diff_options *o, + regex_t *regexp, kwset_t kws) +{ + if (!DIFF_FILE_VALID(p->one)) { + if (!DIFF_FILE_VALID(p->two)) + return 0; /* ignore unmerged */ + /* created */ + return contains(p->two, o, regexp, kws) != 0; + } + if (!DIFF_FILE_VALID(p->two)) + return contains(p->one, o, regexp, kws) != 0; + if (!diff_unmodified_pair(p)) { + return contains(p->one, o, regexp, kws) != + contains(p->two, o, regexp, kws); + } + return 0; +} + static void diffcore_pickaxe_count(struct diff_options *o) { const char *needle = o->pickaxe; int opts = o->pickaxe_opts; - struct diff_queue_struct *q = &diff_queued_diff; unsigned long len = strlen(needle); - int i, has_changes; regex_t regex, *regexp = NULL; kwset_t kws = NULL; - struct diff_queue_struct outq; - DIFF_QUEUE_CLEAR(&outq); if (opts & DIFF_PICKAXE_REGEX) { int err; @@ -220,72 +242,12 @@ static void diffcore_pickaxe_count(struct diff_options *o) kwsprep(kws); } - if (opts & DIFF_PICKAXE_ALL) { - /* Showing the whole changeset if needle exists */ - for (i = has_changes = 0; !has_changes && i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (!DIFF_FILE_VALID(p->one)) { - if (!DIFF_FILE_VALID(p->two)) - continue; /* ignore unmerged */ - /* created */ - if (contains(p->two, needle, len, regexp, kws)) - has_changes++; - } - else if (!DIFF_FILE_VALID(p->two)) { - if (contains(p->one, needle, len, regexp, kws)) - has_changes++; - } - else if (!diff_unmodified_pair(p) && - contains(p->one, needle, len, regexp, kws) != - contains(p->two, needle, len, regexp, kws)) - has_changes++; - } - if (has_changes) - return; /* not munge the queue */ - - /* otherwise we will clear the whole queue - * by copying the empty outq at the end of this - * function, but first clear the current entries - * in the queue. - */ - for (i = 0; i < q->nr; i++) - diff_free_filepair(q->queue[i]); - } - else - /* Showing only the filepairs that has the needle */ - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - has_changes = 0; - if (!DIFF_FILE_VALID(p->one)) { - if (!DIFF_FILE_VALID(p->two)) - ; /* ignore unmerged */ - /* created */ - else if (contains(p->two, needle, len, regexp, - kws)) - has_changes = 1; - } - else if (!DIFF_FILE_VALID(p->two)) { - if (contains(p->one, needle, len, regexp, kws)) - has_changes = 1; - } - else if (!diff_unmodified_pair(p) && - contains(p->one, needle, len, regexp, kws) != - contains(p->two, needle, len, regexp, kws)) - has_changes = 1; - - if (has_changes) - diff_q(&outq, p); - else - diff_free_filepair(p); - } + pickaxe(&diff_queued_diff, o, regexp, kws, has_changes); if (opts & DIFF_PICKAXE_REGEX) regfree(®ex); else kwsfree(kws); - - free(q->queue); - *q = outq; return; } diff --git a/environment.c b/environment.c index 8174b703c4..0bee6a7a88 100644 --- a/environment.c +++ b/environment.c @@ -29,6 +29,7 @@ const char *git_log_output_encoding; int shared_repository = PERM_UMASK; const char *apply_default_whitespace; const char *apply_default_ignorewhitespace; +const char *git_attributes_file; int zlib_compression_level = Z_BEST_SPEED; int core_compression_level; int core_compression_seen; diff --git a/git-bisect.sh b/git-bisect.sh index 2524060475..99efbe8845 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -126,7 +126,8 @@ bisect_start() { start_head=$(cat "$GIT_DIR/BISECT_START") if test "z$mode" != "z--no-checkout" then - git checkout "$start_head" -- + git checkout "$start_head" -- || + die "$(eval_gettext "Checking out '\$start_head' failed. Try 'git bisect reset <validbranch>'.")" fi else # Get rev from where we start. diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh index 8452890be9..e6558d1010 100755 --- a/git-difftool--helper.sh +++ b/git-difftool--helper.sh @@ -43,12 +43,15 @@ launch_merge_tool () { printf "\nViewing: '$MERGED'\n" if use_ext_cmd then - printf "Hit return to launch '%s': " \ + printf "Launch '%s' [Y/n]: " \ "$GIT_DIFFTOOL_EXTCMD" else - printf "Hit return to launch '%s': " "$merge_tool" + printf "Launch '%s' [Y/n]: " "$merge_tool" + fi + if read ans && test "$ans" = n + then + return fi - read ans fi if use_ext_cmd diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index fd6a43d0a2..f8971603f7 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -854,6 +854,7 @@ set default_config(gui.fontdiff) [font configure font_diff] # TODO: this option should be added to the git-config documentation set default_config(gui.maxfilesdisplayed) 5000 set default_config(gui.usettk) 1 +set default_config(gui.warndetachedcommit) 1 set font_descs { {fontui font_ui {mc "Main Font"}} {fontdiff font_diff {mc "Diff/Console Font"}} @@ -1526,7 +1527,7 @@ proc run_prepare_commit_msg_hook {} { # prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an - # empty file but existant file. + # empty file but existent file. set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a] diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 61e358f960..691941e959 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -22,6 +22,7 @@ field w_asim ; # text column: annotations (simple computation) field w_file ; # text column: actual file data field w_cviewer ; # pane showing commit message field finder ; # find mini-dialog frame +field gotoline ; # line goto mini-dialog frame field status ; # status mega-widget instance field old_height ; # last known height of $w.file_pane @@ -231,6 +232,11 @@ constructor new {i_commit i_path i_jump} { -column [expr {[llength $w_columns] - 1}] \ ] + set gotoline [::linebar::new \ + $w.file_pane.out.lf $w_file \ + -column [expr {[llength $w_columns] - 1}] \ + ] + set w_cviewer $w.file_pane.cm.t text $w_cviewer \ -background white \ @@ -274,7 +280,11 @@ constructor new {i_commit i_path i_jump} { $w.ctxm add command \ -label [mc "Find Text..."] \ -accelerator F7 \ - -command [list searchbar::show $finder] + -command [cb _show_finder] + $w.ctxm add command \ + -label [mc "Goto Line..."] \ + -accelerator "Ctrl-G" \ + -command [cb _show_linebar] menu $w.ctxm.enc build_encoding_menu $w.ctxm.enc [cb _setencoding] $w.ctxm add cascade \ @@ -341,10 +351,13 @@ constructor new {i_commit i_path i_jump} { bind $w_cviewer <Tab> "[list focus $w_file];break" bind $w_cviewer <Button-1> [list focus $w_cviewer] bind $w_file <Visibility> [cb _focus_search $w_file] - bind $top <F7> [list searchbar::show $finder] + bind $top <F7> [cb _show_finder] + bind $top <Key-slash> [cb _show_finder] + bind $top <Control-Key-s> [cb _show_finder] bind $top <Escape> [list searchbar::hide $finder] bind $top <F3> [list searchbar::find_next $finder] bind $top <Shift-F3> [list searchbar::find_prev $finder] + bind $top <Control-Key-g> [cb _show_linebar] catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] } grid configure $w.header -sticky ew @@ -1298,9 +1311,9 @@ method _position_tooltip {} { set pos_y [expr {[winfo pointery .] + 10}] set g "${req_w}x${req_h}" - if {$pos_x >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +} append g $pos_x - if {$pos_y >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +} append g $pos_y wm geometry $tooltip_wm $g @@ -1336,4 +1349,14 @@ method _resize {new_height} { set old_height $new_height } +method _show_finder {} { + linebar::hide $gotoline + searchbar::show $finder +} + +method _show_linebar {} { + searchbar::hide $finder + linebar::show $gotoline +} + } diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl index c12d5e1698..54c7957a66 100644 --- a/git-gui/lib/choose_rev.tcl +++ b/git-gui/lib/choose_rev.tcl @@ -610,9 +610,9 @@ method _position_tooltip {} { set pos_y [expr {[winfo pointery .] + 10}] set g "${req_w}x${req_h}" - if {$pos_x >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +} append g $pos_x - if {$pos_y >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +} append g $pos_y wm geometry $tooltip_wm $g diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 5ce46877bf..372bed9948 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -260,8 +260,23 @@ proc commit_prehook_wait {fd_ph curHEAD msg_p} { } proc commit_commitmsg {curHEAD msg_p} { + global is_detached repo_config global pch_error + if {$is_detached && $repo_config(gui.warndetachedcommit)} { + set msg [mc "You are about to commit on a detached head.\ +This is a potentially dangerous thing to do because if you switch\ +to another branch you will loose your changes and it can be difficult\ +to retrieve them later from the reflog. You should probably cancel this\ +commit and create a new branch to continue.\n\ +\n\ +Do you really want to proceed with your Commit?"] + if {[ask_popup $msg] ne yes} { + unlock_index + return + } + } + # -- Run the commit-msg hook. # set fd_ph [githook_read commit-msg $msg_p] diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 5d7bbf23ed..e38b647b71 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -356,12 +356,21 @@ proc do_add_all {} { global file_states set paths [list] + set unknown_paths [list] foreach path [array names file_states] { switch -glob -- [lindex $file_states($path) 0] { U? {continue} ?M - ?T - ?D {lappend paths $path} + ?O {lappend unknown_paths $path} + } + } + if {[llength $unknown_paths]} { + set reply [ask_popup [mc "There are unknown files do you also want +to stage those?"]] + if {$reply} { + set paths [concat $paths $unknown_paths] } } add_helper {Adding all changed files} $paths diff --git a/git-gui/lib/line.tcl b/git-gui/lib/line.tcl new file mode 100644 index 0000000000..c160012de6 --- /dev/null +++ b/git-gui/lib/line.tcl @@ -0,0 +1,81 @@ +# goto line number +# based on code from gitk, Copyright (C) Paul Mackerras + +class linebar { + +field w +field ctext + +field linenum {} + +constructor new {i_w i_text args} { + global use_ttk NS + set w $i_w + set ctext $i_text + + ${NS}::frame $w + ${NS}::label $w.l -text [mc "Goto Line:"] + entry $w.ent \ + -textvariable ${__this}::linenum \ + -background lightgreen \ + -validate key \ + -validatecommand [cb _validate %P] + ${NS}::button $w.bn -text [mc Go] -command [cb _goto] + + pack $w.l -side left + pack $w.bn -side right + pack $w.ent -side left -expand 1 -fill x + + eval grid conf $w -sticky we $args + grid remove $w + + trace add variable linenum write [cb _goto_cb] + bind $w.ent <Return> [cb _goto] + bind $w.ent <Escape> [cb hide] + + bind $w <Destroy> [list delete_this $this] + return $this +} + +method show {} { + if {![visible $this]} { + grid $w + } + focus -force $w.ent +} + +method hide {} { + if {[visible $this]} { + $w.ent delete 0 end + focus $ctext + grid remove $w + } +} + +method visible {} { + return [winfo ismapped $w] +} + +method editor {} { + return $w.ent +} + +method _validate {P} { + # only accept numbers as input + string is integer $P +} + +method _goto_cb {name ix op} { + after idle [cb _goto 1] +} + +method _goto {{nohide {0}}} { + if {$linenum ne {}} { + $ctext see $linenum.0 + if {!$nohide} { + hide $this + } + } +} + +} diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl index 7fdbf87bcd..ef3486f083 100644 --- a/git-gui/lib/search.tcl +++ b/git-gui/lib/search.tcl @@ -35,6 +35,8 @@ constructor new {i_w i_text args} { grid remove $w trace add variable searchstring write [cb _incrsearch_cb] + bind $w.ent <Return> [cb find_next] + bind $w.ent <Shift-Return> [cb find_prev] bind $w <Destroy> [list delete_this $this] return $this @@ -196,4 +198,4 @@ method scrolled {} { } } -}
\ No newline at end of file +} diff --git a/git-gui/po/README b/git-gui/po/README index 595bbf5dee..0f5837d48e 100644 --- a/git-gui/po/README +++ b/git-gui/po/README @@ -18,28 +18,23 @@ specialized so-called "po file editors" (e.g. emacs po-mode, KBabel, poedit, GTranslator --- any of them would work well). Please install them. -You would then need to clone the git-gui internationalization project -repository, so that you can work on it: +You would then need to clone the git-gui project repository and create +a feature branch to begin working: - $ git clone mob@repo.or.cz:/srv/git/git-gui/git-gui-i18n.git/ - $ cd git-gui-i18n - $ git checkout --track -b mob origin/mob - $ git config remote.origin.push mob + $ git clone git://repo.or.cz/git-gui.git + $ cd git-gui.git + $ git checkout -b my-translation -The "git checkout" command creates a 'mob' branch from upstream's -corresponding branch and makes it your current branch. You will be -working on this branch. - -The "git config" command records in your repository configuration file -that you would push "mob" branch to the upstream when you say "git -push". +The "git checkout" command creates a new branch to keep your work +isolated and to make it simple to post your patch series when +completed. You will be working on this branch. 2. Starting a new language. -In the git-gui-i18n directory is a po/ subdirectory. It has a -handful files whose names end with ".po". Is there a file that has -messages in your language? +In the git-gui directory is a po/ subdirectory. It has a handful of +files whose names end with ".po". Is there a file that has messages +in your language? If you do not know what your language should be named, you need to find it. This currently follows ISO 639-1 two letter codes: @@ -149,15 +144,18 @@ There is a trick to test your translation without first installing: $ make $ LANG=af ./git-gui.sh -When you are satisfied with your translation, commit your changes, and -push it back to the 'mob' branch: +When you are satisfied with your translation, commit your changes then submit +your patch series to the maintainer and the Git mailing list: $ edit po/af.po ... be sure to update Last-Translator: and ... PO-Revision-Date: lines. $ git add po/af.po - $ git commit -m 'Started Afrikaans translation.' - $ git push + $ git commit -s -m 'git-gui: added Afrikaans translation.' + $ git send-email --to 'git@vger.kernel.org' \ + --cc 'Pat Thoyts <patthoyts@users.sourceforge.net>' \ + --subject 'git-gui: Afrikaans translation' \ + master.. 3. Updating your translation. @@ -169,6 +167,7 @@ itself was updated and there are new messages that need translation. In any case, make sure you are up-to-date before starting your work: + $ git checkout master $ git pull In the former case, you will edit po/af.po (again, replace "af" with diff --git a/git-gui/po/sv.po b/git-gui/po/sv.po index 8bd3c5d75f..24cc4e3675 100644 --- a/git-gui/po/sv.po +++ b/git-gui/po/sv.po @@ -1714,7 +1714,7 @@ msgstr "" #: lib/index.tcl:30 msgid "Continue" -msgstr "Forstätt" +msgstr "Fortsätt" #: lib/index.tcl:33 msgid "Unlock Index" diff --git a/git-pull.sh b/git-pull.sh index 8c1370f81b..9868a0bfb4 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -11,7 +11,7 @@ OPTIONS_SPEC= . git-sh-setup . git-sh-i18n set_reflog_action "pull${1+ $*}" -require_work_tree +require_work_tree_exists cd_to_toplevel diff --git a/git-rebase.sh b/git-rebase.sh index 6759702c57..00ca7b99fe 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -63,7 +63,7 @@ skip! skip current patch and continue " . git-sh-setup set_reflog_action rebase -require_work_tree +require_work_tree_exists cd_to_toplevel LF=' diff --git a/git-send-email.perl b/git-send-email.perl index 6885dfa1b9..d491db92c9 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1098,7 +1098,7 @@ X-Mailer: git-send-email $gitversion $smtp_encryption = ''; # Send EHLO again to receive fresh # supported commands - $smtp->hello(); + $smtp->hello($smtp_domain); } else { die "Server does not support STARTTLS! ".$smtp->message; } diff --git a/git-svn.perl b/git-svn.perl index a0410f0554..b67fef0bf6 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -22,14 +22,13 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn'; $Git::SVN::Ra::_log_window_size = 100; $Git::SVN::_minimize_url = 'unset'; -if (! exists $ENV{SVN_SSH}) { - if (exists $ENV{GIT_SSH}) { - $ENV{SVN_SSH} = $ENV{GIT_SSH}; - if ($^O eq 'msys') { - $ENV{SVN_SSH} =~ s/\\/\\\\/g; - $ENV{SVN_SSH} =~ s/(.*)/"$1"/; - } - } +if (! exists $ENV{SVN_SSH} && exists $ENV{GIT_SSH}) { + $ENV{SVN_SSH} = $ENV{GIT_SSH}; +} + +if (exists $ENV{SVN_SSH} && $^O eq 'msys') { + $ENV{SVN_SSH} =~ s/\\/\\\\/g; + $ENV{SVN_SSH} =~ s/(.*)/"$1"/; } $Git::SVN::Log::TZ = $ENV{TZ}; @@ -87,14 +86,15 @@ my ($_stdin, $_help, $_edit, $_version, $_fetch_all, $_no_rebase, $_fetch_parent, $_merge, $_strategy, $_dry_run, $_local, $_prefix, $_no_checkout, $_url, $_verbose, - $_git_format, $_commit_url, $_tag, $_merge_info); + $_git_format, $_commit_url, $_tag, $_merge_info, $_interactive); $Git::SVN::_follow_parent = 1; $SVN::Git::Fetcher::_placeholder_filename = ".gitignore"; $_q ||= 0; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, 'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache, - 'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex ); + 'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex, + 'ignore-refs=s' => \$Git::SVN::Ra::_ignore_refs_regex ); my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'authors-file|A=s' => \$_authors, 'authors-prog=s' => \$_authors_prog, @@ -163,6 +163,7 @@ my %cmd = ( 'revision|r=i' => \$_revision, 'no-rebase' => \$_no_rebase, 'mergeinfo=s' => \$_merge_info, + 'interactive|i' => \$_interactive, %cmt_opts, %fc_opts } ], branch => [ \&cmd_branch, 'Create a branch in the SVN repository', @@ -256,6 +257,27 @@ my %cmd = ( {} ], ); +use Term::ReadLine; +package FakeTerm; +sub new { + my ($class, $reason) = @_; + return bless \$reason, shift; +} +sub readline { + my $self = shift; + die "Cannot use readline on FakeTerm: $$self"; +} +package main; + +my $term = eval { + $ENV{"GIT_SVN_NOTTY"} + ? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT + : new Term::ReadLine 'git-svn'; +}; +if ($@) { + $term = new FakeTerm "$@: going non-interactive"; +} + my $cmd; for (my $i = 0; $i < @ARGV; $i++) { if (defined $cmd{$ARGV[$i]}) { @@ -366,6 +388,36 @@ sub version { exit 0; } +sub ask { + my ($prompt, %arg) = @_; + my $valid_re = $arg{valid_re}; + my $default = $arg{default}; + my $resp; + my $i = 0; + + if ( !( defined($term->IN) + && defined( fileno($term->IN) ) + && defined( $term->OUT ) + && defined( fileno($term->OUT) ) ) ){ + return defined($default) ? $default : undef; + } + + while ($i++ < 10) { + $resp = $term->readline($prompt); + if (!defined $resp) { # EOF + print "\n"; + return defined $default ? $default : undef; + } + if ($resp eq '' and defined $default) { + return $default; + } + if (!defined $valid_re or $resp =~ /$valid_re/) { + return $resp; + } + } + return undef; +} + sub do_git_init_db { unless (-d $ENV{GIT_DIR}) { my @init_db = ('init'); @@ -388,9 +440,12 @@ sub do_git_init_db { command_noisy('config', "$pfx.$i", $icv{$i}); $set = $i; } - my $ignore_regex = \$SVN::Git::Fetcher::_ignore_regex; - command_noisy('config', "$pfx.ignore-paths", $$ignore_regex) - if defined $$ignore_regex; + my $ignore_paths_regex = \$SVN::Git::Fetcher::_ignore_regex; + command_noisy('config', "$pfx.ignore-paths", $$ignore_paths_regex) + if defined $$ignore_paths_regex; + my $ignore_refs_regex = \$Git::SVN::Ra::_ignore_refs_regex; + command_noisy('config', "$pfx.ignore-refs", $$ignore_refs_regex) + if defined $$ignore_refs_regex; if (defined $SVN::Git::Fetcher::_preserve_empty_dirs) { my $fname = \$SVN::Git::Fetcher::_placeholder_filename; @@ -746,6 +801,27 @@ sub cmd_dcommit { "If these changes depend on each other, re-running ", "without --no-rebase may be required." } + + if (defined $_interactive){ + my $ask_default = "y"; + foreach my $d (@$linear_refs){ + my ($fh, $ctx) = command_output_pipe(qw(show --summary), "$d"); + while (<$fh>){ + print $_; + } + command_close_pipe($fh, $ctx); + $_ = ask("Commit this patch to SVN? ([y]es (default)|[n]o|[q]uit|[a]ll): ", + valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i, + default => $ask_default); + die "Commit this patch reply required" unless defined $_; + if (/^[nq]/i) { + exit(0); + } elsif (/^a/i) { + last; + } + } + } + my $expect_url = $url; my $push_merge_info = eval { @@ -2119,6 +2195,8 @@ sub read_all_remotes { $r->{$1}->{url} = $2; } elsif (m!^(.+)\.pushurl=\s*(.*)\s*$!) { $r->{$1}->{pushurl} = $2; + } elsif (m!^(.+)\.ignore-refs=\s*(.*)\s*$!) { + $r->{$1}->{ignore_refs_regex} = $2; } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) { my ($remote, $t, $local_ref, $remote_ref) = ($1, $2, $3, $4); @@ -2155,6 +2233,16 @@ sub read_all_remotes { } } keys %$r; + foreach my $remote (keys %$r) { + foreach ( grep { defined $_ } + map { $r->{$remote}->{$_} } qw(branches tags) ) { + foreach my $rs ( @$_ ) { + $rs->{ignore_refs_regex} = + $r->{$remote}->{ignore_refs_regex}; + } + } + } + $r; } @@ -5310,7 +5398,7 @@ sub apply_diff { } package Git::SVN::Ra; -use vars qw/@ISA $config_dir $_log_window_size/; +use vars qw/@ISA $config_dir $_ignore_refs_regex $_log_window_size/; use strict; use warnings; my ($ra_invalid, $can_do_switch, %ignored_err, $RA); @@ -5768,6 +5856,17 @@ sub get_dir_globbed { @finalents; } +# return value: 0 -- don't ignore, 1 -- ignore +sub is_ref_ignored { + my ($g, $p) = @_; + my $refname = $g->{ref}->full_path($p); + return 1 if defined($g->{ignore_refs_regex}) && + $refname =~ m!$g->{ignore_refs_regex}!; + return 0 unless defined($_ignore_refs_regex); + return 1 if $refname =~ m!$_ignore_refs_regex!o; + return 0; +} + sub match_globs { my ($self, $exists, $paths, $globs, $r) = @_; @@ -5804,6 +5903,7 @@ sub match_globs { next unless /$g->{path}->{regex}/; my $p = $1; my $pathname = $g->{path}->full_path($p); + next if is_ref_ignored($g, $p); next if $exists->{$pathname}; next if ($self->check_path($pathname, $r) != $SVN::Node::dir); diff --git a/git.spec.in b/git.spec.in index 91c8462b7d..c562c62284 100644 --- a/git.spec.in +++ b/git.spec.in @@ -199,7 +199,11 @@ rm -rf $RPM_BUILD_ROOT %files -n gitweb %defattr(-,root,root) +%doc gitweb/README gitweb/INSTALL Documentation/*gitweb*.txt %{_datadir}/gitweb +%{!?_without_docs: %{_mandir}/man1/*gitweb*.1*} +%{!?_without_docs: %{_mandir}/man5/*gitweb*.5*} +%{!?_without_docs: %doc Documentation/*gitweb*.html } %files -n perl-Git -f perl-files %defattr(-,root,root) @@ -208,6 +212,9 @@ rm -rf $RPM_BUILD_ROOT # No files for you! %changelog +* Sun Sep 18 2011 Jakub Narebski <jnareb@gmail.com> +- Add gitweb manpages to 'gitweb' subpackage + * Wed Jun 30 2010 Junio C Hamano <gitster@pobox.com> - Add 'gitweb' subpackage. diff --git a/gitweb/INSTALL b/gitweb/INSTALL index f5efe7454c..d134ffe4c7 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -229,7 +229,7 @@ Gitweb config file ------------------ See also "Runtime gitweb configuration" section in README file -for gitweb (in gitweb/README). +for gitweb (in gitweb/README), and gitweb.conf(5) manpage. - You can configure gitweb further using the per-instance gitweb configuration file; by default this is a file named gitweb_config.perl in the same place as @@ -287,97 +287,19 @@ adding the following lines to your $GITWEB_CONFIG: Gitweb repositories ------------------- -- By default all git repositories under projectroot are visible and - available to gitweb. The list of projects is generated by default by - scanning the projectroot directory for git repositories (for object - databases to be more exact). - - You can provide a pre-generated list of [visible] repositories, - together with information about their owners (the project ownership - defaults to the owner of the repository directory otherwise), by setting - the GITWEB_LIST build configuration variable (or the $projects_list - variable in the gitweb config file) to point to a plain file. - - Each line of the projects list file should consist of the url-encoded path - to the project repository database (relative to projectroot), followed - by the url-encoded project owner on the same line (separated by a space). - Spaces in both project path and project owner have to be encoded as either - '%20' or '+'. - - Other characters that have to be url-encoded, i.e. replaced by '%' - followed by two-digit character number in octal, are: other whitespace - characters (because they are field separator in a record), plus sign '+' - (because it can be used as replacement for spaces), and percent sign '%' - (which is used for encoding / escaping). - - You can generate the projects list index file using the project_index - action (the 'TXT' link on projects list page) directly from gitweb. - -- By default, even if a project is not visible on projects list page, you - can view it nevertheless by hand-crafting a gitweb URL. You can set the - GITWEB_STRICT_EXPORT build configuration variable (or the $strict_export - variable in the gitweb config file) to only allow viewing of - repositories also shown on the overview page. - -- Alternatively, you can configure gitweb to only list and allow - viewing of the explicitly exported repositories, via the - GITWEB_EXPORT_OK build configuration variable (or the $export_ok - variable in gitweb config file). If it evaluates to true, gitweb - shows repositories only if this file exists in its object database - (if directory has the magic file named $export_ok). - -- Finally, it is possible to specify an arbitrary perl subroutine that - will be called for each project to determine if it can be exported. - The subroutine receives an absolute path to the project as its only - parameter. - - For example, if you use mod_perl to run the script, and have dumb - http protocol authentication configured for your repositories, you - can use the following hook to allow access only if the user is - authorized to read the files: - - $export_auth_hook = sub { - use Apache2::SubRequest (); - use Apache2::Const -compile => qw(HTTP_OK); - my $path = "$_[0]/HEAD"; - my $r = Apache2::RequestUtil->request; - my $sub = $r->lookup_file($path); - return $sub->filename eq $path - && $sub->status == Apache2::Const::HTTP_OK; - }; - - -Generating projects list using gitweb -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We assume that GITWEB_CONFIG has its default Makefile value, namely -gitweb_config.perl. Put the following in gitweb_make_index.perl file: - - $GITWEB_CONFIG = "gitweb_config.perl"; - do $GITWEB_CONFIG if -e $GITWEB_CONFIG; - - $projects_list = $projectroot; - -Then create the following script to get list of project in the format -suitable for GITWEB_LIST build configuration variable (or -$projects_list variable in gitweb config): - - #!/bin/sh - - export GITWEB_CONFIG="gitweb_make_index.perl" - export GATEWAY_INTERFACE="CGI/1.1" - export HTTP_ACCEPT="*/*" - export REQUEST_METHOD="GET" - export QUERY_STRING="a=project_index" - - perl -- /var/www/cgi-bin/gitweb.cgi +By default gitweb shows all git repositories under single common repository +root on a local filesystem; see description of GITWEB_PROJECTROOT build-time +configuration variable above (and also of GITWEB_LIST). + +More advanced usage, like limiting access or visibility of repositories and +managing multiple roots are described on gitweb manpage. Example web server configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -See also "Webserver configuration" section in README file for gitweb -(in gitweb/README). +See also "Webserver configuration" and "Advanced web server setup" sections +in gitweb(1) manpage. - Apache2, gitweb installed as CGI script, diff --git a/gitweb/README b/gitweb/README index a9988200d6..6da4778b73 100644 --- a/gitweb/README +++ b/gitweb/README @@ -7,9 +7,18 @@ The one working on: From the git version 1.4.0 gitweb is bundled with git. +Build time gitweb configuration +------------------------------- +There are many configuration variables which affect building gitweb (among +others creating gitweb.cgi out of gitweb.perl by replacing placeholders such +as `++GIT_BINDIR++` by their build-time values). + +Building and installing gitweb is described in gitweb's INSTALL file +(in 'gitweb/INSTALL'). + + Runtime gitweb configuration ---------------------------- - Gitweb obtains configuration data from the following sources in the following order: @@ -41,400 +50,22 @@ Ultimate description on how to reconfigure the default features setting in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found as comments inside 'gitweb.cgi'. -See also the "Gitweb config file" (with an example of config file), and -the "Gitweb repositories" sections in INSTALL file for gitweb. - - -The gitweb config file is a fragment of perl code. You can set variables -using "our $variable = value"; text from "#" character until the end -of a line is ignored. See perlsyn(1) man page for details. - -Below is the list of variables which you might want to set in gitweb config. -See the top of 'gitweb.cgi' for the full list of variables and their -descriptions. - -Gitweb config file variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can set, among others, the following variables in gitweb config files -(with the exception of $projectroot and $projects_list this list does -not include variables usually directly set during build): - * $GIT - Core git executable to use. By default set to "$GIT_BINDIR/git", which - in turn is by default set to "$(bindir)/git". If you use git from binary - package, set this to "/usr/bin/git". This can just be "git" if your - webserver has a sensible PATH. If you have multiple git versions - installed it can be used to choose which one to use. - * $version - Gitweb version, set automatically when creating gitweb.cgi from - gitweb.perl. You might want to modify it if you are running modified - gitweb. - * $projectroot - Absolute filesystem path which will be prepended to project path; - the path to repository is $projectroot/$project. Set to - $GITWEB_PROJECTROOT during installation. This variable have to be - set correctly for gitweb to find repositories. - * $projects_list - Source of projects list, either directory to scan, or text file - with list of repositories (in the "<URI-encoded repository path> SP - <URI-encoded repository owner>" line format; actually there can be - any sequence of whitespace in place of space (SP)). Set to - $GITWEB_LIST during installation. If empty, $projectroot is used - to scan for repositories. - * $my_url, $my_uri - Full URL and absolute URL of gitweb script; - in earlier versions of gitweb you might have need to set those - variables, now there should be no need to do it. See - $per_request_config if you need to set them still. - * $base_url - Base URL for relative URLs in pages generated by gitweb, - (e.g. $logo, $favicon, @stylesheets if they are relative URLs), - needed and used only for URLs with nonempty PATH_INFO via - <base href="$base_url">. Usually gitweb sets its value correctly, - and there is no need to set this variable, e.g. to $my_uri or "/". - See $per_request_config if you need to set it anyway. - * $home_link - Target of the home link on top of all pages (the first part of view - "breadcrumbs"). By default set to absolute URI of a page ($my_uri). - * @stylesheets - List of URIs of stylesheets (relative to base URI of a page). You - might specify more than one stylesheet, for example use gitweb.css - as base, with site specific modifications in separate stylesheet - to make it easier to upgrade gitweb. You can add 'site' stylesheet - for example by using - push @stylesheets, "gitweb-site.css"; - in the gitweb config file. - * $logo_url, $logo_label - URI and label (title) of GIT logo link (or your site logo, if you choose - to use different logo image). By default they point to git homepage; - in the past they pointed to git documentation at www.kernel.org. - * $projects_list_description_width - The width (in characters) of the projects list "Description" column. - Longer descriptions will be cut (trying to cut at word boundary); - full description is available as 'title' attribute (usually shown on - mouseover). By default set to 25, which might be too small if you - use long project descriptions. - * $projects_list_group_categories - Enables the grouping of projects by category on the project list page. - The category of a project is determined by the $GIT_DIR/category - file or the 'gitweb.category' variable in its repository configuration. - Disabled by default. - * $project_list_default_category - Default category for projects for which none is specified. If set - to the empty string, such projects will remain uncategorized and - listed at the top, above categorized projects. - * @git_base_url_list - List of git base URLs used for URL to where fetch project from, shown - in project summary page. Full URL is "$git_base_url/$project". - You can setup multiple base URLs (for example one for git:// protocol - access, and one for http:// "dumb" protocol access). Note that per - repository configuration in 'cloneurl' file, or as values of gitweb.url - project config. - * $default_blob_plain_mimetype - Default mimetype for blob_plain (raw) view, if mimetype checking - doesn't result in some other type; by default 'text/plain'. - * $default_text_plain_charset - Default charset for text files. If not set, web server configuration - would be used. - * $mimetypes_file - File to use for (filename extension based) guessing of MIME types before - trying /etc/mime.types. Path, if relative, is taken currently as - relative to the current git repository. - * $fallback_encoding - Gitweb assumes this charset if line contains non-UTF-8 characters. - Fallback decoding is used without error checking, so it can be even - 'utf-8'. Value must be valid encoding; see Encoding::Supported(3pm) man - page for a list. By default 'latin1', aka. 'iso-8859-1'. - * @diff_opts - Rename detection options for git-diff and git-diff-tree. By default - ('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or - set it to () if you don't want to have renames detection. - * $prevent_xss - If true, some gitweb features are disabled to prevent content in - repositories from launching cross-site scripting (XSS) attacks. Set this - to true if you don't trust the content of your repositories. The default - is false. - * $maxload - Used to set the maximum load that we will still respond to gitweb queries. - If server load exceed this value then return "503 Service Unavailable" error. - Server load is taken to be 0 if gitweb cannot determine its value. Set it to - undefined value to turn it off. The default is 300. - * $highlight_bin - Path to the highlight executable to use (must be the one from - http://www.andre-simon.de due to assumptions about parameters and output). - Useful if highlight is not installed on your webserver's PATH. - [Default: highlight] - * $per_request_config - If set to code reference, it would be run once per each request. You can - set parts of configuration that change per session, e.g. by setting it to - sub { $ENV{GL_USER} = $cgi->remote_user || "gitweb"; } - Otherwise it is treated as boolean value: if true gitweb would process - config file once per request, if false it would process config file only - once. Note: $my_url, $my_uri, and $base_url are overwritten with - their default values before every request, so if you want to change - them, be sure to set this variable to true or a code reference effecting - the desired changes. The default is true. - -Projects list file format -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Instead of having gitweb find repositories by scanning filesystem starting -from $projectroot (or $projects_list, if it points to directory), you can -provide list of projects by setting $projects_list to a text file with list -of projects (and some additional info). This file uses the following -format: - -One record (for project / repository) per line, whitespace separated fields; -does not support (at least for now) lines continuation (newline escaping). -Leading and trailing whitespace are ignored, any run of whitespace can be -used as field separator (rules for Perl's "split(' ', $line)"). Keyed by -the first field, which is project name, i.e. path to repository GIT_DIR -relative to $projectroot. Fields use modified URI encoding, defined in -RFC 3986, section 2.1 (Percent-Encoding), or rather "Query string encoding" -(see http://en.wikipedia.org/wiki/Query_string#URL_encoding), the difference -being that SP (' ') can be encoded as '+' (and therefore '+' has to be also -percent-encoded). Reserved characters are: '%' (used for encoding), '+' -(can be used to encode SPACE), all whitespace characters as defined in Perl, -including SP, TAB and LF, (used to separate fields in a record). - -Currently list of fields is - * <repository path> - path to repository GIT_DIR, relative to $projectroot - * <repository owner> - displayed as repository owner, preferably full name, - or email, or both - -You can additionally use $projects_list file to limit which repositories -are visible, and together with $strict_export to limit access to -repositories (see "Gitweb repositories" section in gitweb/INSTALL). - - -Per-repository gitweb configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can also configure individual repositories shown in gitweb by creating -file in the GIT_DIR of git repository, or by setting some repo configuration -variable (in GIT_DIR/config). - -You can use the following files in repository: - * README.html - A .html file (HTML fragment) which is included on the gitweb project - summary page inside <div> block element. You can use it for longer - description of a project, to provide links (for example to project's - homepage), etc. This is recognized only if XSS prevention is off - ($prevent_xss is false); a way to include a readme safely when XSS - prevention is on may be worked out in the future. - * description (or gitweb.description) - Short (shortened by default to 25 characters in the projects list page) - single line description of a project (of a repository). Plain text file; - HTML will be escaped. By default set to - Unnamed repository; edit this file to name it for gitweb. - from the template during repository creation. You can use the - gitweb.description repo configuration variable, but the file takes - precedence. - * category (or gitweb.category) - Singe line category of a project, used to group projects if - $projects_list_group_categories is enabled. By default (file and - configuration variable absent), uncategorized projects are put in - the $project_list_default_category category. You can use the - gitweb.category repo configuration variable, but the file takes - precedence. - * cloneurl (or multiple-valued gitweb.url) - File with repository URL (used for clone and fetch), one per line. - Displayed in the project summary page. You can use multiple-valued - gitweb.url repository configuration variable for that, but the file - takes precedence. - * gitweb.owner - You can use the gitweb.owner repository configuration variable to set - repository's owner. It is displayed in the project list and summary - page. If it's not set, filesystem directory's owner is used - (via GECOS field / real name field from getpwiud(3)). - * various gitweb.* config variables (in config) - Read description of %feature hash for detailed list, and some - descriptions. - - -Webserver configuration ------------------------ - -If you want to have one URL for both gitweb and your http:// -repositories, you can configure apache like this: - -<VirtualHost *:80> - ServerName git.example.org - DocumentRoot /pub/git - SetEnv GITWEB_CONFIG /etc/gitweb.conf - - # turning on mod rewrite - RewriteEngine on - - # make the front page an internal rewrite to the gitweb script - RewriteRule ^/$ /cgi-bin/gitweb.cgi - - # make access for "dumb clients" work - RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] -</VirtualHost> - -The above configuration expects your public repositories to live under -/pub/git and will serve them as http://git.domain.org/dir-under-pub-git, -both as cloneable GIT URL and as browseable gitweb interface. -If you then start your git-daemon with --base-path=/pub/git --export-all -then you can even use the git:// URL with exactly the same path. - -Setting the environment variable GITWEB_CONFIG will tell gitweb to use -the named file (i.e. in this example /etc/gitweb.conf) as a -configuration for gitweb. Perl variables defined in here will -override the defaults given at the head of the gitweb.perl (or -gitweb.cgi). Look at the comments in that file for information on -which variables and what they mean. - -If you use the rewrite rules from the example you'll likely also need -something like the following in your gitweb.conf (or gitweb_config.perl) file: - - @stylesheets = ("/some/absolute/path/gitweb.css"); - $my_uri = "/"; - $home_link = "/"; - - -Webserver configuration with multiple projects' root ----------------------------------------------------- - -If you want to use gitweb with several project roots you can edit your apache -virtual host and gitweb.conf configuration files like this : - -virtual host configuration : - -<VirtualHost *:80> - ServerName git.example.org - DocumentRoot /pub/git - SetEnv GITWEB_CONFIG /etc/gitweb.conf - - # turning on mod rewrite - RewriteEngine on - - # make the front page an internal rewrite to the gitweb script - RewriteRule ^/$ /cgi-bin/gitweb.cgi [QSA,L,PT] - - # look for a public_git folder in unix users' home - # http://git.example.org/~<user>/ - RewriteRule ^/\~([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT] - - # http://git.example.org/+<user>/ - #RewriteRule ^/\+([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT] - - # http://git.example.org/user/<user>/ - #RewriteRule ^/user/([^\/]+)/(gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT] - - # defined list of project roots - RewriteRule ^/scm(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/pub/scm/,L,PT] - RewriteRule ^/var(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/var/git/,L,PT] - - # make access for "dumb clients" work - RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] -</VirtualHost> - -gitweb.conf configuration : - -$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git"; - -These configurations enable two things. First, each unix user (<user>) of the -server will be able to browse through gitweb git repositories found in -~/public_git/ with the following url : http://git.example.org/~<user>/ - -If you do not want this feature on your server just remove the second rewrite rule. - -If you already use mod_userdir in your virtual host or you don't want to use -the '~' as first character just comment or remove the second rewrite rule and -uncomment one of the following according to what you want. - -Second, repositories found in /pub/scm/ and /var/git/ will be accesible -through http://git.example.org/scm/ and http://git.example.org/var/. -You can add as many project roots as you want by adding rewrite rules like the -third and the fourth. - - -PATH_INFO usage ------------------------ -If you enable PATH_INFO usage in gitweb by putting - - $feature{'pathinfo'}{'default'} = [1]; - -in your gitweb.conf, it is possible to set up your server so that it -consumes and produces URLs in the form - -http://git.example.com/project.git/shortlog/sometag - -by using a configuration such as the following, that assumes that -/var/www/gitweb is the DocumentRoot of your webserver, and that it -contains the gitweb.cgi script and complementary static files -(stylesheet, favicon): - -<VirtualHost *:80> - ServerAlias git.example.com - - DocumentRoot /var/www/gitweb - - <Directory /var/www/gitweb> - Options ExecCGI - AddHandler cgi-script cgi - - DirectoryIndex gitweb.cgi - - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^.* /gitweb.cgi/$0 [L,PT] - </Directory> -</VirtualHost> - -The rewrite rule guarantees that existing static files will be properly -served, whereas any other URL will be passed to gitweb as PATH_INFO -parameter. - -Notice that in this case you don't need special settings for -@stylesheets, $my_uri and $home_link, but you lose "dumb client" access -to your project .git dirs. A possible workaround for the latter is the -following: in your project root dir (e.g. /pub/git) have the projects -named without a .git extension (e.g. /pub/git/project instead of -/pub/git/project.git) and configure Apache as follows: - -<VirtualHost *:80> - ServerAlias git.example.com - - DocumentRoot /var/www/gitweb - - AliasMatch ^(/.*?)(\.git)(/.*)?$ /pub/git$1$3 - <Directory /var/www/gitweb> - Options ExecCGI - AddHandler cgi-script cgi - - DirectoryIndex gitweb.cgi - - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^.* /gitweb.cgi/$0 [L,PT] - </Directory> -</VirtualHost> - -The additional AliasMatch makes it so that - -http://git.example.com/project.git - -will give raw access to the project's git dir (so that the project can -be cloned), while - -http://git.example.com/project - -will provide human-friendly gitweb access. - -This solution is not 100% bulletproof, in the sense that if some project -has a named ref (branch, tag) starting with 'git/', then paths such as +See also gitweb.conf(5) manpage. -http://git.example.com/project/command/abranch..git/abranch -will fail with a 404 error. +Web server configuration +------------------------ +Gitweb can be run as CGI script, as legacy mod_perl application (using +ModPerl::Registry), and as FastCGI script. You can find some simple examples +in "Example web server configuration" section in INSTALL file for gitweb (in +gitweb/INSTALL). +See "Webserver configuration" and "Advanced web server setup" sections in +gitweb(1) manpage. +AUTHORS +------- Originally written by: Kay Sievers <kay.sievers@vrfy.org> diff --git a/http-fetch.c b/http-fetch.c index 8c4c5d2224..69299b7bd2 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -67,7 +67,7 @@ int main(int argc, const char **argv) git_config(git_default_config, NULL); - http_init(NULL); + http_init(NULL, url); walker = get_http_walker(url); walker->get_tree = get_tree; walker->get_history = get_history; diff --git a/http-push.c b/http-push.c index 44f814cda2..5d01be9344 100644 --- a/http-push.c +++ b/http-push.c @@ -1747,7 +1747,6 @@ int main(int argc, char **argv) int i; int new_refs; struct ref *ref, *local_refs; - struct remote *remote; git_extract_argv0_path(argv[0]); @@ -1821,14 +1820,7 @@ int main(int argc, char **argv) memset(remote_dir_exists, -1, 256); - /* - * Create a minimum remote by hand to give to http_init(), - * primarily to allow it to look at the URL. - */ - remote = xcalloc(sizeof(*remote), 1); - ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc); - remote->url[remote->url_nr++] = repo->url; - http_init(remote); + http_init(NULL, repo->url); #ifdef USE_CURL_MULTI is_running_queue = 0; @@ -42,7 +42,7 @@ static long curl_low_speed_time = -1; static int curl_ftp_no_epsv; static const char *curl_http_proxy; static const char *curl_cookie_file; -static char *user_name, *user_pass; +static char *user_name, *user_pass, *description; static const char *user_agent; #if LIBCURL_VERSION_NUM >= 0x071700 @@ -139,6 +139,27 @@ static void process_curl_messages(void) } #endif +static char *git_getpass_with_description(const char *what, const char *desc) +{ + struct strbuf prompt = STRBUF_INIT; + char *r; + + if (desc) + strbuf_addf(&prompt, "%s for '%s': ", what, desc); + else + strbuf_addf(&prompt, "%s: ", what); + /* + * NEEDSWORK: for usernames, we should do something less magical that + * actually echoes the characters. However, we need to read from + * /dev/tty and not stdio, which is not portable (but getpass will do + * it for us). http.c uses the same workaround. + */ + r = git_getpass(prompt.buf); + + strbuf_release(&prompt); + return xstrdup(r); +} + static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { @@ -214,7 +235,7 @@ static void init_curl_http_auth(CURL *result) if (user_name) { struct strbuf up = STRBUF_INIT; if (!user_pass) - user_pass = xstrdup(git_getpass("Password: ")); + user_pass = xstrdup(git_getpass_with_description("Password", description)); strbuf_addf(&up, "%s:%s", user_name, user_pass); curl_easy_setopt(result, CURLOPT_USERPWD, strbuf_detach(&up, NULL)); @@ -229,7 +250,7 @@ static int has_cert_password(void) return 0; /* Only prompt the user once. */ ssl_cert_password_required = -1; - ssl_cert_password = git_getpass("Certificate Password: "); + ssl_cert_password = git_getpass_with_description("Certificate Password", description); if (ssl_cert_password != NULL) { ssl_cert_password = xstrdup(ssl_cert_password); return 1; @@ -307,8 +328,7 @@ static CURL *get_curl_handle(void) static void http_auth_init(const char *url) { - char *at, *colon, *cp, *slash, *decoded; - int len; + const char *at, *colon, *cp, *slash, *host; cp = strstr(url, "://"); if (!cp) @@ -324,34 +344,22 @@ static void http_auth_init(const char *url) at = strchr(cp, '@'); colon = strchr(cp, ':'); slash = strchrnul(cp, '/'); - if (!at || slash <= at) - return; /* No credentials */ - if (!colon || at <= colon) { + if (!at || slash <= at) { + /* No credentials, but we may have to ask for some later */ + host = cp; + } + else if (!colon || at <= colon) { /* Only username */ - len = at - cp; - user_name = xmalloc(len + 1); - memcpy(user_name, cp, len); - user_name[len] = '\0'; - decoded = url_decode(user_name); - free(user_name); - user_name = decoded; + user_name = url_decode_mem(cp, at - cp); user_pass = NULL; + host = at + 1; } else { - len = colon - cp; - user_name = xmalloc(len + 1); - memcpy(user_name, cp, len); - user_name[len] = '\0'; - decoded = url_decode(user_name); - free(user_name); - user_name = decoded; - len = at - (colon + 1); - user_pass = xmalloc(len + 1); - memcpy(user_pass, colon + 1, len); - user_pass[len] = '\0'; - decoded = url_decode(user_pass); - free(user_pass); - user_pass = decoded; + user_name = url_decode_mem(cp, colon - cp); + user_pass = url_decode_mem(colon + 1, at - (colon + 1)); + host = at + 1; } + + description = url_decode_mem(host, slash - host); } static void set_from_env(const char **var, const char *envname) @@ -361,7 +369,7 @@ static void set_from_env(const char **var, const char *envname) *var = val; } -void http_init(struct remote *remote) +void http_init(struct remote *remote, const char *url) { char *low_speed_limit; char *low_speed_time; @@ -425,11 +433,11 @@ void http_init(struct remote *remote) if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1; - if (remote && remote->url && remote->url[0]) { - http_auth_init(remote->url[0]); + if (url) { + http_auth_init(url); if (!ssl_cert_password_required && getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") && - !prefixcmp(remote->url[0], "https://")) + !prefixcmp(url, "https://")) ssl_cert_password_required = 1; } @@ -847,7 +855,7 @@ static int http_request(const char *url, void *result, int target, int options) * but that is non-portable. Using git_getpass() can at least be stubbed * on other platforms with a different implementation if/when necessary. */ - user_name = xstrdup(git_getpass("Username: ")); + user_name = xstrdup(git_getpass_with_description("Username", description)); init_curl_http_auth(slot->curl); ret = HTTP_REAUTH; } @@ -870,13 +878,18 @@ static int http_request(const char *url, void *result, int target, int options) return ret; } +static int http_request_reauth(const char *url, void *result, int target, + int options) +{ + int ret = http_request(url, result, target, options); + if (ret != HTTP_REAUTH) + return ret; + return http_request(url, result, target, options); +} + int http_get_strbuf(const char *url, struct strbuf *result, int options) { - int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options); - if (http_ret == HTTP_REAUTH) { - http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options); - } - return http_ret; + return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options); } /* @@ -899,7 +912,7 @@ static int http_get_file(const char *url, const char *filename, int options) goto cleanup; } - ret = http_request(url, result, HTTP_REQUEST_FILE, options); + ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options); fclose(result); if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename)) @@ -86,7 +86,7 @@ extern void add_fill_function(void *data, int (*fill)(void *)); extern void step_active_slots(void); #endif -extern void http_init(struct remote *remote); +extern void http_init(struct remote *remote, const char *url); extern void http_cleanup(void); extern int data_received; diff --git a/log-tree.c b/log-tree.c index 24c295ea1d..e7694a3a4c 100644 --- a/log-tree.c +++ b/log-tree.c @@ -165,6 +165,14 @@ static void show_parents(struct commit *commit, int abbrev) } } +static void show_children(struct rev_info *opt, struct commit *commit, int abbrev) +{ + struct commit_list *p = lookup_decoration(&opt->children, &commit->object); + for ( ; p; p = p->next) { + printf(" %s", find_unique_abbrev(p->item->object.sha1, abbrev)); + } +} + void show_decorations(struct rev_info *opt, struct commit *commit) { const char *prefix; @@ -414,6 +422,8 @@ void show_log(struct rev_info *opt) fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); if (opt->print_parents) show_parents(commit, abbrev_commit); + if (opt->children.name) + show_children(opt, commit, abbrev_commit); show_decorations(opt, commit); if (opt->graph && !graph_is_commit_finished(opt->graph)) { putchar('\n'); @@ -473,6 +483,8 @@ void show_log(struct rev_info *opt) stdout); if (opt->print_parents) show_parents(commit, abbrev_commit); + if (opt->children.name) + show_children(opt, commit, abbrev_commit); if (parent) printf(" (from %s)", find_unique_abbrev(parent->object.sha1, diff --git a/merge-recursive.c b/merge-recursive.c index c34a4f148b..cc664c39b6 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -946,8 +946,10 @@ static struct merge_file_info merge_file_1(struct merge_options *o, free(result_buf.ptr); result.clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result.clean = merge_submodule(result.sha, one->path, one->sha1, - a->sha1, b->sha1); + result.clean = merge_submodule(result.sha, + one->path, one->sha1, + a->sha1, b->sha1, + !o->call_depth); } else if (S_ISLNK(a->mode)) { hashcpy(result.sha, a->sha1); diff --git a/mergetools/bc3 b/mergetools/bc3 index 27b3dd48b8..b6319d206e 100644 --- a/mergetools/bc3 +++ b/mergetools/bc3 @@ -16,5 +16,10 @@ merge_cmd () { } translate_merge_tool_path() { - echo bcompare + if type bcomp >/dev/null 2>/dev/null + then + echo bcomp + else + echo bcompare + fi } diff --git a/name-hash.c b/name-hash.c index c6b6a3fe4c..225dd76995 100644 --- a/name-hash.c +++ b/name-hash.c @@ -57,12 +57,10 @@ static void hash_index_entry_directories(struct index_state *istate, struct cach if (*ptr == '/') { ++ptr; hash = hash_name(ce->name, ptr - ce->name); - if (!lookup_hash(hash, &istate->name_hash)) { - pos = insert_hash(hash, ce, &istate->name_hash); - if (pos) { - ce->next = *pos; - *pos = ce; - } + pos = insert_hash(hash, ce, &istate->name_hash); + if (pos) { + ce->dir_next = *pos; + *pos = ce; } } } @@ -166,7 +164,10 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na if (same_name(ce, name, namelen, icase)) return ce; } - ce = ce->next; + if (icase && name[namelen - 1] == '/') + ce = ce->dir_next; + else + ce = ce->next; } /* @@ -283,7 +283,7 @@ return_null: * links. User relative paths are also returned as they are given, * except DWIM suffixing. */ -char *enter_repo(char *path, int strict) +const char *enter_repo(const char *path, int strict) { static char used_path[PATH_MAX]; static char validated_path[PATH_MAX]; @@ -295,16 +295,19 @@ char *enter_repo(char *path, int strict) static const char *suffix[] = { ".git/.git", "/.git", ".git", "", NULL, }; + const char *gitfile; int len = strlen(path); int i; - while ((1 < len) && (path[len-1] == '/')) { - path[len-1] = 0; + while ((1 < len) && (path[len-1] == '/')) len--; - } + if (PATH_MAX <= len) return NULL; - if (path[0] == '~') { - char *newpath = expand_user_path(path); + strncpy(used_path, path, len); used_path[len] = 0 ; + strcpy(validated_path, used_path); + + if (used_path[0] == '~') { + char *newpath = expand_user_path(used_path); if (!newpath || (PATH_MAX - 10 < strlen(newpath))) { free(newpath); return NULL; @@ -316,24 +319,23 @@ char *enter_repo(char *path, int strict) * anyway. */ strcpy(used_path, newpath); free(newpath); - strcpy(validated_path, path); - path = used_path; } else if (PATH_MAX - 10 < len) return NULL; - else { - path = strcpy(used_path, path); - strcpy(validated_path, path); - } - len = strlen(path); + len = strlen(used_path); for (i = 0; suffix[i]; i++) { - strcpy(path + len, suffix[i]); - if (!access(path, F_OK)) { + strcpy(used_path + len, suffix[i]); + if (!access(used_path, F_OK)) { strcat(validated_path, suffix[i]); break; } } - if (!suffix[i] || chdir(path)) + if (!suffix[i]) + return NULL; + gitfile = read_gitfile(used_path) ; + if (gitfile) + strcpy(used_path, gitfile); + if (chdir(used_path)) return NULL; path = validated_path; } @@ -393,12 +393,22 @@ static struct ref_array *get_loose_refs(const char *submodule) #define MAXDEPTH 5 #define MAXREFLEN (1024) +/* + * Called by resolve_gitlink_ref_recursive() after it failed to read + * from "name", which is "module/.git/<refname>". Find <refname> in + * the packed-refs file for the submodule. + */ static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result) { int retval = -1; struct ref_entry *ref; - struct ref_array *array = get_packed_refs(name); + struct ref_array *array; + /* being defensive: resolve_gitlink_ref() did this for us */ + if (pathlen < 6 || memcmp(name + pathlen - 6, "/.git/", 6)) + die("Oops"); + name[pathlen - 6] = '\0'; /* make it path to the submodule */ + array = get_packed_refs(name); ref = search_ref_array(array, refname); if (ref != NULL) { memcpy(result, ref->sha1, 20); diff --git a/remote-curl.c b/remote-curl.c index 0aa4bfed30..0e720ee8bb 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -115,7 +115,7 @@ static struct discovery* discover_refs(const char *service) http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE); /* try again with "plain" url (no ? or & appended) */ - if (http_ret != HTTP_OK) { + if (http_ret != HTTP_OK && http_ret != HTTP_NOAUTH) { free(refs_url); strbuf_reset(&buffer); @@ -859,7 +859,7 @@ int main(int argc, const char **argv) url = strbuf_detach(&buf, NULL); - http_init(remote); + http_init(remote, url); do { if (strbuf_getline(&buf, stdin, '\n') == EOF) { diff --git a/sha1_file.c b/sha1_file.c index 3401301444..27f3b9b278 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1987,7 +1987,7 @@ off_t find_pack_entry_one(const unsigned char *sha1, return 0; } -static int is_pack_valid(struct packed_git *p) +int is_pack_valid(struct packed_git *p) { /* An already open pack is known to be valid. */ if (p->pack_fd != -1) @@ -2038,7 +2038,7 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) * was loaded! */ if (!is_pack_valid(p)) { - error("packfile %s cannot be accessed", p->pack_name); + warning("packfile %s cannot be accessed", p->pack_name); goto next; } e->offset = offset; diff --git a/show-index.c b/show-index.c index 4c0ac138af..63f9da5323 100644 --- a/show-index.c +++ b/show-index.c @@ -48,7 +48,7 @@ int main(int argc, char **argv) unsigned char sha1[20]; uint32_t crc; uint32_t off; - } *entries = malloc(nr * sizeof(entries[0])); + } *entries = xmalloc(nr * sizeof(entries[0])); for (i = 0; i < nr; i++) if (fread(entries[i].sha1, 20, 1, stdin) != 1) die("unable to read sha1 %u/%u", i, nr); @@ -357,7 +357,6 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) { int ch; - strbuf_grow(sb, 0); if (feof(fp)) return EOF; diff --git a/submodule.c b/submodule.c index 0b709bc291..0fd10a0fdb 100644 --- a/submodule.c +++ b/submodule.c @@ -794,7 +794,7 @@ static void print_commit(struct commit *commit) int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20], const unsigned char a[20], - const unsigned char b[20]) + const unsigned char b[20], int search) { struct commit *commit_base, *commit_a, *commit_b; int parent_count; @@ -849,6 +849,10 @@ int merge_submodule(unsigned char result[20], const char *path, * user needs to confirm the resolution. */ + /* Skip the search if makes no sense to the calling context. */ + if (!search) + return 0; + /* find commit which merges them */ parent_count = find_first_merges(&merges, path, commit_a, commit_b); switch (parent_count) { diff --git a/submodule.h b/submodule.h index 799c22d6c6..80e04f3c8c 100644 --- a/submodule.h +++ b/submodule.h @@ -28,7 +28,7 @@ int fetch_populated_submodules(int num_options, const char **options, int quiet); unsigned is_submodule_modified(const char *path, int ignore_untracked); int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20], - const unsigned char a[20], const unsigned char b[20]); + const unsigned char a[20], const unsigned char b[20], int search); int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name); #endif diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index b8996a373a..f7dc0781d5 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -81,8 +81,7 @@ prepare_httpd() { if test -n "$LIB_HTTPD_SSL" then - HTTPD_URL=https://127.0.0.1:$LIB_HTTPD_PORT - AUTH_HTTPD_URL=https://user%40host:user%40host@127.0.0.1:$LIB_HTTPD_PORT + HTTPD_PROTO=https RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \ -config "$TEST_PATH/ssl.cnf" \ @@ -93,9 +92,12 @@ prepare_httpd() { export GIT_SSL_NO_VERIFY HTTPD_PARA="$HTTPD_PARA -DSSL" else - HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT - AUTH_HTTPD_URL=http://user%40host:user%40host@127.0.0.1:$LIB_HTTPD_PORT + HTTPD_PROTO=http fi + HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT + HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST + HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST + HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:user%40host@$HTTPD_DEST if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN" then diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index 46b0736b35..dbb2623d93 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -7,7 +7,7 @@ test_description=gitattributes attr_check () { path="$1" expect="$2" - git check-attr test -- "$path" >actual 2>err && + git $3 check-attr test -- "$path" >actual 2>err && echo "$path: test: $2" >expect && test_cmp expect actual && test_line_count = 0 err @@ -23,6 +23,7 @@ test_expect_success 'setup' ' echo "onoff test -test" echo "offon -test test" echo "no notest" + echo "A/e/F test=A/e/F" ) >.gitattributes && ( echo "g test=a/g" && @@ -84,6 +85,62 @@ test_expect_success 'attribute test' ' attr_check a/b/d/yes unspecified ' +test_expect_success 'attribute matching is case sensitive when core.ignorecase=0' ' + + test_must_fail attr_check F f "-c core.ignorecase=0" && + test_must_fail attr_check a/F f "-c core.ignorecase=0" && + test_must_fail attr_check a/c/F f "-c core.ignorecase=0" && + test_must_fail attr_check a/G a/g "-c core.ignorecase=0" && + test_must_fail attr_check a/B/g a/b/g "-c core.ignorecase=0" && + test_must_fail attr_check a/b/G a/b/g "-c core.ignorecase=0" && + test_must_fail attr_check a/b/H a/b/h "-c core.ignorecase=0" && + test_must_fail attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=0" && + test_must_fail attr_check oNoFf unset "-c core.ignorecase=0" && + test_must_fail attr_check oFfOn set "-c core.ignorecase=0" && + attr_check NO unspecified "-c core.ignorecase=0" && + test_must_fail attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=0" && + attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" && + test_must_fail attr_check a/E/f "A/e/F" "-c core.ignorecase=0" + +' + +test_expect_success 'attribute matching is case insensitive when core.ignorecase=1' ' + + attr_check F f "-c core.ignorecase=1" && + attr_check a/F f "-c core.ignorecase=1" && + attr_check a/c/F f "-c core.ignorecase=1" && + attr_check a/G a/g "-c core.ignorecase=1" && + attr_check a/B/g a/b/g "-c core.ignorecase=1" && + attr_check a/b/G a/b/g "-c core.ignorecase=1" && + attr_check a/b/H a/b/h "-c core.ignorecase=1" && + attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=1" && + attr_check oNoFf unset "-c core.ignorecase=1" && + attr_check oFfOn set "-c core.ignorecase=1" && + attr_check NO unspecified "-c core.ignorecase=1" && + attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=1" && + attr_check a/b/d/YES unspecified "-c core.ignorecase=1" && + attr_check a/E/f "A/e/F" "-c core.ignorecase=1" + +' + +test_expect_success 'check whether FS is case-insensitive' ' + mkdir junk && + echo good >junk/CamelCase && + echo bad >junk/camelcase && + if test "$(cat junk/CamelCase)" != good + then + test_set_prereq CASE_INSENSITIVE_FS + fi +' + +test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' ' + test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" && + test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" && + attr_check A/b/h a/b/h "-c core.ignorecase=1" && + attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" && + attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1" +' + test_expect_success 'unnormalized paths' ' attr_check ./f f && attr_check ./a/g a/g && diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index 3b1b985996..e23ac0e69d 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -118,7 +118,7 @@ test_expect_success 'alias expansion' ' ) ' -test_expect_success '!alias expansion' ' +test_expect_success NOT_MINGW '!alias expansion' ' pwd >expect && ( git config alias.test !pwd && diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 3e140c18f4..51caff047b 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -7,28 +7,28 @@ test_description='Test git config in different settings' . ./test-lib.sh -test -f .git/config && rm .git/config - -git config core.penguin "little blue" +test_expect_success 'clear default config' ' + rm -f .git/config +' cat > expect << EOF [core] penguin = little blue EOF - -test_expect_success 'initial' 'cmp .git/config expect' - -git config Core.Movie BadPhysics +test_expect_success 'initial' ' + git config core.penguin "little blue" && + test_cmp expect .git/config +' cat > expect << EOF [core] penguin = little blue Movie = BadPhysics EOF - -test_expect_success 'mixed case' 'cmp .git/config expect' - -git config Cores.WhatEver Second +test_expect_success 'mixed case' ' + git config Core.Movie BadPhysics && + test_cmp expect .git/config +' cat > expect << EOF [core] @@ -37,10 +37,10 @@ cat > expect << EOF [Cores] WhatEver = Second EOF - -test_expect_success 'similar section' 'cmp .git/config expect' - -git config CORE.UPPERCASE true +test_expect_success 'similar section' ' + git config Cores.WhatEver Second + test_cmp expect .git/config +' cat > expect << EOF [core] @@ -50,8 +50,10 @@ cat > expect << EOF [Cores] WhatEver = Second EOF - -test_expect_success 'similar section' 'cmp .git/config expect' +test_expect_success 'uppercase section' ' + git config CORE.UPPERCASE true && + test_cmp expect .git/config +' test_expect_success 'replace with non-match' \ 'git config core.penguin kingpin !blue' @@ -69,7 +71,34 @@ cat > expect << EOF WhatEver = Second EOF -test_expect_success 'non-match result' 'cmp .git/config expect' +test_expect_success 'non-match result' 'test_cmp expect .git/config' + +test_expect_success 'find mixed-case key by canonical name' ' + echo Second >expect && + git config cores.whatever >actual && + test_cmp expect actual +' + +test_expect_success 'find mixed-case key by non-canonical name' ' + echo Second >expect && + git config CoReS.WhAtEvEr >actual && + test_cmp expect actual +' + +test_expect_success 'subsections are not canonicalized by git-config' ' + cat >>.git/config <<-\EOF && + [section.SubSection] + key = one + [section "SubSection"] + key = two + EOF + echo one >expect && + git config section.subsection.key >actual && + test_cmp expect actual && + echo two >expect && + git config section.SubSection.key >actual && + test_cmp expect actual +' cat > .git/config <<\EOF [alpha] @@ -88,7 +117,7 @@ bar = foo [beta] EOF -test_expect_success 'unset with cont. lines is correct' 'cmp .git/config expect' +test_expect_success 'unset with cont. lines is correct' 'test_cmp expect .git/config' cat > .git/config << EOF [beta] ; silly comment # another comment @@ -116,7 +145,7 @@ noIndent= sillyValue ; 'nother silly comment [nextSection] noNewline = ouch EOF -test_expect_success 'multiple unset is correct' 'cmp .git/config expect' +test_expect_success 'multiple unset is correct' 'test_cmp expect .git/config' cp .git/config2 .git/config @@ -140,9 +169,7 @@ noIndent= sillyValue ; 'nother silly comment [nextSection] noNewline = ouch EOF -test_expect_success 'all replaced' 'cmp .git/config expect' - -git config beta.haha alpha +test_expect_success 'all replaced' 'test_cmp expect .git/config' cat > expect << EOF [beta] ; silly comment # another comment @@ -153,10 +180,10 @@ noIndent= sillyValue ; 'nother silly comment haha = alpha [nextSection] noNewline = ouch EOF - -test_expect_success 'really mean test' 'cmp .git/config expect' - -git config nextsection.nonewline wow +test_expect_success 'really mean test' ' + git config beta.haha alpha && + test_cmp expect .git/config +' cat > expect << EOF [beta] ; silly comment # another comment @@ -168,11 +195,12 @@ noIndent= sillyValue ; 'nother silly comment [nextSection] nonewline = wow EOF - -test_expect_success 'really really mean test' 'cmp .git/config expect' +test_expect_success 'really really mean test' ' + git config nextsection.nonewline wow && + test_cmp expect .git/config +' test_expect_success 'get value' 'test alpha = $(git config beta.haha)' -git config --unset beta.haha cat > expect << EOF [beta] ; silly comment # another comment @@ -183,10 +211,10 @@ noIndent= sillyValue ; 'nother silly comment [nextSection] nonewline = wow EOF - -test_expect_success 'unset' 'cmp .git/config expect' - -git config nextsection.NoNewLine "wow2 for me" "for me$" +test_expect_success 'unset' ' + git config --unset beta.haha && + test_cmp expect .git/config +' cat > expect << EOF [beta] ; silly comment # another comment @@ -198,8 +226,10 @@ noIndent= sillyValue ; 'nother silly comment nonewline = wow NoNewLine = wow2 for me EOF - -test_expect_success 'multivar' 'cmp .git/config expect' +test_expect_success 'multivar' ' + git config nextsection.NoNewLine "wow2 for me" "for me$" && + test_cmp expect .git/config +' test_expect_success 'non-match' \ 'git config --get nextsection.nonewline !for' @@ -214,8 +244,6 @@ test_expect_success 'ambiguous get' ' test_expect_success 'get multivar' \ 'git config --get-all nextsection.nonewline' -git config nextsection.nonewline "wow3" "wow$" - cat > expect << EOF [beta] ; silly comment # another comment noIndent= sillyValue ; 'nother silly comment @@ -226,8 +254,10 @@ noIndent= sillyValue ; 'nother silly comment nonewline = wow3 NoNewLine = wow2 for me EOF - -test_expect_success 'multivar replace' 'cmp .git/config expect' +test_expect_success 'multivar replace' ' + git config nextsection.nonewline "wow3" "wow$" && + test_cmp expect .git/config +' test_expect_success 'ambiguous value' ' test_must_fail git config nextsection.nonewline @@ -241,8 +271,6 @@ test_expect_success 'invalid unset' ' test_must_fail git config --unset somesection.nonewline ' -git config --unset nextsection.nonewline "wow3$" - cat > expect << EOF [beta] ; silly comment # another comment noIndent= sillyValue ; 'nother silly comment @@ -253,7 +281,10 @@ noIndent= sillyValue ; 'nother silly comment NoNewLine = wow2 for me EOF -test_expect_success 'multivar unset' 'cmp .git/config expect' +test_expect_success 'multivar unset' ' + git config --unset nextsection.nonewline "wow3$" && + test_cmp expect .git/config +' test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla' @@ -276,7 +307,7 @@ noIndent= sillyValue ; 'nother silly comment Alpha = beta EOF -test_expect_success 'hierarchical section value' 'cmp .git/config expect' +test_expect_success 'hierarchical section value' 'test_cmp expect .git/config' cat > expect << EOF beta.noindent=sillyValue @@ -304,15 +335,16 @@ EOF test_expect_success '--get-regexp' \ 'git config --get-regexp in > output && cmp output expect' -git config --add nextsection.nonewline "wow4 for you" - cat > expect << EOF wow2 for me wow4 for you EOF -test_expect_success '--add' \ - 'git config --get-all nextsection.nonewline > output && cmp output expect' +test_expect_success '--add' ' + git config --add nextsection.nonewline "wow4 for you" && + git config --get-all nextsection.nonewline > output && + test_cmp expect output +' cat > .git/config << EOF [novalue] @@ -333,6 +365,12 @@ test_expect_success 'get-regexp variable with no value' \ 'git config --get-regexp novalue > output && cmp output expect' +echo 'novalue.variable true' > expect + +test_expect_success 'get-regexp --bool variable with no value' \ + 'git config --bool --get-regexp novalue > output && + cmp output expect' + echo 'emptyvalue.variable ' > expect test_expect_success 'get-regexp variable with empty value' \ @@ -361,8 +399,6 @@ cat > .git/config << EOF c = d EOF -git config a.x y - cat > expect << EOF [a.b] c = d @@ -370,10 +406,10 @@ cat > expect << EOF x = y EOF -test_expect_success 'new section is partial match of another' 'cmp .git/config expect' - -git config b.x y -git config a.b c +test_expect_success 'new section is partial match of another' ' + git config a.x y && + test_cmp expect .git/config +' cat > expect << EOF [a.b] @@ -385,7 +421,11 @@ cat > expect << EOF x = y EOF -test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect' +test_expect_success 'new variable inserts into proper section' ' + git config b.x y && + git config a.b c && + test_cmp expect .git/config +' test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \ 'test_must_fail git config --file non-existing-config -l' @@ -399,9 +439,10 @@ cat > expect << EOF ein.bahn=strasse EOF -GIT_CONFIG=other-config git config -l > output - -test_expect_success 'alternative GIT_CONFIG' 'cmp output expect' +test_expect_success 'alternative GIT_CONFIG' ' + GIT_CONFIG=other-config git config -l >output && + test_cmp expect output +' test_expect_success 'alternative GIT_CONFIG (--file)' \ 'git config --file other-config -l > output && cmp output expect' @@ -417,8 +458,6 @@ test_expect_success 'refer config from subdirectory' ' ' -GIT_CONFIG=other-config git config anwohner.park ausweis - cat > expect << EOF [ein] bahn = strasse @@ -426,7 +465,10 @@ cat > expect << EOF park = ausweis EOF -test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect' +test_expect_success '--set in alternative GIT_CONFIG' ' + GIT_CONFIG=other-config git config anwohner.park ausweis && + test_cmp expect other-config +' cat > .git/config << EOF # Hallo @@ -516,8 +558,6 @@ EOF test_expect_success "section was removed properly" \ "test_cmp expect .git/config" -rm .git/config - cat > expect << EOF [gitcvs] enabled = true @@ -528,10 +568,11 @@ EOF test_expect_success 'section ending' ' + rm -f .git/config && git config gitcvs.enabled true && git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite && git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite && - cmp .git/config expect + test_cmp expect .git/config ' @@ -600,8 +641,6 @@ test_expect_success 'invalid bool (set)' ' test_must_fail git config --bool bool.nobool foobar' -rm .git/config - cat > expect <<\EOF [bool] true1 = true @@ -616,6 +655,7 @@ EOF test_expect_success 'set --bool' ' + rm -f .git/config && git config --bool bool.true1 01 && git config --bool bool.true2 -1 && git config --bool bool.true3 YeS && @@ -626,8 +666,6 @@ test_expect_success 'set --bool' ' git config --bool bool.false4 FALSE && cmp expect .git/config' -rm .git/config - cat > expect <<\EOF [int] val1 = 1 @@ -637,13 +675,12 @@ EOF test_expect_success 'set --int' ' + rm -f .git/config && git config --int int.val1 01 && git config --int int.val2 -1 && git config --int int.val3 5m && cmp expect .git/config' -rm .git/config - cat >expect <<\EOF [bool] true1 = true @@ -657,6 +694,7 @@ cat >expect <<\EOF EOF test_expect_success 'get --bool-or-int' ' + rm -f .git/config && ( echo "[bool]" echo true1 @@ -676,7 +714,6 @@ test_expect_success 'get --bool-or-int' ' ' -rm .git/config cat >expect <<\EOF [bool] true1 = true @@ -690,6 +727,7 @@ cat >expect <<\EOF EOF test_expect_success 'set --bool-or-int' ' + rm -f .git/config && git config --bool-or-int bool.true1 true && git config --bool-or-int bool.false1 false && git config --bool-or-int bool.true2 yes && @@ -700,8 +738,6 @@ test_expect_success 'set --bool-or-int' ' test_cmp expect .git/config ' -rm .git/config - cat >expect <<\EOF [path] home = ~/ @@ -710,6 +746,7 @@ cat >expect <<\EOF EOF test_expect_success NOT_MINGW 'set --path' ' + rm -f .git/config && git config --path path.home "~/" && git config --path path.normal "/dev/null" && git config --path path.trailingtilde "foo~" && @@ -750,13 +787,6 @@ test_expect_success NOT_MINGW 'get --path copes with unset $HOME' ' test_cmp expect result ' -rm .git/config - -git config quote.leading " test" -git config quote.ending "test " -git config quote.semicolon "test;test" -git config quote.hash "test#test" - cat > expect << EOF [quote] leading = " test" @@ -764,8 +794,14 @@ cat > expect << EOF semicolon = "test;test" hash = "test#test" EOF - -test_expect_success 'quoting' 'cmp .git/config expect' +test_expect_success 'quoting' ' + rm -f .git/config && + git config quote.leading " test" && + git config quote.ending "test " && + git config quote.semicolon "test;test" && + git config quote.hash "test#test" && + test_cmp expect .git/config +' test_expect_success 'key with newline' ' test_must_fail git config "key.with @@ -790,9 +826,10 @@ section.noncont=not continued section.quotecont=cont;inued EOF -git config --list > result - -test_expect_success 'value continued on next line' 'cmp result expect' +test_expect_success 'value continued on next line' ' + git config --list > result && + cmp result expect +' cat > .git/config <<\EOF [section "sub=section"] @@ -813,16 +850,17 @@ barQsection.sub=section.val3 Qsection.sub=section.val4 Qsection.sub=section.val5Q EOF +test_expect_success '--null --list' ' + git config --null --list | nul_to_q >result && + echo >>result && + test_cmp expect result +' -git config --null --list | perl -pe 'y/\000/Q/' > result -echo >>result - -test_expect_success '--null --list' 'cmp result expect' - -git config --null --get-regexp 'val[0-9]' | perl -pe 'y/\000/Q/' > result -echo >>result - -test_expect_success '--null --get-regexp' 'cmp result expect' +test_expect_success '--null --get-regexp' ' + git config --null --get-regexp "val[0-9]" | nul_to_q >result && + echo >>result && + test_cmp expect result +' test_expect_success 'inner whitespace kept verbatim' ' git config section.val "foo bar" && diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh index 710fccad36..1ae4d87c92 100755 --- a/t/t1402-check-ref-format.sh +++ b/t/t1402-check-ref-format.sh @@ -5,38 +5,40 @@ test_description='Test git check-ref-format' . ./test-lib.sh valid_ref() { - if test "$#" = 1 - then - test_expect_success "ref name '$1' is valid" \ - "git check-ref-format '$1'" - else - test_expect_success "ref name '$1' is valid with options $2" \ - "git check-ref-format $2 '$1'" - fi + prereq= + case $1 in + [A-Z]*) + prereq=$1 + shift + esac + test_expect_success $prereq "ref name '$1' is valid${2:+ with options $2}" " + git check-ref-format $2 '$1' + " } invalid_ref() { - if test "$#" = 1 - then - test_expect_success "ref name '$1' is invalid" \ - "test_must_fail git check-ref-format '$1'" - else - test_expect_success "ref name '$1' is invalid with options $2" \ - "test_must_fail git check-ref-format $2 '$1'" - fi + prereq= + case $1 in + [A-Z]*) + prereq=$1 + shift + esac + test_expect_success $prereq "ref name '$1' is invalid${2:+ with options $2}" " + test_must_fail git check-ref-format $2 '$1' + " } invalid_ref '' -invalid_ref '/' -invalid_ref '/' --allow-onelevel -invalid_ref '/' --normalize -invalid_ref '/' '--allow-onelevel --normalize' +invalid_ref NOT_MINGW '/' +invalid_ref NOT_MINGW '/' --allow-onelevel +invalid_ref NOT_MINGW '/' --normalize +invalid_ref NOT_MINGW '/' '--allow-onelevel --normalize' valid_ref 'foo/bar/baz' valid_ref 'foo/bar/baz' --normalize invalid_ref 'refs///heads/foo' valid_ref 'refs///heads/foo' --normalize invalid_ref 'heads/foo/' -invalid_ref '/heads/foo' -valid_ref '/heads/foo' --normalize +invalid_ref NOT_MINGW '/heads/foo' +valid_ref NOT_MINGW '/heads/foo' --normalize invalid_ref '///heads/foo' valid_ref '///heads/foo' --normalize invalid_ref './foo' @@ -115,14 +117,14 @@ invalid_ref "$ref" --refspec-pattern invalid_ref "$ref" '--refspec-pattern --allow-onelevel' ref='/foo' -invalid_ref "$ref" -invalid_ref "$ref" --allow-onelevel -invalid_ref "$ref" --refspec-pattern -invalid_ref "$ref" '--refspec-pattern --allow-onelevel' -invalid_ref "$ref" --normalize -valid_ref "$ref" '--allow-onelevel --normalize' -invalid_ref "$ref" '--refspec-pattern --normalize' -valid_ref "$ref" '--refspec-pattern --allow-onelevel --normalize' +invalid_ref NOT_MINGW "$ref" +invalid_ref NOT_MINGW "$ref" --allow-onelevel +invalid_ref NOT_MINGW "$ref" --refspec-pattern +invalid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel' +invalid_ref NOT_MINGW "$ref" --normalize +valid_ref NOT_MINGW "$ref" '--allow-onelevel --normalize' +invalid_ref NOT_MINGW "$ref" '--refspec-pattern --normalize' +valid_ref NOT_MINGW "$ref" '--refspec-pattern --allow-onelevel --normalize' test_expect_success "check-ref-format --branch @{-1}" ' T=$(git write-tree) && @@ -155,21 +157,35 @@ test_expect_success 'check-ref-format --branch from subdir' ' ' valid_ref_normalized() { - test_expect_success "ref name '$1' simplifies to '$2'" " + prereq= + case $1 in + [A-Z]*) + prereq=$1 + shift + esac + test_expect_success $prereq "ref name '$1' simplifies to '$2'" " refname=\$(git check-ref-format --normalize '$1') && - test \"\$refname\" = '$2'" + test \"\$refname\" = '$2' + " } invalid_ref_normalized() { - test_expect_success "check-ref-format --normalize rejects '$1'" " - test_must_fail git check-ref-format --normalize '$1'" + prereq= + case $1 in + [A-Z]*) + prereq=$1 + shift + esac + test_expect_success $prereq "check-ref-format --normalize rejects '$1'" " + test_must_fail git check-ref-format --normalize '$1' + " } valid_ref_normalized 'heads/foo' 'heads/foo' valid_ref_normalized 'refs///heads/foo' 'refs/heads/foo' -valid_ref_normalized '/heads/foo' 'heads/foo' +valid_ref_normalized NOT_MINGW '/heads/foo' 'heads/foo' valid_ref_normalized '///heads/foo' 'heads/foo' invalid_ref_normalized 'foo' -invalid_ref_normalized '/foo' +invalid_ref_normalized NOT_MINGW '/foo' invalid_ref_normalized 'heads/foo/../bar' invalid_ref_normalized 'heads/./foo' invalid_ref_normalized 'heads\foo' diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh new file mode 100755 index 0000000000..56090d2eba --- /dev/null +++ b/t/t2022-checkout-paths.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='checkout $tree -- $paths' +. ./test-lib.sh + +test_expect_success setup ' + mkdir dir && + >dir/master && + echo common >dir/common && + git add dir/master dir/common && + test_tick && git commit -m "master has dir/master" && + git checkout -b next && + git mv dir/master dir/next0 && + echo next >dir/next1 && + git add dir && + test_tick && git commit -m "next has dir/next but not dir/master" +' + +test_expect_success 'checking out paths out of a tree does not clobber unrelated paths' ' + git checkout next && + git reset --hard && + rm dir/next0 && + cat dir/common >expect.common && + echo modified >expect.next1 && + cat expect.next1 >dir/next1 && + echo untracked >expect.next2 && + cat expect.next2 >dir/next2 && + + git checkout master dir && + + test_cmp expect.common dir/common && + test_path_is_file dir/master && + git diff --exit-code master dir/master && + + test_path_is_missing dir/next0 && + test_cmp expect.next1 dir/next1 && + test_path_is_file dir/next2 && + test_must_fail git ls-files --error-unmatch dir/next2 && + test_cmp expect.next2 dir/next2 +' + +test_done diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index 2eec0118c4..88be904c09 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -65,4 +65,23 @@ test_expect_success '--no-empty-directory hides empty directory' ' test_cmp expected3 output ' +test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' ' + git init super && + git init sub && + ( + cd sub && + >a && + git add a && + git commit -m sub && + git pack-refs --all + ) && + ( + cd super && + "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub + git ls-files --others --exclude-standard >../actual + ) && + echo sub/ >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index 212ec54aaf..cb45574a7b 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -77,6 +77,21 @@ test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' ' test_must_fail git rev-parse --verify CHERRY_PICK_HEAD ' +test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' ' + pristine_detach initial && + echo foo > foo && + test_must_fail git cherry-pick base && + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD +' + +test_expect_success \ + 'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' ' + pristine_detach initial && + echo foo > foo && + test_must_fail git cherry-pick --strategy=resolve base && + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD +' + test_expect_success 'GIT_CHERRY_PICK_HELP suppresses CHERRY_PICK_HEAD' ' pristine_detach initial && ( diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh new file mode 100755 index 0000000000..001d678e09 --- /dev/null +++ b/t/t4051-diff-function-context.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +test_description='diff function context' + +. ./test-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh + + +cat <<\EOF >hello.c +#include <stdio.h> + +static int a(void) +{ + /* + * Dummy. + */ +} + +static int hello_world(void) +{ + /* Classic. */ + printf("Hello world.\n"); + + /* Success! */ + return 0; +} +static int b(void) +{ + /* + * Dummy, too. + */ +} + +int main(int argc, char **argv) +{ + a(); + b(); + return hello_world(); +} +EOF + +test_expect_success 'setup' ' + git add hello.c && + test_tick && + git commit -m initial && + + grep -v Classic <hello.c >hello.c.new && + mv hello.c.new hello.c +' + +cat <<\EOF >expected +diff --git a/hello.c b/hello.c +--- a/hello.c ++++ b/hello.c +@@ -10,8 +10,7 @@ static int a(void) + static int hello_world(void) + { +- /* Classic. */ + printf("Hello world.\n"); + + /* Success! */ + return 0; + } +EOF + +test_expect_success 'diff -U0 -W' ' + git diff -U0 -W >actual && + compare_diff_patch actual expected +' + +cat <<\EOF >expected +diff --git a/hello.c b/hello.c +--- a/hello.c ++++ b/hello.c +@@ -9,9 +9,8 @@ static int a(void) + + static int hello_world(void) + { +- /* Classic. */ + printf("Hello world.\n"); + + /* Success! */ + return 0; + } +EOF + +test_expect_success 'diff -W' ' + git diff -W >actual && + compare_diff_patch actual expected +' + +test_done diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh new file mode 100755 index 0000000000..b7da95fac5 --- /dev/null +++ b/t/t4254-am-corrupt.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description='git am with corrupt input' +. ./test-lib.sh + +# Note the missing "+++" line: +cat > bad-patch.diff <<'EOF' +From: A U Thor <au.thor@example.com> +diff --git a/f b/f +index 7898192..6178079 100644 +--- a/f +@@ -1 +1 @@ +-a ++b +EOF + +test_expect_success setup ' + test $? = 0 && + echo a > f && + git add f && + test_tick && + git commit -m initial +' + +# This used to fail before, too, but with a different diagnostic. +# fatal: unable to write file '(null)' mode 100644: Bad address +# Also, it had the unwanted side-effect of deleting f. +test_expect_success 'try to apply corrupted patch' ' + git am bad-patch.diff 2> actual + test $? = 1 +' + +cat > expected <<EOF +fatal: git diff header lacks filename information (line 4) +EOF + +test_expect_success 'compare diagnostic; ensure file is still here' ' + test $? = 0 && + test -f f && + test_cmp expected actual +' + +test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 3abb2907ea..b69cf574d7 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -40,6 +40,40 @@ mk_test () { ) } +mk_test_with_hooks() { + mk_test "$@" && + ( + cd testrepo && + mkdir .git/hooks && + cd .git/hooks && + + cat >pre-receive <<-'EOF' && + #!/bin/sh + cat - >>pre-receive.actual + EOF + + cat >update <<-'EOF' && + #!/bin/sh + printf "%s %s %s\n" "$@" >>update.actual + EOF + + cat >post-receive <<-'EOF' && + #!/bin/sh + cat - >>post-receive.actual + EOF + + cat >post-update <<-'EOF' && + #!/bin/sh + for ref in "$@" + do + printf "%s\n" "$ref" >>post-update.actual + done + EOF + + chmod +x pre-receive update post-receive post-update + ) +} + mk_child() { rm -rf "$1" && git clone testrepo "$1" @@ -559,6 +593,169 @@ test_expect_success 'allow deleting an invalid remote ref' ' ' +test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' ' + mk_test_with_hooks heads/master heads/next && + orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && + newmaster=$(git show-ref -s --verify refs/heads/master) && + orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && + newnext=$_z40 && + git push testrepo refs/heads/master:refs/heads/master :refs/heads/next && + ( + cd testrepo/.git && + cat >pre-receive.expect <<-EOF && + $orgmaster $newmaster refs/heads/master + $orgnext $newnext refs/heads/next + EOF + + cat >update.expect <<-EOF && + refs/heads/master $orgmaster $newmaster + refs/heads/next $orgnext $newnext + EOF + + cat >post-receive.expect <<-EOF && + $orgmaster $newmaster refs/heads/master + $orgnext $newnext refs/heads/next + EOF + + cat >post-update.expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + + test_cmp pre-receive.expect pre-receive.actual && + test_cmp update.expect update.actual && + test_cmp post-receive.expect post-receive.actual && + test_cmp post-update.expect post-update.actual + ) +' + +test_expect_success 'deleting dangling ref triggers hooks with correct args' ' + mk_test_with_hooks heads/master && + rm -f testrepo/.git/objects/??/* && + git push testrepo :refs/heads/master && + ( + cd testrepo/.git && + cat >pre-receive.expect <<-EOF && + $_z40 $_z40 refs/heads/master + EOF + + cat >update.expect <<-EOF && + refs/heads/master $_z40 $_z40 + EOF + + cat >post-receive.expect <<-EOF && + $_z40 $_z40 refs/heads/master + EOF + + cat >post-update.expect <<-EOF && + refs/heads/master + EOF + + test_cmp pre-receive.expect pre-receive.actual && + test_cmp update.expect update.actual && + test_cmp post-receive.expect post-receive.actual && + test_cmp post-update.expect post-update.actual + ) +' + +test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' ' + mk_test_with_hooks heads/master && + orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && + newmaster=$(git show-ref -s --verify refs/heads/master) && + git push testrepo master :refs/heads/nonexistent && + ( + cd testrepo/.git && + cat >pre-receive.expect <<-EOF && + $orgmaster $newmaster refs/heads/master + $_z40 $_z40 refs/heads/nonexistent + EOF + + cat >update.expect <<-EOF && + refs/heads/master $orgmaster $newmaster + refs/heads/nonexistent $_z40 $_z40 + EOF + + cat >post-receive.expect <<-EOF && + $orgmaster $newmaster refs/heads/master + EOF + + cat >post-update.expect <<-EOF && + refs/heads/master + EOF + + test_cmp pre-receive.expect pre-receive.actual && + test_cmp update.expect update.actual && + test_cmp post-receive.expect post-receive.actual && + test_cmp post-update.expect post-update.actual + ) +' + +test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' ' + mk_test_with_hooks heads/master && + git push testrepo :refs/heads/nonexistent && + ( + cd testrepo/.git && + cat >pre-receive.expect <<-EOF && + $_z40 $_z40 refs/heads/nonexistent + EOF + + cat >update.expect <<-EOF && + refs/heads/nonexistent $_z40 $_z40 + EOF + + test_cmp pre-receive.expect pre-receive.actual && + test_cmp update.expect update.actual && + test_path_is_missing post-receive.actual && + test_path_is_missing post-update.actual + ) +' + +test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' ' + mk_test_with_hooks heads/master heads/next heads/pu && + orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && + newmaster=$(git show-ref -s --verify refs/heads/master) && + orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && + newnext=$_z40 && + orgpu=$(cd testrepo && git show-ref -s --verify refs/heads/pu) && + newpu=$(git show-ref -s --verify refs/heads/master) && + git push testrepo refs/heads/master:refs/heads/master \ + refs/heads/master:refs/heads/pu :refs/heads/next \ + :refs/heads/nonexistent && + ( + cd testrepo/.git && + cat >pre-receive.expect <<-EOF && + $orgmaster $newmaster refs/heads/master + $orgnext $newnext refs/heads/next + $orgpu $newpu refs/heads/pu + $_z40 $_z40 refs/heads/nonexistent + EOF + + cat >update.expect <<-EOF && + refs/heads/master $orgmaster $newmaster + refs/heads/next $orgnext $newnext + refs/heads/pu $orgpu $newpu + refs/heads/nonexistent $_z40 $_z40 + EOF + + cat >post-receive.expect <<-EOF && + $orgmaster $newmaster refs/heads/master + $orgnext $newnext refs/heads/next + $orgpu $newpu refs/heads/pu + EOF + + cat >post-update.expect <<-EOF && + refs/heads/master + refs/heads/next + refs/heads/pu + EOF + + test_cmp pre-receive.expect pre-receive.actual && + test_cmp update.expect update.actual && + test_cmp post-receive.expect post-receive.actual && + test_cmp post-update.expect post-update.actual + ) +' + test_expect_success 'allow deleting a ref using --delete' ' mk_test heads/master && (cd testrepo && git config receive.denyDeleteCurrent warn) && diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index a1883ca6b6..311a33ca84 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -8,8 +8,8 @@ if test -n "$NO_CURL"; then test_done fi -. "$TEST_DIRECTORY"/lib-httpd.sh LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'} +. "$TEST_DIRECTORY"/lib-httpd.sh start_httpd test_expect_success 'setup repository' ' @@ -35,11 +35,54 @@ test_expect_success 'clone http repository' ' test_cmp file clone/file ' -test_expect_success 'clone http repository with authentication' ' +test_expect_success 'create password-protected repository' ' mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" && - cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git" && - git clone $AUTH_HTTPD_URL/auth/repo.git clone-auth && - test_cmp file clone-auth/file + cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ + "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git" +' + +test_expect_success 'setup askpass helpers' ' + cat >askpass <<-EOF && + #!/bin/sh + echo >>"$PWD/askpass-query" "askpass: \$*" && + cat "$PWD/askpass-response" + EOF + chmod +x askpass && + GIT_ASKPASS="$PWD/askpass" && + export GIT_ASKPASS && + >askpass-expect-none && + echo "askpass: Password for '\''$HTTPD_DEST'\'': " >askpass-expect-pass && + { echo "askpass: Username for '\''$HTTPD_DEST'\'': " && + cat askpass-expect-pass + } >askpass-expect-both +' + +test_expect_success 'cloning password-protected repository can fail' ' + >askpass-query && + echo wrong >askpass-response && + test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail && + test_cmp askpass-expect-both askpass-query +' + +test_expect_success 'http auth can use user/pass in URL' ' + >askpass-query && + echo wrong >askpass-reponse && + git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none && + test_cmp askpass-expect-none askpass-query +' + +test_expect_success 'http auth can use just user in URL' ' + >askpass-query && + echo user@host >askpass-response && + git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass && + test_cmp askpass-expect-pass askpass-query +' + +test_expect_success 'http auth can request both user and pass' ' + >askpass-query && + echo user@host >askpass-response && + git clone "$HTTPD_URL/auth/repo.git" clone-auth-both && + test_cmp askpass-expect-both askpass-query ' test_expect_success 'fetch changes via http' ' diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index e8103144bb..87ee01662c 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -206,6 +206,20 @@ test_expect_success 'clone from .git file' ' git clone dst/.git dst2 ' +test_expect_success 'fetch from .git gitfile' ' + ( + cd dst2 && + git fetch ../dst/.git + ) +' + +test_expect_success 'fetch from gitfile parent' ' + ( + cd dst2 && + git fetch ../dst + ) +' + test_expect_success 'clone separate gitdir where target already exists' ' rm -rf dst && test_must_fail git clone --separate-git-dir realgitdir src dst diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh index a8fb30b792..0d5b42a25b 100755 --- a/t/t7405-submodule-merge.sh +++ b/t/t7405-submodule-merge.sh @@ -228,4 +228,55 @@ test_expect_success 'merging with a modify/modify conflict between merge bases' git merge d ' +# canonical criss-cross history in top and submodule +test_expect_success 'setup for recursive merge with submodule' ' + mkdir merge-recursive && + (cd merge-recursive && + git init && + mkdir sub && + (cd sub && + git init && + test_commit a && + git checkout -b sub-b master && + test_commit b && + git checkout -b sub-c master && + test_commit c && + git checkout -b sub-bc sub-b && + git merge sub-c && + git checkout -b sub-cb sub-c && + git merge sub-b && + git checkout master) && + git add sub && + git commit -m a && + git checkout -b top-b master && + (cd sub && git checkout sub-b) && + git add sub && + git commit -m b && + git checkout -b top-c master && + (cd sub && git checkout sub-c) && + git add sub && + git commit -m c && + git checkout -b top-bc top-b && + git merge -s ours --no-commit top-c && + (cd sub && git checkout sub-bc) && + git add sub && + git commit -m bc && + git checkout -b top-cb top-c && + git merge -s ours --no-commit top-b && + (cd sub && git checkout sub-cb) && + git add sub && + git commit -m cb) +' + +# merge should leave submodule unmerged in index +test_expect_success 'recursive merge with submodule' ' + (cd merge-recursive && + test_must_fail git merge top-bc && + echo "160000 $(git rev-parse top-cb:sub) 2 sub" > expect2 && + echo "160000 $(git rev-parse top-bc:sub) 3 sub" > expect3 && + git ls-files -u > actual && + grep "$(cat expect2)" actual > /dev/null && + grep "$(cat expect3)" actual > /dev/null) +' + test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 87aac835a1..3008e4e121 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -643,4 +643,27 @@ test_expect_success 'amending no-ff merge commit' ' test_debug 'git log --graph --decorate --oneline --all' +cat >editor <<\EOF +#!/bin/sh +# Add a new message string that was not in the template +( + echo "Merge work done on the side branch c1" + echo + cat <"$1" +) >"$1.tmp" && mv "$1.tmp" "$1" +# strip comments and blank lines from end of message +sed -e '/^#/d' < "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' > expected +EOF +chmod 755 editor + +test_expect_success 'merge --no-ff --edit' ' + git reset --hard c0 && + EDITOR=./editor git merge --no-ff --edit c1 && + verify_parents $c0 $c1 && + git cat-file commit HEAD >raw && + grep "work done on the side branch" raw && + sed "1,/^$/d" >actual raw && + test_cmp actual expected +' + test_done diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 395adfc8a9..4fb4c9384a 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -38,7 +38,17 @@ restore_test_defaults() prompt_given() { prompt="$1" - test "$prompt" = "Hit return to launch 'test-tool': branch" + test "$prompt" = "Launch 'test-tool' [Y/n]: branch" +} + +stdin_contains() +{ + grep >/dev/null "$1" +} + +stdin_doesnot_contain() +{ + ! stdin_contains "$1" } # Create a file on master and change it on branch @@ -265,4 +275,35 @@ test_expect_success PERL 'difftool --extcmd cat arg2' ' test "$diff" = branch ' +# Create a second file on master and a different version on branch +test_expect_success PERL 'setup with 2 files different' ' + echo m2 >file2 && + git add file2 && + git commit -m "added file2" && + + git checkout branch && + echo br2 >file2 && + git add file2 && + git commit -a -m "branch changed file2" && + git checkout master +' + +test_expect_success PERL 'say no to the first file' ' + diff=$( (echo n; echo) | git difftool -x cat branch ) && + + echo "$diff" | stdin_contains m2 && + echo "$diff" | stdin_contains br2 && + echo "$diff" | stdin_doesnot_contain master && + echo "$diff" | stdin_doesnot_contain branch +' + +test_expect_success PERL 'say no to the second file' ' + diff=$( (echo; echo n) | git difftool -x cat branch ) && + + echo "$diff" | stdin_contains master && + echo "$diff" | stdin_contains branch && + echo "$diff" | stdin_doesnot_contain m2 && + echo "$diff" | stdin_doesnot_contain br2 +' + test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 87b4acc9a6..8c12c65c72 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -23,6 +23,7 @@ test_expect_success $PREREQ \ echo do echo " echo \"!\$a!\"" echo "done >commandline\$output" + test_have_prereq MINGW && echo "dos2unix commandline\$output" echo "cat > msgtxt\$output" ) >fake.sendmail && chmod +x ./fake.sendmail && diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh new file mode 100755 index 0000000000..e38d9fa37b --- /dev/null +++ b/t/t9162-git-svn-dcommit-interactive.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# +# Copyright (c) 2011 Frédéric Heitzmann + +test_description='git svn dcommit --interactive series' +. ./lib-git-svn.sh + +test_expect_success 'initialize repo' ' + svn_cmd mkdir -m"mkdir test-interactive" "$svnrepo/test-interactive" && + git svn clone "$svnrepo/test-interactive" test-interactive && + cd test-interactive && + touch foo && git add foo && git commit -m"foo: first commit" && + git svn dcommit + ' + +test_expect_success 'answers: y [\n] yes' ' + ( + echo "change #1" >> foo && git commit -a -m"change #1" && + echo "change #2" >> foo && git commit -a -m"change #2" && + echo "change #3" >> foo && git commit -a -m"change #3" && + ( echo "y + +y" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) && + test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn) + ) + ' + +test_expect_success 'answers: yes yes no' ' + ( + echo "change #1" >> foo && git commit -a -m"change #1" && + echo "change #2" >> foo && git commit -a -m"change #2" && + echo "change #3" >> foo && git commit -a -m"change #3" && + ( echo "yes +yes +no" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) && + test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) && + git reset --hard remotes/git-svn + ) + ' + +test_expect_success 'answers: yes quit' ' + ( + echo "change #1" >> foo && git commit -a -m"change #1" && + echo "change #2" >> foo && git commit -a -m"change #2" && + echo "change #3" >> foo && git commit -a -m"change #3" && + ( echo "yes +quit" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) && + test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) && + git reset --hard remotes/git-svn + ) + ' + +test_expect_success 'answers: all' ' + ( + echo "change #1" >> foo && git commit -a -m"change #1" && + echo "change #2" >> foo && git commit -a -m"change #2" && + echo "change #3" >> foo && git commit -a -m"change #3" && + ( echo "all" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) && + test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn) && + git reset --hard remotes/git-svn + ) + ' + +test_done diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index bd32b91d8f..438aaf6b14 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -2237,7 +2237,7 @@ test_expect_success 'R: cat-blob-fd must be a nonnegative integer' ' test_must_fail git fast-import --cat-blob-fd=-1 </dev/null ' -test_expect_success 'R: print old blob' ' +test_expect_success NOT_MINGW 'R: print old blob' ' blob=$(echo "yes it can" | git hash-object -w --stdin) && cat >expect <<-EOF && ${blob} blob 11 @@ -2249,7 +2249,7 @@ test_expect_success 'R: print old blob' ' test_cmp expect actual ' -test_expect_success 'R: in-stream cat-blob-fd not respected' ' +test_expect_success NOT_MINGW 'R: in-stream cat-blob-fd not respected' ' echo hello >greeting && blob=$(git hash-object -w greeting) && cat >expect <<-EOF && @@ -2270,7 +2270,7 @@ test_expect_success 'R: in-stream cat-blob-fd not respected' ' test_cmp expect actual.1 ' -test_expect_success 'R: print new blob' ' +test_expect_success NOT_MINGW 'R: print new blob' ' blob=$(echo "yep yep yep" | git hash-object --stdin) && cat >expect <<-EOF && ${blob} blob 12 @@ -2288,7 +2288,7 @@ test_expect_success 'R: print new blob' ' test_cmp expect actual ' -test_expect_success 'R: print new blob by sha1' ' +test_expect_success NOT_MINGW 'R: print new blob by sha1' ' blob=$(echo "a new blob named by sha1" | git hash-object --stdin) && cat >expect <<-EOF && ${blob} blob 25 diff --git a/t/t9901-git-web--browse.sh b/t/t9901-git-web--browse.sh index 7906e5d032..b0a6bad8dd 100755 --- a/t/t9901-git-web--browse.sh +++ b/t/t9901-git-web--browse.sh @@ -7,31 +7,32 @@ This test checks that git web--browse can handle various valid URLs.' . ./test-lib.sh +test_web_browse () { + # browser=$1 url=$2 + git web--browse --browser="$1" "$2" >actual && + tr -d '\015' <actual >text && + test_cmp expect text +} + test_expect_success \ 'URL with an ampersand in it' ' echo http://example.com/foo\&bar >expect && git config browser.custom.cmd echo && - git web--browse --browser=custom \ - http://example.com/foo\&bar >actual && - test_cmp expect actual + test_web_browse custom http://example.com/foo\&bar ' test_expect_success \ 'URL with a semi-colon in it' ' echo http://example.com/foo\;bar >expect && git config browser.custom.cmd echo && - git web--browse --browser=custom \ - http://example.com/foo\;bar >actual && - test_cmp expect actual + test_web_browse custom http://example.com/foo\;bar ' test_expect_success \ 'URL with a hash in it' ' echo http://example.com/foo#bar >expect && git config browser.custom.cmd echo && - git web--browse --browser=custom \ - http://example.com/foo#bar >actual && - test_cmp expect actual + test_web_browse custom http://example.com/foo#bar ' test_expect_success \ @@ -43,9 +44,7 @@ test_expect_success \ EOF chmod +x "fake browser" && git config browser.w3m.path "`pwd`/fake browser" && - git web--browse --browser=w3m \ - http://example.com/foo >actual && - test_cmp expect actual + test_web_browse w3m http://example.com/foo ' test_expect_success \ @@ -58,9 +57,7 @@ test_expect_success \ done } f" && - git web--browse --browser=custom \ - http://example.com/foo >actual && - test_cmp expect actual + test_web_browse custom http://example.com/foo ' test_done diff --git a/transport.c b/transport.c index c048ef179b..4ff21ad28d 100644 --- a/transport.c +++ b/transport.c @@ -907,7 +907,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; ret->smart_options = NULL; - } else if (is_local(url) && is_file(url)) { + } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; @@ -48,18 +48,20 @@ static int url_decode_char(const char *q) return val; } -static char *url_decode_internal(const char **query, const char *stop_at, - struct strbuf *out, int decode_plus) +static char *url_decode_internal(const char **query, int len, + const char *stop_at, struct strbuf *out, + int decode_plus) { const char *q = *query; - do { + while (len) { unsigned char c = *q; if (!c) break; if (stop_at && strchr(stop_at, c)) { q++; + len--; break; } @@ -68,6 +70,7 @@ static char *url_decode_internal(const char **query, const char *stop_at, if (0 <= val) { strbuf_addch(out, val); q += 3; + len -= 3; continue; } } @@ -77,34 +80,41 @@ static char *url_decode_internal(const char **query, const char *stop_at, else strbuf_addch(out, c); q++; - } while (1); + len--; + } *query = q; return strbuf_detach(out, NULL); } char *url_decode(const char *url) { + return url_decode_mem(url, strlen(url)); +} + +char *url_decode_mem(const char *url, int len) +{ struct strbuf out = STRBUF_INIT; - const char *colon = strchr(url, ':'); + const char *colon = memchr(url, ':', len); /* Skip protocol part if present */ if (colon && url < colon) { strbuf_add(&out, url, colon - url); + len -= colon - url; url = colon; } - return url_decode_internal(&url, NULL, &out, 0); + return url_decode_internal(&url, len, NULL, &out, 0); } char *url_decode_parameter_name(const char **query) { struct strbuf out = STRBUF_INIT; - return url_decode_internal(query, "&=", &out, 1); + return url_decode_internal(query, -1, "&=", &out, 1); } char *url_decode_parameter_value(const char **query) { struct strbuf out = STRBUF_INIT; - return url_decode_internal(query, "&", &out, 1); + return url_decode_internal(query, -1, "&", &out, 1); } void end_url_with_slash(struct strbuf *buf, const char *url) @@ -4,6 +4,7 @@ extern int is_url(const char *url); extern int is_urlschemechar(int first_flag, int ch); extern char *url_decode(const char *url); +extern char *url_decode_mem(const char *url, int len); extern char *url_decode_parameter_name(const char **query); extern char *url_decode_parameter_value(const char **query); diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h index 4beb10c678..00d36c3ac7 100644 --- a/xdiff/xdiff.h +++ b/xdiff/xdiff.h @@ -43,6 +43,7 @@ extern "C" { #define XDL_EMIT_FUNCNAMES (1 << 0) #define XDL_EMIT_COMMON (1 << 1) +#define XDL_EMIT_FUNCCONTEXT (1 << 2) #define XDL_MMB_READONLY (1 << 0) diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 277e2eec5b..2e669c3e25 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -100,14 +100,40 @@ static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, return 0; } +struct func_line { + long len; + char buf[80]; +}; + +static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg, + struct func_line *func_line, long start, long limit) +{ + find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff; + long l, size, step = (start > limit) ? -1 : 1; + char *buf, dummy[1]; + + buf = func_line ? func_line->buf : dummy; + size = func_line ? sizeof(func_line->buf) : sizeof(dummy); + + for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) { + const char *rec; + long reclen = xdl_get_rec(&xe->xdf1, l, &rec); + long len = ff(rec, reclen, buf, size, xecfg->find_func_priv); + if (len >= 0) { + if (func_line) + func_line->len = len; + return l; + } + } + return -1; +} + int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg) { long s1, s2, e1, e2, lctx; xdchange_t *xch, *xche; - char funcbuf[80]; - long funclen = 0; long funclineprev = -1; - find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff; + struct func_line func_line = { 0 }; if (xecfg->flags & XDL_EMIT_COMMON) return xdl_emit_common(xe, xscr, ecb, xecfg); @@ -118,6 +144,17 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); + if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { + long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1); + if (fs1 < 0) + fs1 = 0; + if (fs1 < s1) { + s2 -= s1 - fs1; + s1 = fs1; + } + } + + again: lctx = xecfg->ctxlen; lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1)); lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2)); @@ -125,27 +162,43 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, e1 = xche->i1 + xche->chg1 + lctx; e2 = xche->i2 + xche->chg2 + lctx; + if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { + long fe1 = get_func_line(xe, xecfg, NULL, + xche->i1 + xche->chg1, + xe->xdf1.nrec); + if (fe1 < 0) + fe1 = xe->xdf1.nrec; + if (fe1 > e1) { + e2 += fe1 - e1; + e1 = fe1; + } + + /* + * Overlap with next change? Then include it + * in the current hunk and start over to find + * its new end. + */ + if (xche->next) { + long l = xche->next->i1; + if (l <= e1 || + get_func_line(xe, xecfg, NULL, l, e1) < 0) { + xche = xche->next; + goto again; + } + } + } + /* * Emit current hunk header. */ if (xecfg->flags & XDL_EMIT_FUNCNAMES) { - long l; - for (l = s1 - 1; l >= 0 && l > funclineprev; l--) { - const char *rec; - long reclen = xdl_get_rec(&xe->xdf1, l, &rec); - long newfunclen = ff(rec, reclen, funcbuf, - sizeof(funcbuf), - xecfg->find_func_priv); - if (newfunclen >= 0) { - funclen = newfunclen; - break; - } - } + get_func_line(xe, xecfg, &func_line, + s1 - 1, funclineprev); funclineprev = s1 - 1; } if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, - funcbuf, funclen, ecb) < 0) + func_line.buf, func_line.len, ecb) < 0) return -1; /* |