From: Junio C Hamano Date: Tue, 8 Jan 2013 21:22:32 +0000 (-0800) Subject: Merge branch 'jk/pathspec-literal' X-Git-Tag: v1.8.2-rc0~171 X-Git-Url: https://www.git.lorimer.id.au/gitweb.git/diff_plain/7e7d71e7ced3d160e45da07ef28457cb82ebd70d?hp=6af95e8cbe431480e4504e7848c66c523beda2a4 Merge branch 'jk/pathspec-literal' Finishing touches to fix a test breakage on Windows * jk/pathspec-literal: t6130-pathspec-noglob: Windows does not allow a file named "f*" --- diff --git a/.gitignore b/.gitignore index f702415c12..8e8dc275db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ /GIT-BUILD-OPTIONS /GIT-CFLAGS /GIT-LDFLAGS -/GIT-GUI-VARS /GIT-PREFIX +/GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-USER-AGENT /GIT-VERSION-FILE @@ -124,7 +124,7 @@ /git-remote-ftps /git-remote-fd /git-remote-ext -/git-remote-testgit +/git-remote-testpy /git-remote-testsvn /git-repack /git-replace @@ -171,7 +171,6 @@ /git-whatchanged /git-write-tree /git-core-*/?* -/gitk-git/gitk-wish /gitweb/GITWEB-BUILD-OPTIONS /gitweb/gitweb.cgi /gitweb/static/gitweb.js diff --git a/.mailmap b/.mailmap index bcf4f8770f..c7e8618300 100644 --- a/.mailmap +++ b/.mailmap @@ -9,7 +9,9 @@ Alex Bennée Alexander Gavrilov Aneesh Kumar K.V Brian M. Carlson +Cheng Renquan Chris Shoemaker +Dan Johnson Dana L. How Dana L. How Daniel Barkalow @@ -18,14 +20,18 @@ David Kågedal David S. Miller Deskin Miller Dirk Süsserott +Eric S. Raymond Erik Faye-Lund Fredrik Kuivinen +Frédéric Heitzmann H. Peter Anvin H. Peter Anvin H. Peter Anvin Horst H. von Brand İsmail Dönmez +Jakub Narębski Jay Soffian +Jeff King Joachim Berdal Haga Johannes Sixt Johannes Sixt @@ -41,12 +47,21 @@ Junio C Hamano Junio C Hamano Junio C Hamano Karl Hasselström +Kevin Leung Kent Engstrom Lars Doelle Lars Doelle Li Hong +Linus Torvalds +Linus Torvalds +Linus Torvalds +Linus Torvalds +Linus Torvalds +Linus Torvalds Lukas Sandström -Martin Langhoff +Marc-André Lureau +Mark Rada +Martin Langhoff Martin von Zweigbergk Michael Coleman Michael J Gruber @@ -63,11 +78,13 @@ Ralf Thielow Ramsay Allan Jones René Scharfe Robert Fitzsimons +Robert Zeh Sam Vilain Santi Béjar Sean Estabrooks Shawn O. Pearce Steven Grimm +Tay Ray Chuan Theodore Ts'o Thomas Rast Tony Luck diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 57da6aadeb..69f7e9b76c 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -112,6 +112,14 @@ For C programs: - We try to keep to at most 80 characters per line. + - We try to support a wide range of C compilers to compile git with, + including old ones. That means that you should not use C99 + initializers, even if a lot of compilers grok it. + + - Variables have to be declared at the beginning of the block. + + - NULL pointers shall be written as NULL, not as 0. + - When declaring pointers, the star sides with the variable name, i.e. "char *string", not "char* string" or "char * string". This makes it easier to understand code diff --git a/Documentation/Makefile b/Documentation/Makefile index 7df75d0b9d..e53d333e5c 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -21,6 +21,7 @@ ARTICLES += git-tools ARTICLES += git-bisect-lk2009 # with their own formatting rules. SP_ARTICLES = user-manual +SP_ARTICLES += howto/new-command SP_ARTICLES += howto/revert-branch-rebase SP_ARTICLES += howto/using-merge-subtree SP_ARTICLES += howto/using-signed-tag-in-pull-request @@ -330,7 +331,7 @@ $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml howto-index.txt: howto-index.sh $(wildcard howto/*.txt) $(QUIET_GEN)$(RM) $@+ $@ && \ - '$(SHELL_PATH_SQ)' ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \ + '$(SHELL_PATH_SQ)' ./howto-index.sh $(sort $(wildcard howto/*.txt)) >$@+ && \ mv $@+ $@ $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt diff --git a/Documentation/RelNotes/1.8.0.3.txt b/Documentation/RelNotes/1.8.0.3.txt new file mode 100644 index 0000000000..92b1e4b363 --- /dev/null +++ b/Documentation/RelNotes/1.8.0.3.txt @@ -0,0 +1,14 @@ +Git v1.8.0.3 Release Notes +========================== + +Fixes since v1.8.0.2 +-------------------- + + * "git log -p -S" did not apply the textconv filter while + looking for the . + + * In the documentation, some invalid example e-mail addresses were + formatted into mailto: links. + +Also contains many documentation updates backported from the 'master' +branch that is preparing for the upcoming 1.8.1 release. diff --git a/Documentation/RelNotes/1.8.1.txt b/Documentation/RelNotes/1.8.1.txt index fec1a06ec9..d6f9555923 100644 --- a/Documentation/RelNotes/1.8.1.txt +++ b/Documentation/RelNotes/1.8.1.txt @@ -29,24 +29,17 @@ UI, Workflows & Features * Command-line completion scripts for tcsh and zsh have been added. - * A new remote-helper interface for Mercurial has been added to - contrib/remote-helpers. + * "git-prompt" scriptlet (in contrib/completion) can be told to paint + pieces of the hints in the prompt string in colors. + + * Some documentation pages that used to ship only in the plain text + format are now formatted in HTML as well. * We used to have a workaround for a bug in ancient "less" that causes it to exit without any output when the terminal is resized. The bug has been fixed in "less" version 406 (June 2007), and the workaround has been removed in this release. - * Some documentation pages that used to ship only in the plain text - format are now formatted in HTML as well. - - * "git-prompt" scriptlet (in contrib/completion) can be told to paint - pieces of the hints in the prompt string in colors. - - * A new configuration variable "diff.context" can be used to - give the default number of context lines in the patch output, to - override the hardcoded default of 3 lines. - * When "git checkout" checks out a branch, it tells the user how far behind (or ahead) the new branch is relative to the remote tracking branch it builds upon. The message now also advises how to sync @@ -60,13 +53,17 @@ UI, Workflows & Features API regression but it is expected that nobody will notice it in practice. - * "git log -p -S" now looks for the after applying - the textconv filter (if defined); earlier it inspected the contents - of the blobs without filtering. + * A new configuration variable "diff.context" can be used to + give the default number of context lines in the patch output, to + override the hardcoded default of 3 lines. * "git format-patch" learned the "--notes=" option to give notes for the commit after the three-dash lines in its output. + * "git log -p -S" now looks for the after applying + the textconv filter (if defined); earlier it inspected the contents + of the blobs without filtering. + * "git log --grep=" learned to honor the "grep.patterntype" configuration set to "perl". @@ -116,6 +113,9 @@ Foreign Interface * The remote helper interface to interact with subversion repositories (one of the GSoC 2012 projects) has been merged. + * A new remote-helper interface for Mercurial has been added to + contrib/remote-helpers. + * The documentation for git(1) was pointing at a page at an external site for the list of authors that no longer existed. The link has been updated to point at an alternative site. diff --git a/Documentation/RelNotes/1.8.2.txt b/Documentation/RelNotes/1.8.2.txt new file mode 100644 index 0000000000..92de751f3f --- /dev/null +++ b/Documentation/RelNotes/1.8.2.txt @@ -0,0 +1,147 @@ +Git v1.8.2 Release Notes +======================== + +Backward compatibility notes +---------------------------- + +In the upcoming major release (tentatively called 1.8.2), we will +change the behavior of the "git push" command. + +When "git push [$there]" does not say what to push, we have used the +traditional "matching" semantics so far (all your branches were sent +to the remote as long as there already are branches of the same name +over there). We will use the "simple" semantics that pushes the +current branch to the branch with the same name, only when the current +branch is set to integrate with that remote branch. There is a user +preference configuration variable "push.default" to change this. + + +Updates since v1.8.1 +-------------------- + +UI, Workflows & Features + + * Initial ports to QNX and z/OS UNIX System Services have started. + + * Output from the tests is coloured using "green is okay, yellow is + questionable, red is bad and blue is informative" scheme. + + * In bare repositories, "git shortlog" and other commands now read + mailmap files from the tip of the history, to help running these + tools in server settings. + + * Color specifiers, e.g. "%C(blue)Hello%C(reset)", used in the + "--format=" option of "git log" and friends can be disabled when + the output is not sent to a terminal by prefixing them with + "auto,", e.g. "%C(auto,blue)Hello%C(auto,reset)". + + * Scripts can ask Git that wildcard patterns in pathspecs they give do + not have any significance, i.e. take them as literal strings. + + * "git fetch --mirror" and fetch that uses other forms of refspec + with wildcard used to attempt to update a symbolic ref that match + the wildcard on the receiving end, which made little sense (the + real ref that is pointed at by the symbolic ref would be updated + anyway). Symbolic refs no longer are affected by such a fetch. + + * "git push" now requires "-f" to update a tag, even if it is a + fast-forward, as tags are meant to be fixed points. + + * "git submodule" started learning a new mode to integrate with the + tip of the remote branch (as opposed to integrating with the commit + recorded in the superproject's gitlink). + + +Foreign Interface + + * "git fast-export" has been updated for its use in the context of + the remote helper interface. + + +Performance, Internal Implementation, etc. + + * "git fsck" has been taught to be pickier about entries in tree + objects that should not be there, e.g. ".", ".git", and "..". + + * Matching paths with common forms of pathspecs that contain wildcard + characters has been optimized further. + + * The implementation of "imap-send" has been updated to reuse xml + quoting code from http-push codepath. + + +Also contains minor documentation updates and code clean-ups. + + +Fixes since v1.8.1 +------------------ + +Unless otherwise noted, all the fixes since v1.8.1 in the maintenance +track are contained in this release (see release notes to them for +details). + + * An element on GIT_CEILING_DIRECTORIES list that does not name the + real path to a directory (i.e. a symbolic link) could have caused + the GIT_DIR discovery logic to escape the ceiling. + (merge 059b379 mh/ceiling later to maint). + + * t4014, t9502 and t0200 tests had various portability issues that + broke on OpenBSD. + (merge 27f6342 jc/maint-test-portability later to maint). + + * t9020 and t3600 tests had various portability issues. + (merge 5a02966 jc/test-portability later to maint). + + * t9200 runs "cvs init" on a directory that already exists, but a + platform can configure this fail for the current user (e.g. you + need to be in the cvsadmin group on NetBSD 6.0). + (merge 8666df0 jc/test-cvs-no-init-in-existing-dir later to maint). + + * The behaviour visible to the end users was confusing, when they + attempt to kill a process spawned in the editor that was in turn + launched by Git with SIGINT (or SIGQUIT), as Git would catch that + signal and die. We ignore these signals now. + (merge 1250857 pf/editor-ignore-sigint later to maint). + + * After failing to create a temporary file using mkstemp(), failing + pathname was not reported correctly on some platforms. + (merge f7be59b jc/mkstemp-more-careful-error-reporting later to maint). + + * The attribute mechanism didn't allow limiting attributes to be + applied to only a single directory itself with "path/" like the + exclude mechanism does. + (merge 94bc671 ja/directory-attrs later to maint). + + * The way "git svn" asked for password using SSH_ASKPASS and + GIT_ASKPASS was not in line with the rest of the system. + (merge e9263e4 ss/svn-prompt later to maint). + + * The --graph code fell into infinite loop when asked to do what the + code did not expect. + (merge 656197a mk/maint-graph-infinity-loop later to maint). + + * http transport was wrong to ask for the username when the + authentication is done by certificate identity. + (merge 75e9a40 rb/http-cert-cred-no-username-prompt later to maint). + + * "git pack-refs" that ran in parallel to another process that + created new refs had a nasty race. + (merge b3f1280 jk/repack-ref-racefix later to maint). + + * After "git add -N" and then writing a tree object out of the + index, the cache-tree data structure got corrupted. + (merge eec3e7e nd/invalidate-i-t-a-cache-tree later to maint). + + * "gitweb", when sorting by age to show repositories with new + activities first, used to sort repositories with absolutely + nothing in it early, which was not very useful. + (merge 28dae18 md/gitweb-sort-by-age later to maint). + + * When a line to be wrapped has a solid run of non space characters + whose length exactly is the wrap width, "git shortlog -w" failed + to add a newline after such a line. + (merge e0db176 sp/shortlog-missing-lf later to maint). + + * Some shells do not behave correctly when IFS is unset; work it + around by explicitly setting it to the default value. + (merge 393050c jc/maint-fbsd-sh-ifs-workaround later to maint). diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 3d8b2fe4d1..75935d500d 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -9,6 +9,14 @@ Checklist (and a short version for the impatient): - the first line of the commit message should be a short description (50 characters is the soft limit, see DISCUSSION in git-commit(1)), and should skip the full stop + - it is also conventional in most cases to prefix the + first line with "area: " where the area is a filename + or identifier for the general area of the code being + modified, e.g. + . archive: ustar header checksum is computed unsigned + . git-cherry-pick.txt: clarify the use of revision range notation + (if in doubt which identifier to use, run "git log --no-merges" + on the files you are modifying to see the current conventions) - the body should provide a meaningful commit message, which: . explains the problem the change tries to solve, iow, what is wrong with the current code without the change. @@ -119,19 +127,6 @@ in templates/hooks--pre-commit. To help ensure this does not happen, run git diff --check on your changes before you commit. -(1a) Try to be nice to older C compilers - -We try to support a wide range of C compilers to compile -git with. That means that you should not use C99 initializers, even -if a lot of compilers grok it. - -Also, variables have to be declared at the beginning of the block -(you can check this with gcc, using the -Wdeclaration-after-statement -option). - -Another thing: NULL pointers shall be written as NULL, not as 0. - - (2) Generate your patch using git tools out of your commits. git based diff tools generate unidiff which is the preferred format. diff --git a/Documentation/config.txt b/Documentation/config.txt index bf8f911e1f..53c4ca1d7a 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -140,10 +140,11 @@ advice.*:: can tell Git that you do not need help by setting these to 'false': + -- - pushNonFastForward:: + pushUpdateRejected:: Set this variable to 'false' if you want to disable - 'pushNonFFCurrent', 'pushNonFFDefault', and - 'pushNonFFMatching' simultaneously. + 'pushNonFFCurrent', 'pushNonFFDefault', + 'pushNonFFMatching', and 'pushAlreadyExists' + simultaneously. pushNonFFCurrent:: Advice shown when linkgit:git-push[1] fails due to a non-fast-forward update to the current branch. @@ -158,6 +159,9 @@ advice.*:: 'matching refs' explicitly (i.e. you used ':', or specified a refspec that isn't your current branch) and it resulted in a non-fast-forward error. + pushAlreadyExists:: + Shown when linkgit:git-push[1] rejects an update that + does not qualify for fast-forwarding (e.g., a tag.) statusHints:: Show directions on how to proceed from the current state in the output of linkgit:git-status[1], in @@ -1517,6 +1521,14 @@ mailmap.file:: subdirectory, or somewhere outside of the repository itself. See linkgit:git-shortlog[1] and linkgit:git-blame[1]. +mailmap.blob:: + Like `mailmap.file`, but consider the value as a reference to a + blob in the repository. If both `mailmap.file` and + `mailmap.blob` are given, both are parsed, with entries from + `mailmap.file` taking precedence. In a bare repository, this + defaults to `HEAD:.mailmap`. In a non-bare repository, it + defaults to empty. + man.viewer:: Specify the programs that may be used to display help in the 'man' format. See linkgit:git-help[1]. @@ -1995,6 +2007,12 @@ submodule..update:: URL and other values found in the `.gitmodules` file. See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details. +submodule..branch:: + The remote branch name for a submodule, used by `git submodule + update --remote`. Set this option to override the value found in + the `.gitmodules` file. See linkgit:git-submodule[1] and + linkgit:gitmodules[5] for details. + submodule..fetchRecurseSubmodules:: This option can be used to control recursive fetching of this submodule. It can be overridden by using the --[no-]recurse-submodules diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index b4d6476ac8..6e98bdf149 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -57,14 +57,11 @@ endif::git-pull[] ifndef::git-pull[] -t:: --tags:: - Most of the tags are fetched automatically as branch - heads are downloaded, but tags that do not point at - objects reachable from the branch heads that are being - tracked will not be fetched by this mechanism. This - flag lets all tags and their associated objects be - downloaded. The default behavior for a remote may be - specified with the remote..tagopt setting. See - linkgit:git-config[1]. + This is a short-hand for giving "refs/tags/*:refs/tags/*" + refspec from the command line, to ask all tags to be fetched + and stored locally. Because this acts as an explicit + refspec, the default refspecs (configured with the + remote.$name.fetch variable) are overridden and not used. --recurse-submodules[=yes|on-demand|no]:: This option controls if and under what conditions new commits of diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 7958a47006..6f04d22f5e 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -21,18 +21,34 @@ or the specified tree. If no paths are given, 'git checkout' will also update `HEAD` to set the specified branch as the current branch. -'git checkout' []:: +'git checkout' :: + To prepare for working on , switch to it by updating + the index and the files in the working tree, and by pointing + HEAD at the branch. Local modifications to the files in the + working tree are kept, so that they can be committed to the + . ++ +If is not found but there does exist a tracking branch in +exactly one remote (call it ) with a matching name, treat as +equivalent to ++ +------------ +$ git checkout -b --track / +------------ ++ +You could omit , in which case the command degenerates to +"check out the current branch", which is a glorified no-op with a +rather expensive side-effects to show only the tracking information, +if exists, for the current branch. + 'git checkout' -b|-B []:: -'git checkout' [--detach] []:: - This form switches branches by updating the index, working - tree, and HEAD to reflect the specified branch or commit. -+ -If `-b` is given, a new branch is created as if linkgit:git-branch[1] -were called and then checked out; in this case you can -use the `--track` or `--no-track` options, which will be passed to -'git branch'. As a convenience, `--track` without `-b` implies branch -creation; see the description of `--track` below. + Specifying `-b` causes a new branch to be created as if + linkgit:git-branch[1] were called and then checked out. In + this case you can use the `--track` or `--no-track` options, + which will be passed to 'git branch'. As a convenience, + `--track` without `-b` implies branch creation; see the + description of `--track` below. + If `-B` is given, is created if it doesn't exist; otherwise, it is reset. This is the transactional equivalent of @@ -45,6 +61,21 @@ $ git checkout that is to say, the branch is not reset/created unless "git checkout" is successful. +'git checkout' --detach []:: +'git checkout' :: + + Prepare to work on top of , by detaching HEAD at it + (see "DETACHED HEAD" section), and updating the index and the + files in the working tree. Local modifications to the files + in the working tree are kept, so that the resulting working + tree will be the state recorded in the commit plus the local + modifications. ++ +Passing `--detach` forces this behavior in the case of a (without +the option, giving a branch name to the command would check out the branch, +instead of detaching HEAD at it), or the current commit, +if no is specified. + 'git checkout' [-p|--patch] [] [--] ...:: When or `--patch` are given, 'git checkout' does *not* diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index eaea079165..9ae2508f3f 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -240,6 +240,10 @@ GIT_CONFIG:: Using the "--global" option forces this to ~/.gitconfig. Using the "--system" option forces this to $(prefix)/etc/gitconfig. +GIT_CONFIG_NOSYSTEM:: + Whether to skip reading settings from the system-wide + $(prefix)/etc/gitconfig file. See linkgit:git[1] for details. + See also <>. diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index f8d0819113..f8c06013f3 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -12,6 +12,7 @@ SYNOPSIS 'git diff' [options] [] [--] [...] 'git diff' [options] --cached [] [--] [...] 'git diff' [options] [--] [...] +'git diff' [options] 'git diff' [options] [--no-index] [--] DESCRIPTION @@ -55,6 +56,11 @@ directories. This behavior can be forced by --no-index. This is to view the changes between two arbitrary . +'git diff' [options] :: + + This form is to view the differences between the raw + contents of two blob objects. + 'git diff' [--options] .. [--] [...]:: This is synonymous to the previous form. If on @@ -72,8 +78,7 @@ directories. This behavior can be forced by --no-index. Just in case if you are doing something exotic, it should be noted that all of the in the above description, except in the last two forms that use ".." notations, can be any -. The third form ('git diff ') can also -be used to compare two objects. +. For a more complete list of ways to spell , see "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7]. diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index d1844ead4a..68bca1a29d 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -427,7 +427,7 @@ they made it. Here `` is the person's display name (for example ``Com M Itter'') and `` is the person's email address -(``cm@example.com''). `LT` and `GT` are the literal less-than (\x3c) +(``\cm@example.com''). `LT` and `GT` are the literal less-than (\x3c) and greater-than (\x3e) symbols. These are required to delimit the email address from the other fields in the line. Note that `` and `` are free-form and may contain any sequence diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 585dac40ba..08a185db7f 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -167,7 +167,7 @@ log.showroot:: `git log -p` output would be shown without a diff attached. The default is `true`. -mailmap.file:: +mailmap.*:: See linkgit:git-shortlog[1]. notes.displayRef:: diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 8b637d339f..c964b796be 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -51,10 +51,11 @@ be named. If `:` is omitted, the same ref as will be updated. + The object referenced by is used to update the reference -on the remote side, but by default this is only allowed if the -update can fast-forward . By having the optional leading `+`, -you can tell git to update the ref even when the update is not a -fast-forward. This does *not* attempt to merge into . See +on the remote side. By default this is only allowed if is not +a tag (annotated or lightweight), and then only if it can fast-forward +. By having the optional leading `+`, you can tell git to update +the ref even if it is not allowed by default (e.g., it is not a +fast-forward.) This does *not* attempt to merge into . See EXAMPLES below for details. + `tag ` means the same as `refs/tags/:refs/tags/`. diff --git a/Documentation/git-remote-testgit.txt b/Documentation/git-remote-testgit.txt index 2a67d456a3..612a625ced 100644 --- a/Documentation/git-remote-testgit.txt +++ b/Documentation/git-remote-testgit.txt @@ -19,7 +19,7 @@ testcase for the remote-helper functionality, and as an example to show remote-helper authors one possible implementation. The best way to learn more is to read the comments and source code in -'git-remote-testgit.py'. +'git-remote-testgit'. SEE ALSO -------- diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index b1de3bade7..b1996f1a63 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -13,7 +13,7 @@ SYNOPSIS [--reference ] [--] [] 'git submodule' [--quiet] status [--cached] [--recursive] [--] [...] 'git submodule' [--quiet] init [--] [...] -'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] +'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] [--rebase] [--reference ] [--merge] [--recursive] [--] [...] 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) ] [commit] [--] [...] @@ -208,6 +208,8 @@ OPTIONS -b:: --branch:: Branch of repository to add as submodule. + The name of the branch is recorded as `submodule..branch` in + `.gitmodules` for `update --remote`. -f:: --force:: @@ -236,6 +238,27 @@ OPTIONS (the default). This limit only applies to modified submodules. The size is always limited to 1 for added/deleted/typechanged submodules. +--remote:: + This option is only valid for the update command. Instead of using + the superproject's recorded SHA-1 to update the submodule, use the + status of the submodule's remote tracking branch. The remote used + is branch's remote (`branch..remote`), defaulting to `origin`. + The remote branch used defaults to `master`, but the branch name may + be overridden by setting the `submodule..branch` option in + either `.gitmodules` or `.git/config` (with `.git/config` taking + precedence). ++ +This works for any of the supported update procedures (`--checkout`, +`--rebase`, etc.). The only change is the source of the target SHA-1. +For example, `submodule update --remote --merge` will merge upstream +submodule changes into the submodules, while `submodule update +--merge` will merge superproject gitlink changes into the submodules. ++ +In order to ensure a current tracking branch state, `update --remote` +fetches the submodule's remote repository before calculating the +SHA-1. If you don't want to fetch, you should use `submodule update +--remote --no-fetch`. + -N:: --no-fetch:: This option is only valid for the update command. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 8b0d3adfed..69decb13b0 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -628,10 +628,19 @@ ADVANCED OPTIONS Default: "svn" --follow-parent:: + This option is only relevant if we are tracking branches (using + one of the repository layout options --trunk, --tags, + --branches, --stdlayout). For each tracked branch, try to find + out where its revision was copied from, and set + a suitable parent in the first git commit for the branch. This is especially helpful when we're tracking a directory - that has been moved around within the repository, or if we - started tracking a branch and never tracked the trunk it was - descended from. This feature is enabled by default, use + that has been moved around within the repository. If this + feature is disabled, the branches created by 'git svn' will all + be linear and not share any history, meaning that there will be + no information on where branches were branched off or merged. + However, following long/convoluted histories can take a long + time, so disabling this feature may speed up the cloning + process. This feature is enabled by default, use --no-follow-parent to disable it. + [verse] @@ -739,7 +748,8 @@ for rewriteRoot and rewriteUUID which can be used together. BASIC EXAMPLES -------------- -Tracking and contributing to the trunk of a Subversion-managed project: +Tracking and contributing to the trunk of a Subversion-managed project +(ignoring tags and branches): ------------------------------------------------------------------------ # Clone a repo (like git clone): @@ -764,8 +774,10 @@ Tracking and contributing to an entire Subversion-managed project (complete with a trunk, tags and branches): ------------------------------------------------------------------------ -# Clone a repo (like git clone): - git svn clone http://svn.example.com/project -T trunk -b branches -t tags +# Clone a repo with standard SVN directory layout (like git clone): + git svn clone http://svn.example.com/project --stdlayout +# Or, if the repo uses a non-standard directory layout: + git svn clone http://svn.example.com/project -T tr -b branch -t tag # View all branches and tags you have cloned: git branch -r # Create a new branch in SVN @@ -830,6 +842,52 @@ inside git back upstream to SVN users. Therefore it is advised that users keep history as linear as possible inside git to ease compatibility with SVN (see the CAVEATS section below). +HANDLING OF SVN BRANCHES +------------------------ +If 'git svn' is configured to fetch branches (and --follow-branches +is in effect), it sometimes creates multiple git branches for one +SVN branch, where the addtional branches have names of the form +'branchname@nnn' (with nnn an SVN revision number). These additional +branches are created if 'git svn' cannot find a parent commit for the +first commit in an SVN branch, to connect the branch to the history of +the other branches. + +Normally, the first commit in an SVN branch consists +of a copy operation. 'git svn' will read this commit to get the SVN +revision the branch was created from. It will then try to find the +git commit that corresponds to this SVN revision, and use that as the +parent of the branch. However, it is possible that there is no suitable +git commit to serve as parent. This will happen, among other reasons, +if the SVN branch is a copy of a revision that was not fetched by 'git +svn' (e.g. because it is an old revision that was skipped with +'--revision'), or if in SVN a directory was copied that is not tracked +by 'git svn' (such as a branch that is not tracked at all, or a +subdirectory of a tracked branch). In these cases, 'git svn' will still +create a git branch, but instead of using an existing git commit as the +parent of the branch, it will read the SVN history of the directory the +branch was copied from and create appropriate git commits. This is +indicated by the message "Initializing parent: ". + +Additionally, it will create a special branch named +'@', where is the SVN revision +number the branch was copied from. This branch will point to the newly +created parent commit of the branch. If in SVN the branch was deleted +and later recreated from a different version, there will be multiple +such branches with an '@'. + +Note that this may mean that multiple git commits are created for a +single SVN revision. + +An example: in an SVN repository with a standard +trunk/tags/branches layout, a directory trunk/sub is created in r.100. +In r.200, trunk/sub is branched by copying it to branches/. 'git svn +clone -s' will then create a branch 'sub'. It will also create new git +commits for r.100 through r.199 and use these as the history of branch +'sub'. Thus there will be two git commits for each revision from r.100 +to r.199 (one containing trunk/, one containing trunk/sub/). Finally, +it will create a branch 'sub@200' pointing to the new parent commit of +branch 'sub' (i.e. the commit for r.200 and trunk/sub/). + CAVEATS ------- @@ -871,6 +929,21 @@ already dcommitted. It is considered bad practice to --amend commits you've already pushed to a remote repository for other users, and dcommit with SVN is analogous to that. +When cloning an SVN repository, if none of the options for describing +the repository layout is used (--trunk, --tags, --branches, +--stdlayout), 'git svn clone' will create a git repository with +completely linear history, where branches and tags appear as separate +directories in the working copy. While this is the easiest way to get a +copy of a complete repository, for projects with many branches it will +lead to a working copy many times larger than just the trunk. Thus for +projects using the standard directory structure (trunk/branches/tags), +it is recommended to clone with option '--stdlayout'. If the project +uses a non-standard structure, and/or if branches and tags are not +required, it is easiest to only clone one directory (typically trunk), +without giving any repository layout options. If the full history with +branches and tags is required, the options '--trunk' / '--branches' / +'--tags' must be used. + When using multiple --branches or --tags, 'git svn' does not automatically handle name collisions (for example, if two branches from different paths have the same name, or if a branch and a tag have the same name). In these cases, @@ -894,6 +967,12 @@ the possible corner cases (git doesn't do it, either). Committing renamed and copied files is fully supported if they're similar enough for git to detect them. +In SVN, it is possible (though discouraged) to commit changes to a tag +(because a tag is just a directory copy, thus technically the same as a +branch). When cloning an SVN repository, 'git svn' cannot know if such a +commit to a tag will happen in the future. Thus it acts conservatively +and imports all SVN tags as branches, prefixing the tag name with 'tags/'. + CONFIGURATION ------------- diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 247534e908..6470cffd32 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -129,7 +129,7 @@ This option is only applicable when listing tags without annotation lines. CONFIGURATION ------------- By default, 'git tag' in sign-with-default mode (-s) will use your -committer identity (of the form "Your Name ") to +committer identity (of the form "Your Name <\your@email.address>") to find a key. If you want to use a different default key, you can specify it in the repository configuration as follows: diff --git a/Documentation/git.txt b/Documentation/git.txt index 8d869db682..c03b7adc97 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,15 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.8.0.2/git.html[documentation for release 1.8.0.2] +* link:v1.8.1/git.html[documentation for release 1.8.1] * release notes for + link:RelNotes/1.8.1.txt[1.8.1]. + +* link:v1.8.0.3/git.html[documentation for release 1.8.0.3] + +* release notes for + link:RelNotes/1.8.0.3.txt[1.8.0.3], link:RelNotes/1.8.0.2.txt[1.8.0.2], link:RelNotes/1.8.0.1.txt[1.8.0.1], link:RelNotes/1.8.0.txt[1.8.0]. @@ -771,6 +777,14 @@ for further details. and read the password from its STDOUT. See also the 'core.askpass' option in linkgit:git-config[1]. +'GIT_CONFIG_NOSYSTEM':: + Whether to skip reading settings from the system-wide + `$(prefix)/etc/gitconfig` file. This environment variable can + be used along with `$HOME` and `$XDG_CONFIG_HOME` to create a + predictable environment for a picky script, or you can set it + temporarily to avoid using a buggy `/etc/gitconfig` file while + waiting for someone with sufficient permissions to fix it. + 'GIT_FLUSH':: If this environment variable is set to "1", then commands such as 'git blame' (in incremental mode), 'git rev-list', 'git log', diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index ab3e91c054..52d7ae4313 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -49,6 +49,11 @@ submodule..update:: This config option is overridden if 'git submodule update' is given the '--merge', '--rebase' or '--checkout' options. +submodule..branch:: + A remote branch name for tracking updates in the upstream submodule. + If the option is not specified, it defaults to 'master'. See the + `--remote` documentation in linkgit:git-submodule[1] for details. + submodule..fetchRecurseSubmodules:: This option can be used to control recursive fetching of this submodule. If this option is also present in the submodules entry in diff --git a/Documentation/howto/new-command.txt b/Documentation/howto/new-command.txt new file mode 100644 index 0000000000..36502f6718 --- /dev/null +++ b/Documentation/howto/new-command.txt @@ -0,0 +1,104 @@ +From: Eric S. Raymond +Abstract: This is how-to documentation for people who want to add extension + commands to git. It should be read alongside api-builtin.txt. +Content-type: text/asciidoc + +How to integrate new subcommands +================================ + +This is how-to documentation for people who want to add extension +commands to git. It should be read alongside api-builtin.txt. + +Runtime environment +------------------- + +git subcommands are standalone executables that live in the git exec +path, normally /usr/lib/git-core. The git executable itself is a +thin wrapper that knows where the subcommands live, and runs them by +passing command-line arguments to them. + +(If "git foo" is not found in the git exec path, the wrapper +will look in the rest of your $PATH for it. Thus, it's possible +to write local git extensions that don't live in system space.) + +Implementation languages +------------------------ + +Most subcommands are written in C or shell. A few are written in +Perl. + +While we strongly encourage coding in portable C for portability, +these specific scripting languages are also acceptable. We won't +accept more without a very strong technical case, as we don't want +to broaden the git suite's required dependencies. Import utilities, +surgical tools, remote helpers and other code at the edges of the +git suite are more lenient and we allow Python (and even Tcl/tk), +but they should not be used for core functions. + +This may change in the future. Especially Python is not allowed in +core because we need better Python integration in the git Windows +installer before we can be confident people in that environment +won't experience an unacceptably large loss of capability. + +C commands are normally written as single modules, named after the +command, that link a collection of functions called libgit. Thus, +your command 'git-foo' would normally be implemented as a single +"git-foo.c" (or "builtin/foo.c" if it is to be linked to the main +binary); this organization makes it easy for people reading the code +to find things. + +See the CodingGuidelines document for other guidance on what we consider +good practice in C and shell, and api-builtin.txt for the support +functions available to built-in commands written in C. + +What every extension command needs +---------------------------------- + +You must have a man page, written in asciidoc (this is what git help +followed by your subcommand name will display). Be aware that there is +a local asciidoc configuration and macros which you should use. It's +often helpful to start by cloning an existing page and replacing the +text content. + +You must have a test, written to report in TAP (Test Anything Protocol). +Tests are executables (usually shell scripts) that live in the 't' +subdirectory of the tree. Each test name begins with 't' and a sequence +number that controls where in the test sequence it will be executed; +conventionally the rest of the name stem is that of the command +being tested. + +Read the file t/README to learn more about the conventions to be used +in writing tests, and the test support library. + +Integrating a command +--------------------- + +Here are the things you need to do when you want to merge a new +subcommand into the git tree. + +1. Don't forget to sign off your patch! + +2. Append your command name to one of the variables BUILTIN_OBJS, +EXTRA_PROGRAMS, SCRIPT_SH, SCRIPT_PERL or SCRIPT_PYTHON. + +3. Drop its test in the t directory. + +4. If your command is implemented in an interpreted language with a +p-code intermediate form, make sure .gitignore in the main directory +includes a pattern entry that ignores such files. Python .pyc and +.pyo files will already be covered. + +5. If your command has any dependency on a particular version of +your language, document it in the INSTALL file. + +6. There is a file command-list.txt in the distribution main directory +that categorizes commands by type, so they can be listed in appropriate +subsections in the documentation's summary command list. Add an entry +for yours. To understand the categories, look at git-cmmands.txt +in the main directory. + +7. Give the maintainer one paragraph to include in the RelNotes file +to describe the new feature; a good place to do so is in the cover +letter [PATCH 0/n]. + +That's all there is to it. diff --git a/Documentation/mailmap.txt b/Documentation/mailmap.txt index 288f04e70c..4a8c276529 100644 --- a/Documentation/mailmap.txt +++ b/Documentation/mailmap.txt @@ -1,5 +1,6 @@ If the file `.mailmap` exists at the toplevel of the repository, or at -the location pointed to by the mailmap.file configuration option, it +the location pointed to by the mailmap.file or mailmap.blob +configuration options, it is used to map author and committer names and email addresses to canonical real names and email addresses. @@ -46,7 +47,7 @@ Jane Doe Joe R. Developer ------------ -Note how there is no need for an entry for , because the +Note how there is no need for an entry for ``, because the real name of that author is already correct. Example 2: Your repository contains commits from the following diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index d9eddedc72..105f18a6f9 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -144,7 +144,11 @@ The placeholders are: - '%Cgreen': switch color to green - '%Cblue': switch color to blue - '%Creset': reset color -- '%C(...)': color specification, as described in color.branch.* config option +- '%C(...)': color specification, as described in color.branch.* config option; + adding `auto,` at the beginning will emit color only when colors are + enabled for log output (by `color.diff`, `color.ui`, or `--color`, and + respecting the `auto` settings of the former if we are going to a + terminal) - '%m': left, right or boundary mark - '%n': newline - '%%': a raw '%' diff --git a/Documentation/technical/api-command.txt b/Documentation/technical/api-command.txt deleted file mode 100644 index d3b978177b..0000000000 --- a/Documentation/technical/api-command.txt +++ /dev/null @@ -1,99 +0,0 @@ -Integrating new subcommands -=========================== - -This is how-to documentation for people who want to add extension -commands to git. It should be read alongside api-builtin.txt. - -Runtime environment -------------------- - -git subcommands are standalone executables that live in the git exec -path, normally /usr/lib/git-core. The git executable itself is a -thin wrapper that knows where the subcommands live, and runs them by -passing command-line arguments to them. - -(If "git foo" is not found in the git exec path, the wrapper -will look in the rest of your $PATH for it. Thus, it's possible -to write local git extensions that don't live in system space.) - -Implementation languages ------------------------- - -Most subcommands are written in C or shell. A few are written in -Perl. - -While we strongly encourage coding in portable C for portability, -these specific scripting languages are also acceptable. We won't -accept more without a very strong technical case, as we don't want -to broaden the git suite's required dependencies. Import utilities, -surgical tools, remote helpers and other code at the edges of the -git suite are more lenient and we allow Python (and even Tcl/tk), -but they should not be used for core functions. - -This may change in the future. Especially Python is not allowed in -core because we need better Python integration in the git Windows -installer before we can be confident people in that environment -won't experience an unacceptably large loss of capability. - -C commands are normally written as single modules, named after the -command, that link a collection of functions called libgit. Thus, -your command 'git-foo' would normally be implemented as a single -"git-foo.c" (or "builtin/foo.c" if it is to be linked to the main -binary); this organization makes it easy for people reading the code -to find things. - -See the CodingGuidelines document for other guidance on what we consider -good practice in C and shell, and api-builtin.txt for the support -functions available to built-in commands written in C. - -What every extension command needs ----------------------------------- - -You must have a man page, written in asciidoc (this is what git help -followed by your subcommand name will display). Be aware that there is -a local asciidoc configuration and macros which you should use. It's -often helpful to start by cloning an existing page and replacing the -text content. - -You must have a test, written to report in TAP (Test Anything Protocol). -Tests are executables (usually shell scripts) that live in the 't' -subdirectory of the tree. Each test name begins with 't' and a sequence -number that controls where in the test sequence it will be executed; -conventionally the rest of the name stem is that of the command -being tested. - -Read the file t/README to learn more about the conventions to be used -in writing tests, and the test support library. - -Integrating a command ---------------------- - -Here are the things you need to do when you want to merge a new -subcommand into the git tree. - -1. Don't forget to sign off your patch! - -2. Append your command name to one of the variables BUILTIN_OBJS, -EXTRA_PROGRAMS, SCRIPT_SH, SCRIPT_PERL or SCRIPT_PYTHON. - -3. Drop its test in the t directory. - -4. If your command is implemented in an interpreted language with a -p-code intermediate form, make sure .gitignore in the main directory -includes a pattern entry that ignores such files. Python .pyc and -.pyo files will already be covered. - -5. If your command has any dependency on a particular version of -your language, document it in the INSTALL file. - -6. There is a file command-list.txt in the distribution main directory -that categorizes commands by type, so they can be listed in appropriate -subsections in the documentation's summary command list. Add an entry -for yours. To understand the categories, look at git-cmmands.txt -in the main directory. - -7. Give the maintainer one paragraph to include in the RelNotes file -to describe the new feature; a good place to do so is in the cover -letter [PATCH 0/n]. - -That's all there is to it. diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt index d6fc90ac7e..18142b6d29 100644 --- a/Documentation/technical/api-history-graph.txt +++ b/Documentation/technical/api-history-graph.txt @@ -33,11 +33,11 @@ The following utility functions are wrappers around `graph_next_line()` and They can all be called with a NULL graph argument, in which case no graph output will be printed. -* `graph_show_commit()` calls `graph_next_line()` until it returns non-zero. - This prints all graph lines up to, and including, the line containing this - commit. Output is printed to stdout. The last line printed does not contain - a terminating newline. This should not be called if the commit line has - already been printed, or it will loop forever. +* `graph_show_commit()` calls `graph_next_line()` and + `graph_is_commit_finished()` until one of them return non-zero. This prints + all graph lines up to, and including, the line containing this commit. + Output is printed to stdout. The last line printed does not contain a + terminating newline. * `graph_show_oneline()` calls `graph_next_line()` and prints the result to stdout. The line printed does not contain a terminating newline. diff --git a/Documentation/technical/api-index-skel.txt b/Documentation/technical/api-index-skel.txt index af7cc2e395..730cfacf78 100644 --- a/Documentation/technical/api-index-skel.txt +++ b/Documentation/technical/api-index-skel.txt @@ -11,5 +11,3 @@ documents them. //////////////////////////////////////////////////////////////// // table of contents end //////////////////////////////////////////////////////////////// - -2007-11-24 diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt index 7386bcab3e..20be348834 100644 --- a/Documentation/technical/api-string-list.txt +++ b/Documentation/technical/api-string-list.txt @@ -82,14 +82,6 @@ Functions call free() on the util members of any items that have to be deleted. Preserve the order of the items that are retained. -`string_list_longest_prefix`:: - - Return the longest string within a string_list that is a - prefix (in the sense of prefixcmp()) of the specified string, - or NULL if no such prefix exists. This function does not - require the string_list to be sorted (it does a linear - search). - `print_string_list`:: Dump a string_list to stdout, useful mainly for debugging purposes. It diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt index 57d6f915b1..7324154838 100644 --- a/Documentation/technical/index-format.txt +++ b/Documentation/technical/index-format.txt @@ -161,8 +161,9 @@ GIT index format this span of index as a tree. An entry can be in an invalidated state and is represented by having - -1 in the entry_count field. In this case, there is no object name - and the next entry starts immediately after the newline. + a negative number in the entry_count field. In this case, there is no + object name and the next entry starts immediately after the newline. + When writing an invalid entry, -1 should always be used as entry_count. The entries are written out in the top-down, depth-first order. The first entry represents the root level of the repository, followed by the diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 1a9e4a20da..e9f7abca91 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.8.1-rc2 +DEF_VER=v1.8.1.GIT LF=' ' diff --git a/Makefile b/Makefile index 736ecd45b5..56424b48b0 100644 --- a/Makefile +++ b/Makefile @@ -74,10 +74,14 @@ all:: # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks # d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7). # +# Define HAVE_STRINGS_H if you have strings.h and need it for strcasecmp. +# # Define NO_STRCASESTR if you don't have strcasestr. # # Define NO_MEMMEM if you don't have memmem. # +# Define NO_GETPAGESIZE if you don't have getpagesize. +# # Define NO_STRLCPY if you don't have strlcpy. # # Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the @@ -165,6 +169,10 @@ all:: # Define NO_POLL if you do not have or don't want to use poll(). # This also implies NO_SYS_POLL_H. # +# Define NEEDS_SYS_PARAM_H if you need to include sys/param.h to compile, +# *PLEASE* REPORT to git@vger.kernel.org if your platform needs this; +# we want to know more about the issue. +# # Define NO_PTHREADS if you do not have or do not want to use Pthreads. # # Define NO_PREAD if you have a problem with pread() system call (e.g. @@ -470,7 +478,7 @@ SCRIPT_PERL += git-relink.perl SCRIPT_PERL += git-send-email.perl SCRIPT_PERL += git-svn.perl -SCRIPT_PYTHON += git-remote-testgit.py +SCRIPT_PYTHON += git-remote-testpy.py SCRIPT_PYTHON += git-p4.py SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ @@ -1353,6 +1361,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL) # Added manually, see above. NEEDS_SSL_WITH_CURL = YesPlease HAVE_LIBCHARSET_H = YesPlease + HAVE_STRINGS_H = YesPlease NEEDS_LIBICONV = YesPlease NEEDS_LIBINTL_BEFORE_LIBICONV = YesPlease NO_SYS_SELECT_H = UnfortunatelyYes @@ -1448,6 +1457,22 @@ else NO_CURL = YesPlease endif endif +ifeq ($(uname_S),QNX) + COMPAT_CFLAGS += -DSA_RESTART=0 + HAVE_STRINGS_H = YesPlease + NEEDS_SOCKET = YesPlease + NO_FNMATCH_CASEFOLD = YesPlease + NO_GETPAGESIZE = YesPlease + NO_ICONV = YesPlease + NO_MEMMEM = YesPlease + NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease + NO_NSEC = YesPlease + NO_PTHREADS = YesPlease + NO_R_TO_GCC_LINKER = YesPlease + NO_STRCASESTR = YesPlease + NO_STRLCPY = YesPlease +endif -include config.mak.autogen -include config.mak @@ -1655,6 +1680,9 @@ endif ifdef NO_D_INO_IN_DIRENT BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT endif +ifdef NO_GECOS_IN_PWENT + BASIC_CFLAGS += -DNO_GECOS_IN_PWENT +endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif @@ -1748,6 +1776,9 @@ endif ifdef NO_SYS_POLL_H BASIC_CFLAGS += -DNO_SYS_POLL_H endif +ifdef NEEDS_SYS_PARAM_H + BASIC_CFLAGS += -DNEEDS_SYS_PARAM_H +endif ifdef NO_INTTYPES_H BASIC_CFLAGS += -DNO_INTTYPES_H endif @@ -1859,6 +1890,9 @@ ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM COMPAT_OBJS += compat/memmem.o endif +ifdef NO_GETPAGESIZE + COMPAT_CFLAGS += -DNO_GETPAGESIZE +endif ifdef INTERNAL_QSORT COMPAT_CFLAGS += -DINTERNAL_QSORT COMPAT_OBJS += compat/qsort.o @@ -1884,6 +1918,10 @@ ifdef HAVE_LIBCHARSET_H EXTLIBS += $(CHARSET_LIB) endif +ifdef HAVE_STRINGS_H + BASIC_CFLAGS += -DHAVE_STRINGS_H +endif + ifdef HAVE_DEV_TTY BASIC_CFLAGS += -DHAVE_DEV_TTY endif @@ -2183,7 +2221,7 @@ endef GIT-SCRIPT-DEFINES: FORCE @FLAGS='$(SCRIPT_DEFINES)'; \ if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \ - echo 1>&2 " * new script parameters"; \ + echo >&2 " * new script parameters"; \ echo "$$FLAGS" >$@; \ fi @@ -2245,7 +2283,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh endif # NO_PERL ifndef NO_PYTHON -$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX +$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py $(QUIET_GEN)$(RM) $@ $@+ && \ INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \ @@ -2564,7 +2602,7 @@ TRACK_PREFIX = $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\ GIT-PREFIX: FORCE @FLAGS='$(TRACK_PREFIX)'; \ if test x"$$FLAGS" != x"`cat GIT-PREFIX 2>/dev/null`" ; then \ - echo 1>&2 " * new prefix flags"; \ + echo >&2 " * new prefix flags"; \ echo "$$FLAGS" >GIT-PREFIX; \ fi @@ -2573,7 +2611,7 @@ TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):$(USE_GETTEXT_SCHEME) GIT-CFLAGS: FORCE @FLAGS='$(TRACK_CFLAGS)'; \ if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new build flags"; \ + echo >&2 " * new build flags"; \ echo "$$FLAGS" >GIT-CFLAGS; \ fi @@ -2582,7 +2620,7 @@ TRACK_LDFLAGS = $(subst ','\'',$(ALL_LDFLAGS)) GIT-LDFLAGS: FORCE @FLAGS='$(TRACK_LDFLAGS)'; \ if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new link flags"; \ + echo >&2 " * new link flags"; \ echo "$$FLAGS" >GIT-LDFLAGS; \ fi @@ -2624,14 +2662,14 @@ ifdef GIT_PERF_MAKE_OPTS @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@ endif -### Detect Tck/Tk interpreter path changes -ifndef NO_TCLTK -TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)') +### Detect Python interpreter path changes +ifndef NO_PYTHON +TRACK_PYTHON = $(subst ','\'',-DPYTHON_PATH='$(PYTHON_PATH_SQ)') -GIT-GUI-VARS: FORCE - @VARS='$(TRACK_VARS)'; \ +GIT-PYTHON-VARS: FORCE + @VARS='$(TRACK_PYTHON)'; \ if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \ - echo 1>&2 " * new Tcl/Tk interpreter location"; \ + echo >&2 " * new Python interpreter location"; \ echo "$$VARS" >$@; \ fi endif @@ -2910,8 +2948,8 @@ ifndef NO_TCLTK $(MAKE) -C gitk-git clean $(MAKE) -C git-gui clean endif - $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS - $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES + $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS + $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES GIT-PYTHON-VARS .PHONY: all install profile-clean clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell diff --git a/README b/README index d2690ec8dc..49713ea08e 100644 --- a/README +++ b/README @@ -19,9 +19,10 @@ Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. -Git is an Open Source project covered by the GNU General Public License. -It was originally written by Linus Torvalds with help of a group of -hackers around the net. It is currently maintained by Junio C Hamano. +Git is an Open Source project covered by the GNU General Public +License version 2 (some parts of it are under different licenses, +compatible with the GPLv2). It was originally written by Linus +Torvalds with help of a group of hackers around the net. Please read the file INSTALL for installation instructions. diff --git a/RelNotes b/RelNotes index 28607146f3..bdce3136ea 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes/1.8.1.txt \ No newline at end of file +Documentation/RelNotes/1.8.2.txt \ No newline at end of file diff --git a/abspath.c b/abspath.c index 05f2d79348..40cdc46219 100644 --- a/abspath.c +++ b/abspath.c @@ -15,16 +15,34 @@ int is_directory(const char *path) #define MAXDEPTH 5 /* - * Use this to get the real path, i.e. resolve links. If you want an - * absolute path but don't mind links, use absolute_path. + * Return the real path (i.e., absolute path, with symlinks resolved + * and extra slashes removed) equivalent to the specified path. (If + * you want an absolute path but don't mind links, use + * absolute_path().) The return value is a pointer to a static + * buffer. + * + * The input and all intermediate paths must be shorter than MAX_PATH. + * The directory part of path (i.e., everything up to the last + * dir_sep) must denote a valid, existing directory, but the last + * component need not exist. If die_on_error is set, then die with an + * informative error message if there is a problem. Otherwise, return + * NULL on errors (without generating any output). * * If path is our buffer, then return path, as it's already what the * user wants. */ -const char *real_path(const char *path) +static const char *real_path_internal(const char *path, int die_on_error) { static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1]; + char *retval = NULL; + + /* + * If we have to temporarily chdir(), store the original CWD + * here so that we can chdir() back to it at the end of the + * function: + */ char cwd[1024] = ""; + int buf_index = 1; int depth = MAXDEPTH; @@ -35,11 +53,19 @@ const char *real_path(const char *path) if (path == buf || path == next_buf) return path; - if (!*path) - die("The empty string is not a valid path"); + if (!*path) { + if (die_on_error) + die("The empty string is not a valid path"); + else + goto error_out; + } - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die ("Too long path: %.*s", 60, path); + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) { + if (die_on_error) + die("Too long path: %.*s", 60, path); + else + goto error_out; + } while (depth--) { if (!is_directory(buf)) { @@ -54,20 +80,36 @@ const char *real_path(const char *path) } if (*buf) { - if (!*cwd && !getcwd(cwd, sizeof(cwd))) - die_errno ("Could not get current working directory"); + if (!*cwd && !getcwd(cwd, sizeof(cwd))) { + if (die_on_error) + die_errno("Could not get current working directory"); + else + goto error_out; + } - if (chdir(buf)) - die_errno ("Could not switch to '%s'", buf); + if (chdir(buf)) { + if (die_on_error) + die_errno("Could not switch to '%s'", buf); + else + goto error_out; + } + } + if (!getcwd(buf, PATH_MAX)) { + if (die_on_error) + die_errno("Could not get current working directory"); + else + goto error_out; } - if (!getcwd(buf, PATH_MAX)) - die_errno ("Could not get current working directory"); if (last_elem) { size_t len = strlen(buf); - if (len + strlen(last_elem) + 2 > PATH_MAX) - die ("Too long path name: '%s/%s'", - buf, last_elem); + if (len + strlen(last_elem) + 2 > PATH_MAX) { + if (die_on_error) + die("Too long path name: '%s/%s'", + buf, last_elem); + else + goto error_out; + } if (len && !is_dir_sep(buf[len-1])) buf[len++] = '/'; strcpy(buf + len, last_elem); @@ -77,10 +119,18 @@ const char *real_path(const char *path) if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) { ssize_t len = readlink(buf, next_buf, PATH_MAX); - if (len < 0) - die_errno ("Invalid symlink '%s'", buf); - if (PATH_MAX <= len) - die("symbolic link too long: %s", buf); + if (len < 0) { + if (die_on_error) + die_errno("Invalid symlink '%s'", buf); + else + goto error_out; + } + if (PATH_MAX <= len) { + if (die_on_error) + die("symbolic link too long: %s", buf); + else + goto error_out; + } next_buf[len] = '\0'; buf = next_buf; buf_index = 1 - buf_index; @@ -89,10 +139,23 @@ const char *real_path(const char *path) break; } + retval = buf; +error_out: + free(last_elem); if (*cwd && chdir(cwd)) die_errno ("Could not change back to '%s'", cwd); - return buf; + return retval; +} + +const char *real_path(const char *path) +{ + return real_path_internal(path, 1); +} + +const char *real_path_if_valid(const char *path) +{ + return real_path_internal(path, 0); } static const char *get_pwd_cwd(void) diff --git a/advice.c b/advice.c index edfbd4a6fb..d287927280 100644 --- a/advice.c +++ b/advice.c @@ -1,9 +1,10 @@ #include "cache.h" -int advice_push_nonfastforward = 1; +int advice_push_update_rejected = 1; int advice_push_non_ff_current = 1; int advice_push_non_ff_default = 1; int advice_push_non_ff_matching = 1; +int advice_push_already_exists = 1; int advice_status_hints = 1; int advice_commit_before_merge = 1; int advice_resolve_conflict = 1; @@ -14,15 +15,19 @@ static struct { const char *name; int *preference; } advice_config[] = { - { "pushnonfastforward", &advice_push_nonfastforward }, + { "pushupdaterejected", &advice_push_update_rejected }, { "pushnonffcurrent", &advice_push_non_ff_current }, { "pushnonffdefault", &advice_push_non_ff_default }, { "pushnonffmatching", &advice_push_non_ff_matching }, + { "pushalreadyexists", &advice_push_already_exists }, { "statushints", &advice_status_hints }, { "commitbeforemerge", &advice_commit_before_merge }, { "resolveconflict", &advice_resolve_conflict }, { "implicitidentity", &advice_implicit_identity }, { "detachedhead", &advice_detached_head }, + + /* make this an alias for backward compatibility */ + { "pushnonfastforward", &advice_push_update_rejected } }; void advise(const char *advice, ...) diff --git a/advice.h b/advice.h index f3cdbbf29e..8bf63563a5 100644 --- a/advice.h +++ b/advice.h @@ -3,10 +3,11 @@ #include "git-compat-util.h" -extern int advice_push_nonfastforward; +extern int advice_push_update_rejected; extern int advice_push_non_ff_current; extern int advice_push_non_ff_default; extern int advice_push_non_ff_matching; +extern int advice_push_already_exists; extern int advice_status_hints; extern int advice_commit_before_merge; extern int advice_resolve_conflict; diff --git a/archive.c b/archive.c index 466640479e..93e00bb4ae 100644 --- a/archive.c +++ b/archive.c @@ -120,6 +120,8 @@ static int write_archive_entry(const unsigned char *sha1, const char *base, strbuf_add(&path, args->base, args->baselen); strbuf_add(&path, base, baselen); strbuf_addstr(&path, filename); + if (S_ISDIR(mode) || S_ISGITLINK(mode)) + strbuf_addch(&path, '/'); path_without_prefix = path.buf + args->baselen; setup_archive_check(check); @@ -130,7 +132,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base, } if (S_ISDIR(mode) || S_ISGITLINK(mode)) { - strbuf_addch(&path, '/'); if (args->verbose) fprintf(stderr, "%.*s\n", (int)path.len, path.buf); err = write_entry(args, sha1, path.buf, path.len, mode); diff --git a/attr.c b/attr.c index 097ae87f3c..466c93fa50 100644 --- a/attr.c +++ b/attr.c @@ -564,17 +564,24 @@ static void bootstrap_attr_stack(void) attr_stack = elem; } +static const char *find_basename(const char *path) +{ + const char *cp, *last_slash = NULL; + + for (cp = path; *cp; cp++) { + if (*cp == '/' && cp[1]) + last_slash = cp; + } + return last_slash ? last_slash + 1 : path; +} + static void prepare_attr_stack(const char *path) { struct attr_stack *elem, *info; int dirlen, len; const char *cp; - cp = strrchr(path, '/'); - if (!cp) - dirlen = 0; - else - dirlen = cp - path; + dirlen = find_basename(path) - path; /* * At the bottom of the attribute stack is the built-in @@ -668,6 +675,10 @@ static int path_matches(const char *pathname, int pathlen, const char *pattern = pat->pattern; int prefix = pat->nowildcardlen; + if ((pat->flags & EXC_FLAG_MUSTBEDIR) && + ((!pathlen) || (pathname[pathlen-1] != '/'))) + return 0; + if (pat->flags & EXC_FLAG_NODIR) { return match_basename(basename, pathlen - (basename - pathname), @@ -758,9 +769,7 @@ static void collect_all_attrs(const char *path) for (i = 0; i < attr_nr; i++) check_all_attr[i].value = ATTR__UNKNOWN; - basename = strrchr(path, '/'); - basename = basename ? basename + 1 : path; - + basename = find_basename(path); pathlen = strlen(path); rem = attr_nr; for (stk = attr_stack; 0 < rem && stk; stk = stk->prev) diff --git a/builtin/apply.c b/builtin/apply.c index 156b3ce3b7..6c11e8bc73 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -2095,7 +2095,7 @@ static void update_pre_post_images(struct image *preimage, char *buf, size_t len, size_t postlen) { - int i, ctx; + int i, ctx, reduced; char *new, *old, *fixed; struct image fixed_preimage; @@ -2105,8 +2105,10 @@ static void update_pre_post_images(struct image *preimage, * free "oldlines". */ prepare_image(&fixed_preimage, buf, len, 1); - assert(fixed_preimage.nr == preimage->nr); - for (i = 0; i < preimage->nr; i++) + assert(postlen + ? fixed_preimage.nr == preimage->nr + : fixed_preimage.nr <= preimage->nr); + for (i = 0; i < fixed_preimage.nr; i++) fixed_preimage.line[i].flag = preimage->line[i].flag; free(preimage->line_allocated); *preimage = fixed_preimage; @@ -2126,7 +2128,8 @@ static void update_pre_post_images(struct image *preimage, else new = old; fixed = preimage->buf; - for (i = ctx = 0; i < postimage->nr; i++) { + + for (i = reduced = ctx = 0; i < postimage->nr; i++) { size_t len = postimage->line[i].len; if (!(postimage->line[i].flag & LINE_COMMON)) { /* an added line -- no counterparts in preimage */ @@ -2145,8 +2148,15 @@ static void update_pre_post_images(struct image *preimage, fixed += preimage->line[ctx].len; ctx++; } - if (preimage->nr <= ctx) - die(_("oops")); + + /* + * preimage is expected to run out, if the caller + * fixed addition of trailing blank lines. + */ + if (preimage->nr <= ctx) { + reduced++; + continue; + } /* and copy it in, while fixing the line length */ len = preimage->line[ctx].len; @@ -2159,6 +2169,7 @@ static void update_pre_post_images(struct image *preimage, /* Fix the length of the whole thing */ postimage->len = new - postimage->buf; + postimage->nr -= reduced; } static int match_fragment(struct image *img, diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 12220ad8da..77dffd1ce3 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -474,18 +474,21 @@ static void handle_tag(const char *name, struct tag *tag) (int)message_size, (int)message_size, message ? message : ""); } -static void get_tags_and_duplicates(struct object_array *pending, +static void get_tags_and_duplicates(struct rev_cmdline_info *info, struct string_list *extra_refs) { struct tag *tag; int i; - for (i = 0; i < pending->nr; i++) { - struct object_array_entry *e = pending->objects + i; + for (i = 0; i < info->nr; i++) { + struct rev_cmdline_entry *e = info->rev + i; unsigned char sha1[20]; - struct commit *commit = commit; + struct commit *commit; char *full_name; + if (e->flags & UNINTERESTING) + continue; + if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1) continue; @@ -523,10 +526,14 @@ static void get_tags_and_duplicates(struct object_array *pending, typename(e->item->type)); continue; } - if (commit->util) - /* more than one name for the same object */ + + /* + * This ref will not be updated through a commit, lets make + * sure it gets properly updated eventually. + */ + if (commit->util || commit->object.flags & SHOWN) string_list_append(extra_refs, full_name)->util = commit; - else + if (!commit->util) commit->util = full_name; } } @@ -614,6 +621,10 @@ static void import_marks(char *input_file) if (object->flags & SHOWN) error("Object %s already has a mark", sha1_to_hex(sha1)); + if (object->type != OBJ_COMMIT) + /* only commits */ + continue; + mark_object(object, mark); if (last_idnum < mark) last_idnum = mark; @@ -677,7 +688,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (import_filename && revs.prune_data.nr) full_tree = 1; - get_tags_and_duplicates(&revs.pending, &extra_refs); + get_tags_and_duplicates(&revs.cmdline, &extra_refs); if (prepare_revision_walk(&revs)) die("revision walk setup failed"); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index b5434af0c8..4a9ee690c7 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -337,7 +337,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) matchbuf[0] = prefix; matchbuf[1] = NULL; init_pathspec(&pathspec, matchbuf); - pathspec.items[0].use_wildcard = 0; + pathspec.items[0].nowildcard_len = pathspec.items[0].len; } else init_pathspec(&pathspec, NULL); if (read_tree(tree, 1, &pathspec)) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 235c17cc01..fb76e38d84 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -168,7 +168,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) init_pathspec(&pathspec, get_pathspec(prefix, argv + 1)); for (i = 0; i < pathspec.nr; i++) - pathspec.items[i].use_wildcard = 0; + pathspec.items[i].nowildcard_len = pathspec.items[i].len; pathspec.has_wildcard = 0; tree = parse_tree_indirect(sha1); if (!tree) diff --git a/builtin/push.c b/builtin/push.c index db9ba30b08..8491e431e4 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -220,31 +220,42 @@ static const char message_advice_checkout_pull_push[] = "(e.g. 'git pull') before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); +static const char message_advice_ref_already_exists[] = + N_("Updates were rejected because the destination reference already exists\n" + "in the remote."); + static void advise_pull_before_push(void) { - if (!advice_push_non_ff_current || !advice_push_nonfastforward) + if (!advice_push_non_ff_current || !advice_push_update_rejected) return; advise(_(message_advice_pull_before_push)); } static void advise_use_upstream(void) { - if (!advice_push_non_ff_default || !advice_push_nonfastforward) + if (!advice_push_non_ff_default || !advice_push_update_rejected) return; advise(_(message_advice_use_upstream)); } static void advise_checkout_pull_push(void) { - if (!advice_push_non_ff_matching || !advice_push_nonfastforward) + if (!advice_push_non_ff_matching || !advice_push_update_rejected) return; advise(_(message_advice_checkout_pull_push)); } +static void advise_ref_already_exists(void) +{ + if (!advice_push_already_exists || !advice_push_update_rejected) + return; + advise(_(message_advice_ref_already_exists)); +} + static int push_with_options(struct transport *transport, int flags) { int err; - int nonfastforward; + unsigned int reject_reasons; transport_set_verbosity(transport, verbosity, progress); @@ -257,7 +268,7 @@ static int push_with_options(struct transport *transport, int flags) if (verbosity > 0) fprintf(stderr, _("Pushing to %s\n"), transport->url); err = transport_push(transport, refspec_nr, refspec, flags, - &nonfastforward); + &reject_reasons); if (err != 0) error(_("failed to push some refs to '%s'"), transport->url); @@ -265,18 +276,15 @@ static int push_with_options(struct transport *transport, int flags) if (!err) return 0; - switch (nonfastforward) { - default: - break; - case NON_FF_HEAD: + if (reject_reasons & REJECT_NON_FF_HEAD) { advise_pull_before_push(); - break; - case NON_FF_OTHER: + } else if (reject_reasons & REJECT_NON_FF_OTHER) { if (default_matching_used) advise_use_upstream(); else advise_checkout_pull_push(); - break; + } else if (reject_reasons & REJECT_ALREADY_EXISTS) { + advise_ref_already_exists(); } return 1; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index d34201372d..f849e0a4a0 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -44,6 +44,11 @@ static void print_helper_status(struct ref *ref) msg = "non-fast forward"; break; + case REF_STATUS_REJECT_ALREADY_EXISTS: + res = "error"; + msg = "already exists"; + break; + case REF_STATUS_REJECT_NODELETE: case REF_STATUS_REMOTE_REJECT: res = "error"; @@ -85,7 +90,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) int send_all = 0; const char *receivepack = "git-receive-pack"; int flags; - int nonfastforward = 0; + unsigned int reject_reasons; int progress = -1; argv++; @@ -223,7 +228,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) ret |= finish_connect(conn); if (!helper_status) - transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward); + transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_reasons); if (!args.dry_run && remote) { struct ref *ref; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index b316cf37ca..83605143ac 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -306,9 +306,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s, const struct shortlog *log) { - int col = strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap); - if (col != log->wrap) - strbuf_addch(sb, '\n'); + strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap); + strbuf_addch(sb, '\n'); } void shortlog_output(struct shortlog *log) diff --git a/cache-tree.c b/cache-tree.c index 28ed6574a2..37e4d008b5 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -166,12 +166,8 @@ static int verify_cache(struct cache_entry **cache, fprintf(stderr, "...\n"); break; } - if (ce_stage(ce)) - fprintf(stderr, "%s: unmerged (%s)\n", - ce->name, sha1_to_hex(ce->sha1)); - else - fprintf(stderr, "%s: not added yet\n", - ce->name); + fprintf(stderr, "%s: unmerged (%s)\n", + ce->name, sha1_to_hex(ce->sha1)); } } if (funny) @@ -242,13 +238,17 @@ static int update_one(struct cache_tree *it, int entries, const char *base, int baselen, + int *skip_count, int flags) { struct strbuf buffer; int missing_ok = flags & WRITE_TREE_MISSING_OK; int dryrun = flags & WRITE_TREE_DRY_RUN; + int to_invalidate = 0; int i; + *skip_count = 0; + if (0 <= it->entry_count && has_sha1_file(it->sha1)) return it->entry_count; @@ -263,11 +263,12 @@ static int update_one(struct cache_tree *it, /* * Find the subtrees and update them. */ - for (i = 0; i < entries; i++) { + i = 0; + while (i < entries) { struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; - int pathlen, sublen, subcnt; + int pathlen, sublen, subcnt, subskip; path = ce->name; pathlen = ce_namelen(ce); @@ -275,8 +276,10 @@ static int update_one(struct cache_tree *it, break; /* at the end of this level */ slash = strchr(path + baselen, '/'); - if (!slash) + if (!slash) { + i++; continue; + } /* * a/bbb/c (base = a/, slash = /c) * ==> @@ -290,10 +293,13 @@ static int update_one(struct cache_tree *it, cache + i, entries - i, path, baselen + sublen + 1, + &subskip, flags); if (subcnt < 0) return subcnt; - i += subcnt - 1; + i += subcnt; + sub->count = subcnt; /* to be used in the next loop */ + *skip_count += subskip; sub->used = 1; } @@ -304,7 +310,8 @@ static int update_one(struct cache_tree *it, */ strbuf_init(&buffer, 8192); - for (i = 0; i < entries; i++) { + i = 0; + while (i < entries) { struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; @@ -324,14 +331,17 @@ static int update_one(struct cache_tree *it, if (!sub) die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); - i += sub->cache_tree->entry_count - 1; + i += sub->count; sha1 = sub->cache_tree->sha1; mode = S_IFDIR; + if (sub->cache_tree->entry_count < 0) + to_invalidate = 1; } else { sha1 = ce->sha1; mode = ce->ce_mode; entlen = pathlen - baselen; + i++; } if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) { strbuf_release(&buffer); @@ -339,8 +349,25 @@ static int update_one(struct cache_tree *it, mode, sha1_to_hex(sha1), entlen+baselen, path); } - if (ce->ce_flags & (CE_REMOVE | CE_INTENT_TO_ADD)) - continue; /* entry being removed or placeholder */ + /* + * CE_REMOVE entries are removed before the index is + * written to disk. Skip them to remain consistent + * with the future on-disk index. + */ + if (ce->ce_flags & CE_REMOVE) { + *skip_count = *skip_count + 1; + continue; + } + + /* + * CE_INTENT_TO_ADD entries exist on on-disk index but + * they are not part of generated trees. Invalidate up + * to root to force cache-tree users to read elsewhere. + */ + if (ce->ce_flags & CE_INTENT_TO_ADD) { + to_invalidate = 1; + continue; + } strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); @@ -360,7 +387,7 @@ static int update_one(struct cache_tree *it, } strbuf_release(&buffer); - it->entry_count = i; + it->entry_count = to_invalidate ? -1 : i - *skip_count; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, @@ -374,11 +401,11 @@ int cache_tree_update(struct cache_tree *it, int entries, int flags) { - int i; + int i, skip; i = verify_cache(cache, entries, flags); if (i) return i; - i = update_one(it, cache, entries, "", 0, flags); + i = update_one(it, cache, entries, "", 0, &skip, flags); if (i < 0) return i; return 0; diff --git a/cache-tree.h b/cache-tree.h index d8cb2e9e39..55d0f59f2b 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -7,6 +7,7 @@ struct cache_tree; struct cache_tree_sub { struct cache_tree *cache_tree; + int count; /* internally used by update_one() */ int namelen; int used; char name[FLEX_ARRAY]; diff --git a/cache.h b/cache.h index 95608d9dc5..c257953fa7 100644 --- a/cache.h +++ b/cache.h @@ -474,6 +474,8 @@ extern int index_name_is_other(const struct index_state *, const char *, int); extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); +#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */ + struct pathspec { const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ int nr; @@ -483,7 +485,8 @@ struct pathspec { struct pathspec_item { const char *match; int len; - unsigned int use_wildcard:1; + int nowildcard_len; + int flags; } *items; }; @@ -717,10 +720,11 @@ static inline int is_absolute_path(const char *path) } int is_directory(const char *); const char *real_path(const char *path); +const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); const char *relative_path(const char *abs, const char *base); int normalize_path_copy(char *dst, const char *src); -int longest_ancestor_length(const char *path, const char *prefix_list); +int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); int offset_1st_component(const char *path); @@ -1002,14 +1006,19 @@ struct ref { unsigned char old_sha1[20]; unsigned char new_sha1[20]; char *symref; - unsigned int force:1, + unsigned int + force:1, + requires_force:1, merge:1, nonfastforward:1, + not_forwardable:1, + update:1, deletion:1; enum { REF_STATUS_NONE = 0, REF_STATUS_OK, REF_STATUS_REJECT_NONFASTFORWARD, + REF_STATUS_REJECT_ALREADY_EXISTS, REF_STATUS_REJECT_NODELETE, REF_STATUS_UPTODATE, REF_STATUS_REMOTE_REJECT, @@ -1139,6 +1148,9 @@ extern int check_repository_format_version(const char *var, const char *value, v extern int git_env_bool(const char *, int); extern int git_config_system(void); extern int config_error_nonbool(const char *); +#ifdef __GNUC__ +#define config_error_nonbool(s) (config_error_nonbool(s), -1) +#endif extern const char *get_log_output_encoding(void); extern const char *get_commit_output_encoding(void); @@ -1158,6 +1170,7 @@ extern int author_ident_sufficiently_given(void); extern const char *git_commit_encoding; extern const char *git_log_output_encoding; extern const char *git_mailmap_file; +extern const char *git_mailmap_blob; /* IO helper functions */ extern void maybe_flush_or_die(FILE *, const char *); diff --git a/commit.h b/commit.h index b6ad8f3f30..0f469e507d 100644 --- a/commit.h +++ b/commit.h @@ -89,6 +89,7 @@ struct pretty_print_context { char *notes_message; struct reflog_walk_info *reflog_info; const char *output_encoding; + int color; }; struct userformat_want { diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c index 0ff1d273a5..b8b7dc2543 100644 --- a/compat/fnmatch/fnmatch.c +++ b/compat/fnmatch/fnmatch.c @@ -135,9 +135,9 @@ extern int errno; # if !defined HAVE___STRCHRNUL && !defined _LIBC static char * -__strchrnul (s, c) - const char *s; - int c; +__strchrnul (const char *s, int c) + + { char *result = strchr (s, c); if (result == NULL) @@ -159,11 +159,11 @@ static int internal_fnmatch __P ((const char *pattern, const char *string, internal_function; static int internal_function -internal_fnmatch (pattern, string, no_leading_period, flags) - const char *pattern; - const char *string; - int no_leading_period; - int flags; +internal_fnmatch (const char *pattern, const char *string, int no_leading_period, int flags) + + + + { register const char *p = pattern, *n = string; register unsigned char c; @@ -481,10 +481,10 @@ internal_fnmatch (pattern, string, no_leading_period, flags) int -fnmatch (pattern, string, flags) - const char *pattern; - const char *string; - int flags; +fnmatch (const char *pattern, const char *string, int flags) + + + { return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags); } diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c index d9a17a8057..91c4e7f27b 100644 --- a/compat/nedmalloc/nedmalloc.c +++ b/compat/nedmalloc/nedmalloc.c @@ -603,7 +603,10 @@ static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, si } /* We really want to make sure this goes into memory now but we have to be careful of breaking aliasing rules, so write it twice */ - *((volatile struct malloc_state **) &p->m[end])=p->m[end]=temp; + { + volatile struct malloc_state **_m=(volatile struct malloc_state **) &p->m[end]; + *_m=(p->m[end]=temp); + } ACQUIRE_LOCK(&p->m[end]->mutex); /*printf("Created mspace idx %d\n", end);*/ RELEASE_LOCK(&p->mutex); diff --git a/config.c b/config.c index fb3f8681ee..7b444b68ab 100644 --- a/config.c +++ b/config.c @@ -58,7 +58,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc path = buf.buf; } - if (!access_or_warn(path, R_OK)) { + if (!access_or_die(path, R_OK)) { if (++inc->depth > MAX_INCLUDE_DEPTH) die(include_depth_advice, MAX_INCLUDE_DEPTH, path, cf && cf->name ? cf->name : "the command line"); @@ -839,6 +839,8 @@ static int git_default_mailmap_config(const char *var, const char *value) { if (!strcmp(var, "mailmap.file")) return git_config_string(&git_mailmap_file, var, value); + if (!strcmp(var, "mailmap.blob")) + return git_config_string(&git_mailmap_blob, var, value); /* Add other config variables here and to Documentation/config.txt. */ return 0; @@ -938,23 +940,23 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) home_config_paths(&user_config, &xdg_config, "config"); - if (git_config_system() && !access_or_warn(git_etc_gitconfig(), R_OK)) { + if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), data); found += 1; } - if (xdg_config && !access_or_warn(xdg_config, R_OK)) { + if (xdg_config && !access_or_die(xdg_config, R_OK)) { ret += git_config_from_file(fn, xdg_config, data); found += 1; } - if (user_config && !access_or_warn(user_config, R_OK)) { + if (user_config && !access_or_die(user_config, R_OK)) { ret += git_config_from_file(fn, user_config, data); found += 1; } - if (repo_config && !access_or_warn(repo_config, R_OK)) { + if (repo_config && !access_or_die(repo_config, R_OK)) { ret += git_config_from_file(fn, repo_config, data); found += 1; } @@ -1660,6 +1662,7 @@ int git_config_rename_section(const char *old_name, const char *new_name) * Call this to report error for your variable that should not * get a boolean value (i.e. "[my] var" means "true"). */ +#undef config_error_nonbool int config_error_nonbool(const char *var) { return error("Missing value for '%s'", var); diff --git a/configure.ac b/configure.ac index ad215cc4a1..1991258ae0 100644 --- a/configure.ac +++ b/configure.ac @@ -753,6 +753,14 @@ AC_CHECK_MEMBER(struct dirent.d_type, [#include ]) GIT_CONF_SUBST([NO_D_TYPE_IN_DIRENT]) # +# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd +# in the C library. +AC_CHECK_MEMBER(struct passwd.pw_gecos, +[NO_GECOS_IN_PWENT=], +[NO_GECOS_IN_PWENT=YesPlease], +[#include ]) +GIT_CONF_SUBST([NO_GECOS_IN_PWENT]) +# # Define NO_SOCKADDR_STORAGE if your platform does not have struct # sockaddr_storage. AC_CHECK_TYPE(struct sockaddr_storage, @@ -872,6 +880,12 @@ AC_CHECK_HEADER([libcharset.h], [HAVE_LIBCHARSET_H=YesPlease], [HAVE_LIBCHARSET_H=]) GIT_CONF_SUBST([HAVE_LIBCHARSET_H]) +# +# Define HAVE_STRINGS_H if you have strings.h +AC_CHECK_HEADER([strings.h], +[HAVE_STRINGS_H=YesPlease], +[HAVE_STRINGS_H=]) +GIT_CONF_SUBST([HAVE_STRINGS_H]) # Define CHARSET_LIB if libiconv does not export the locale_charset symbol # and libcharset does CHARSET_LIB= @@ -1021,7 +1035,17 @@ if test -n "$USER_NOPTHREAD"; then # -D_REENTRANT' or some such. elif test -z "$PTHREAD_CFLAGS"; then threads_found=no - for opt in -mt -pthread -lpthread; do + # Attempt to compile and link some code using pthreads to determine + # required linker flags. The order is somewhat important here: We + # first try it without any extra flags, to catch systems where + # pthreads are part of the C library, then go on testing various other + # flags. We do so to avoid false positives. For example, on Mac OS X + # pthreads are part of the C library; moreover, the compiler allows us + # to add "-mt" to the CFLAGS (although it will do nothing except + # trigger a warning about an unused flag). Hence if we checked for + # "-mt" before "" we would end up picking it. But unfortunately this + # would then trigger compiler warnings on every single file we compile. + for opt in "" -mt -pthread -lpthread; do old_CFLAGS="$CFLAGS" CFLAGS="$opt $CFLAGS" AC_MSG_CHECKING([for POSIX Threads with '$opt']) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0b77eb1fa4..a4c48e179e 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -971,6 +971,13 @@ _git_commit () { __git_has_doubledash && return + case "$prev" in + -c|-C) + __gitcomp_nl "$(__git_refs)" "" "${cur}" + return + ;; + esac + case "$cur" in --cleanup=*) __gitcomp "default strip verbatim whitespace diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 9b074e148d..9bef0531c5 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -24,6 +24,8 @@ # will show username, at-sign, host, colon, cwd, then # various status string, followed by dollar and SP, as # your prompt. +# Optionally, you can supply a third argument with a printf +# format string to finetune the output of the branch status # # The argument to __git_ps1 will be displayed only if you are currently # in a git repository. The %s token will be the name of the current @@ -222,10 +224,12 @@ __git_ps1_show_upstream () # when called from PS1 using command substitution # in this mode it prints text to add to bash PS1 prompt (includes branch name) # -# __git_ps1 requires 2 arguments when called from PROMPT_COMMAND (pc) +# __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc) # in that case it _sets_ PS1. The arguments are parts of a PS1 string. -# when both arguments are given, the first is prepended and the second appended +# when two arguments are given, the first is prepended and the second appended # to the state string when assigned to PS1. +# The optional third parameter will be used as printf format string to further +# customize the output of the git-status string. # In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true __git_ps1 () { @@ -236,9 +240,10 @@ __git_ps1 () local printf_format=' (%s)' case "$#" in - 2) pcmode=yes + 2|3) pcmode=yes ps1pc_start="$1" ps1pc_end="$2" + printf_format="${3:-$printf_format}" ;; 0|1) printf_format="${1:-$printf_format}" ;; @@ -339,6 +344,7 @@ __git_ps1 () local f="$w$i$s$u" if [ $pcmode = yes ]; then + local gitstring= if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then local c_red='\e[31m' local c_green='\e[32m' @@ -356,29 +362,31 @@ __git_ps1 () branch_color="$bad_color" fi - # Setting PS1 directly with \[ and \] around colors + # Setting gitstring directly with \[ and \] around colors # is necessary to prevent wrapping issues! - PS1="$ps1pc_start (\[$branch_color\]$branchstring\[$c_clear\]" + gitstring="\[$branch_color\]$branchstring\[$c_clear\]" if [ -n "$w$i$s$u$r$p" ]; then - PS1="$PS1 " + gitstring="$gitstring " fi if [ "$w" = "*" ]; then - PS1="$PS1\[$bad_color\]$w" + gitstring="$gitstring\[$bad_color\]$w" fi if [ -n "$i" ]; then - PS1="$PS1\[$ok_color\]$i" + gitstring="$gitstring\[$ok_color\]$i" fi if [ -n "$s" ]; then - PS1="$PS1\[$flags_color\]$s" + gitstring="$gitstring\[$flags_color\]$s" fi if [ -n "$u" ]; then - PS1="$PS1\[$bad_color\]$u" + gitstring="$gitstring\[$bad_color\]$u" fi - PS1="$PS1\[$c_clear\]$r$p)$ps1pc_end" + gitstring="$gitstring\[$c_clear\]$r$p" else - PS1="$ps1pc_start ($c${b##refs/heads/}${f:+ $f}$r$p)$ps1pc_end" + gitstring="$c${b##refs/heads/}${f:+ $f}$r$p" fi + gitstring=$(printf -- "$printf_format" "$gitstring") + PS1="$ps1pc_start$gitstring$ps1pc_end" else # NO color option unless in PROMPT_COMMAND mode printf -- "$printf_format" "$c${b##refs/heads/}${f:+ $f}$r$p" diff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl index 4b852e2455..9513f5e35b 100755 --- a/contrib/stats/mailmap.pl +++ b/contrib/stats/mailmap.pl @@ -1,38 +1,70 @@ -#!/usr/bin/perl -w -my %mailmap = (); -open I, "<", ".mailmap"; -while () { - chomp; - next if /^#/; - if (my ($author, $mail) = /^(.*?)\s+<(.+)>$/) { - $mailmap{$mail} = $author; - } +#!/usr/bin/perl + +use warnings 'all'; +use strict; +use Getopt::Long; + +my $match_emails; +my $match_names; +my $order_by = 'count'; +Getopt::Long::Configure(qw(bundling)); +GetOptions( + 'emails|e!' => \$match_emails, + 'names|n!' => \$match_names, + 'count|c' => sub { $order_by = 'count' }, + 'time|t' => sub { $order_by = 'stamp' }, +) or exit 1; +$match_emails = 1 unless $match_names; + +my $email = {}; +my $name = {}; + +open(my $fh, '-|', "git log --format='%at <%aE> %aN'"); +while(<$fh>) { + my ($t, $e, $n) = /(\S+) <(\S+)> (.*)/; + mark($email, $e, $n, $t); + mark($name, $n, $e, $t); } -close I; - -my %mail2author = (); -open I, "git log --pretty='format:%ae %an' |"; -while () { - chomp; - my ($mail, $author) = split(/\t/, $_); - next if exists $mailmap{$mail}; - $mail2author{$mail} ||= {}; - $mail2author{$mail}{$author} ||= 0; - $mail2author{$mail}{$author}++; +close($fh); + +if ($match_emails) { + foreach my $e (dups($email)) { + foreach my $n (vals($email->{$e})) { + show($n, $e, $email->{$e}->{$n}); + } + print "\n"; + } } -close I; - -while (my ($mail, $authorcount) = each %mail2author) { - # %$authorcount is ($author => $count); - # sort and show the names from the most frequent ones. - my @names = (map { $_->[0] } - sort { $b->[1] <=> $a->[1] } - map { [$_, $authorcount->{$_}] } - keys %$authorcount); - if (1 < @names) { - for (@names) { - print "$_ <$mail>\n"; +if ($match_names) { + foreach my $n (dups($name)) { + foreach my $e (vals($name->{$n})) { + show($n, $e, $name->{$n}->{$e}); } + print "\n"; } } +exit 0; +sub mark { + my ($h, $k, $v, $t) = @_; + my $e = $h->{$k}->{$v} ||= { count => 0, stamp => 0 }; + $e->{count}++; + $e->{stamp} = $t unless $t < $e->{stamp}; +} + +sub dups { + my $h = shift; + return grep { keys($h->{$_}) > 1 } keys($h); +} + +sub vals { + my $h = shift; + return sort { + $h->{$b}->{$order_by} <=> $h->{$a}->{$order_by} + } keys($h); +} + +sub show { + my ($n, $e, $h) = @_; + print "$n <$e> ($h->{$order_by})\n"; +} diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore index 7e77c9d022..91360a3d7f 100644 --- a/contrib/subtree/.gitignore +++ b/contrib/subtree/.gitignore @@ -1,4 +1,5 @@ *~ +git-subtree git-subtree.xml git-subtree.1 mainline diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 0c44fda011..c5bce41ac7 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -93,7 +93,7 @@ pull:: repository. push:: - Does a 'split' (see above) using the supplied + Does a 'split' (see below) using the supplied and then does a 'git push' to push the result to the repository and refspec. This can be used to push your subtree to different branches of the remote repository. diff --git a/dir.c b/dir.c index 03ff36bc6e..095ea7ebab 100644 --- a/dir.c +++ b/dir.c @@ -34,6 +34,28 @@ int fnmatch_icase(const char *pattern, const char *string, int flags) return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0)); } +inline int git_fnmatch(const char *pattern, const char *string, + int flags, int prefix) +{ + int fnm_flags = 0; + if (flags & GFNM_PATHNAME) + fnm_flags |= FNM_PATHNAME; + if (prefix > 0) { + if (strncmp(pattern, string, prefix)) + return FNM_NOMATCH; + pattern += prefix; + string += prefix; + } + if (flags & GFNM_ONESTAR) { + int pattern_len = strlen(++pattern); + int string_len = strlen(string); + return string_len < pattern_len || + strcmp(pattern, + string + string_len - pattern_len); + } + return fnmatch(pattern, string, fnm_flags); +} + static size_t common_prefix_len(const char **pathspec) { const char *n, *first; @@ -234,7 +256,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, return MATCHED_RECURSIVELY; } - if (item->use_wildcard && !fnmatch(match, name, 0)) + if (item->nowildcard_len < item->len && + !git_fnmatch(match, name, + item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + item->nowildcard_len - prefix)) return MATCHED_FNMATCH; return 0; @@ -1433,10 +1458,18 @@ int init_pathspec(struct pathspec *pathspec, const char **paths) item->match = path; item->len = strlen(path); - item->use_wildcard = !limit_pathspec_to_literal() && - !no_wildcard(path); - if (item->use_wildcard) - pathspec->has_wildcard = 1; + item->flags = 0; + if (limit_pathspec_to_literal()) { + item->nowildcard_len = item->len; + } else { + item->nowildcard_len = simple_length(path); + if (item->nowildcard_len < item->len) { + pathspec->has_wildcard = 1; + if (path[item->nowildcard_len] == '*' && + no_wildcard(path + item->nowildcard_len + 1)) + item->flags |= PATHSPEC_ONESTAR; + } + } } qsort(pathspec->items, pathspec->nr, diff --git a/dir.h b/dir.h index f5c89e3b80..ab5af42b2e 100644 --- a/dir.h +++ b/dir.h @@ -139,4 +139,13 @@ extern int strcmp_icase(const char *a, const char *b); extern int strncmp_icase(const char *a, const char *b, size_t count); extern int fnmatch_icase(const char *pattern, const char *string, int flags); +/* + * The prefix part of pattern must not contains wildcards. + */ +#define GFNM_PATHNAME 1 /* similar to FNM_PATHNAME */ +#define GFNM_ONESTAR 2 /* there is only _one_ wildcard, a star */ + +extern int git_fnmatch(const char *pattern, const char *string, + int flags, int prefix); + #endif diff --git a/editor.c b/editor.c index d8340031d2..065a7abf2f 100644 --- a/editor.c +++ b/editor.c @@ -1,6 +1,7 @@ #include "cache.h" #include "strbuf.h" #include "run-command.h" +#include "sigchain.h" #ifndef DEFAULT_EDITOR #define DEFAULT_EDITOR "vi" @@ -37,8 +38,25 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en if (strcmp(editor, ":")) { const char *args[] = { editor, path, NULL }; + struct child_process p; + int ret, sig; - if (run_command_v_opt_cd_env(args, RUN_USING_SHELL, NULL, env)) + memset(&p, 0, sizeof(p)); + p.argv = args; + p.env = env; + p.use_shell = 1; + if (start_command(&p) < 0) + return error("unable to start editor '%s'", editor); + + sigchain_push(SIGINT, SIG_IGN); + sigchain_push(SIGQUIT, SIG_IGN); + ret = finish_command(&p); + sig = ret + 128; + sigchain_pop(SIGINT); + sigchain_pop(SIGQUIT); + if (sig == SIGINT || sig == SIGQUIT) + raise(sig); + if (ret) return error("There was a problem with the editor '%s'.", editor); } diff --git a/fetch-pack.c b/fetch-pack.c index 099ff4ddff..f0acdf7331 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -874,8 +874,6 @@ static int fetch_pack_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static struct lock_file lock; - static void fetch_pack_setup(void) { static int did_setup; @@ -917,6 +915,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args, ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile); if (args->depth > 0) { + static struct lock_file lock; struct cache_time mtime; struct strbuf sb = STRBUF_INIT; char *shallow = git_path("shallow"); diff --git a/fsck.c b/fsck.c index 7395ef6a42..99c0497674 100644 --- a/fsck.c +++ b/fsck.c @@ -142,6 +142,9 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) int has_null_sha1 = 0; int has_full_path = 0; int has_empty_name = 0; + int has_dot = 0; + int has_dotdot = 0; + int has_dotgit = 0; int has_zero_pad = 0; int has_bad_modes = 0; int has_dup_entries = 0; @@ -168,6 +171,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) has_full_path = 1; if (!*name) has_empty_name = 1; + if (!strcmp(name, ".")) + has_dot = 1; + if (!strcmp(name, "..")) + has_dotdot = 1; + if (!strcmp(name, ".git")) + has_dotgit = 1; has_zero_pad |= *(char *)desc.buffer == '0'; update_tree_entry(&desc); @@ -217,6 +226,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) retval += error_func(&item->object, FSCK_WARN, "contains full pathnames"); if (has_empty_name) retval += error_func(&item->object, FSCK_WARN, "contains empty pathname"); + if (has_dot) + retval += error_func(&item->object, FSCK_WARN, "contains '.'"); + if (has_dotdot) + retval += error_func(&item->object, FSCK_WARN, "contains '..'"); + if (has_dotgit) + retval += error_func(&item->object, FSCK_WARN, "contains '.git'"); if (has_zero_pad) retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes"); if (has_bad_modes) diff --git a/git-compat-util.h b/git-compat-util.h index 2e79b8a2f3..2cecf56eb3 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -75,7 +75,7 @@ # endif #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \ !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \ - !defined(__TANDEM) + !defined(__TANDEM) && !defined(__QNX__) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #endif @@ -99,12 +99,14 @@ #include #include #include -#ifdef __TANDEM /* or HAVE_STRINGS_H or !NO_STRINGS_H? */ +#ifdef HAVE_STRINGS_H #include /* for strcasecmp() */ #endif #include #include +#ifdef NEEDS_SYS_PARAM_H #include +#endif #include #include #include @@ -288,6 +290,17 @@ extern NORETURN void die_errno(const char *err, ...) __attribute__((format (prin extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); +/* + * Let callers be aware of the constant return value; this can help + * gcc with -Wuninitialized analysis. We have to restrict this trick to + * gcc, though, because of the variadic macro and the magic ## comma pasting + * behavior. But since we're only trying to help gcc, anyway, it's OK; other + * compilers will fall back to using the function as usual. + */ +#ifdef __GNUC__ +#define error(fmt, ...) (error((fmt), ##__VA_ARGS__), -1) +#endif + extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params)); extern void set_error_routine(void (*routine)(const char *err, va_list params)); @@ -411,6 +424,10 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif +#ifdef NO_GETPAGESIZE +#define getpagesize() sysconf(_SC_PAGESIZE) +#endif + #ifdef FREAD_READS_DIRECTORIES #ifdef fopen #undef fopen @@ -637,8 +654,12 @@ int rmdir_or_warn(const char *path); */ int remove_or_warn(unsigned int mode, const char *path); -/* Call access(2), but warn for any error besides ENOENT. */ +/* + * Call access(2), but warn for any error except "missing file" + * (ENOENT or ENOTDIR). + */ int access_or_warn(const char *path, int mode); +int access_or_die(const char *path, int mode); /* Warn on an inaccessible file that ought to be accessible */ void warn_on_inaccessible(const char *path); diff --git a/git-remote-testgit b/git-remote-testgit new file mode 100755 index 0000000000..b395c8de59 --- /dev/null +++ b/git-remote-testgit @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# Copyright (c) 2012 Felipe Contreras + +alias=$1 +url=$2 + +dir="$GIT_DIR/testgit/$alias" +prefix="refs/testgit/$alias" + +default_refspec="refs/heads/*:${prefix}/heads/*" + +refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}" + +test -z "$refspec" && prefix="refs" + +export GIT_DIR="$url/.git" + +mkdir -p "$dir" + +if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS" +then + gitmarks="$dir/git.marks" + testgitmarks="$dir/testgit.marks" + test -e "$gitmarks" || >"$gitmarks" + test -e "$testgitmarks" || >"$testgitmarks" + testgitmarks_args=( "--"{import,export}"-marks=$testgitmarks" ) +fi + +while read line +do + case $line in + capabilities) + echo 'import' + echo 'export' + test -n "$refspec" && echo "refspec $refspec" + if test -n "$gitmarks" + then + echo "*import-marks $gitmarks" + echo "*export-marks $gitmarks" + fi + echo + ;; + list) + git for-each-ref --format='? %(refname)' 'refs/heads/' + head=$(git symbolic-ref HEAD) + echo "@$head HEAD" + echo + ;; + import*) + # read all import lines + while true + do + ref="${line#* }" + refs="$refs $ref" + read line + test "${line%% *}" != "import" && break + done + + if test -n "$gitmarks" + then + echo "feature import-marks=$gitmarks" + echo "feature export-marks=$gitmarks" + fi + echo "feature done" + git fast-export "${testgitmarks_args[@]}" $refs | + sed -e "s#refs/heads/#${prefix}/heads/#g" + echo "done" + ;; + export) + before=$(git for-each-ref --format='%(refname) %(objectname)') + + git fast-import "${testgitmarks_args[@]}" --quiet + + after=$(git for-each-ref --format='%(refname) %(objectname)') + + # figure out which refs were updated + join -e 0 -o '0 1.2 2.2' -a 2 <(echo "$before") <(echo "$after") | + while read ref a b + do + test $a == $b && continue + echo "ok $ref" + done + + echo + ;; + '') + exit + ;; + esac +done diff --git a/git-remote-testgit.py b/git-remote-testgit.py deleted file mode 100644 index 5f3ebd244d..0000000000 --- a/git-remote-testgit.py +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env python - -# This command is a simple remote-helper, that is used both as a -# testcase for the remote-helper functionality, and as an example to -# show remote-helper authors one possible implementation. -# -# This is a Git <-> Git importer/exporter, that simply uses git -# fast-import and git fast-export to consume and produce fast-import -# streams. -# -# To understand better the way things work, one can activate debug -# traces by setting (to any value) the environment variables -# GIT_TRANSPORT_HELPER_DEBUG and GIT_DEBUG_TESTGIT, to see messages -# from the transport-helper side, or from this example remote-helper. - -# hashlib is only available in python >= 2.5 -try: - import hashlib - _digest = hashlib.sha1 -except ImportError: - import sha - _digest = sha.new -import sys -import os -import time -sys.path.insert(0, os.getenv("GITPYTHONLIB",".")) - -from git_remote_helpers.util import die, debug, warn -from git_remote_helpers.git.repo import GitRepo -from git_remote_helpers.git.exporter import GitExporter -from git_remote_helpers.git.importer import GitImporter -from git_remote_helpers.git.non_local import NonLocalGit - -def get_repo(alias, url): - """Returns a git repository object initialized for usage. - """ - - repo = GitRepo(url) - repo.get_revs() - repo.get_head() - - hasher = _digest() - hasher.update(repo.path) - repo.hash = hasher.hexdigest() - - repo.get_base_path = lambda base: os.path.join( - base, 'info', 'fast-import', repo.hash) - - prefix = 'refs/testgit/%s/' % alias - debug("prefix: '%s'", prefix) - - repo.gitdir = os.environ["GIT_DIR"] - repo.alias = alias - repo.prefix = prefix - - repo.exporter = GitExporter(repo) - repo.importer = GitImporter(repo) - repo.non_local = NonLocalGit(repo) - - return repo - - -def local_repo(repo, path): - """Returns a git repository object initalized for usage. - """ - - local = GitRepo(path) - - local.non_local = None - local.gitdir = repo.gitdir - local.alias = repo.alias - local.prefix = repo.prefix - local.hash = repo.hash - local.get_base_path = repo.get_base_path - local.exporter = GitExporter(local) - local.importer = GitImporter(local) - - return local - - -def do_capabilities(repo, args): - """Prints the supported capabilities. - """ - - print "import" - print "export" - print "refspec refs/heads/*:%s*" % repo.prefix - - dirname = repo.get_base_path(repo.gitdir) - - if not os.path.exists(dirname): - os.makedirs(dirname) - - path = os.path.join(dirname, 'testgit.marks') - - print "*export-marks %s" % path - if os.path.exists(path): - print "*import-marks %s" % path - - print # end capabilities - - -def do_list(repo, args): - """Lists all known references. - - Bug: This will always set the remote head to master for non-local - repositories, since we have no way of determining what the remote - head is at clone time. - """ - - for ref in repo.revs: - debug("? refs/heads/%s", ref) - print "? refs/heads/%s" % ref - - if repo.head: - debug("@refs/heads/%s HEAD" % repo.head) - print "@refs/heads/%s HEAD" % repo.head - else: - debug("@refs/heads/master HEAD") - print "@refs/heads/master HEAD" - - print # end list - - -def update_local_repo(repo): - """Updates (or clones) a local repo. - """ - - if repo.local: - return repo - - path = repo.non_local.clone(repo.gitdir) - repo.non_local.update(repo.gitdir) - repo = local_repo(repo, path) - return repo - - -def do_import(repo, args): - """Exports a fast-import stream from testgit for git to import. - """ - - if len(args) != 1: - die("Import needs exactly one ref") - - if not repo.gitdir: - die("Need gitdir to import") - - ref = args[0] - refs = [ref] - - while True: - line = sys.stdin.readline() - if line == '\n': - break - if not line.startswith('import '): - die("Expected import line.") - - # strip of leading 'import ' - ref = line[7:].strip() - refs.append(ref) - - repo = update_local_repo(repo) - repo.exporter.export_repo(repo.gitdir, refs) - - print "done" - - -def do_export(repo, args): - """Imports a fast-import stream from git to testgit. - """ - - if not repo.gitdir: - die("Need gitdir to export") - - update_local_repo(repo) - changed = repo.importer.do_import(repo.gitdir) - - if not repo.local: - repo.non_local.push(repo.gitdir) - - for ref in changed: - print "ok %s" % ref - print - - -COMMANDS = { - 'capabilities': do_capabilities, - 'list': do_list, - 'import': do_import, - 'export': do_export, -} - - -def sanitize(value): - """Cleans up the url. - """ - - if value.startswith('testgit::'): - value = value[9:] - - return value - - -def read_one_line(repo): - """Reads and processes one command. - """ - - sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY") - if sleepy: - debug("Sleeping %d sec before readline" % int(sleepy)) - time.sleep(int(sleepy)) - - line = sys.stdin.readline() - - cmdline = line - - if not cmdline: - warn("Unexpected EOF") - return False - - cmdline = cmdline.strip().split() - if not cmdline: - # Blank line means we're about to quit - return False - - cmd = cmdline.pop(0) - debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline)) - - if cmd not in COMMANDS: - die("Unknown command, %s", cmd) - - func = COMMANDS[cmd] - func(repo, cmdline) - sys.stdout.flush() - - return True - - -def main(args): - """Starts a new remote helper for the specified repository. - """ - - if len(args) != 3: - die("Expecting exactly three arguments.") - sys.exit(1) - - if os.getenv("GIT_DEBUG_TESTGIT"): - import git_remote_helpers.util - git_remote_helpers.util.DEBUG = True - - alias = sanitize(args[1]) - url = sanitize(args[2]) - - if not alias.isalnum(): - warn("non-alnum alias '%s'", alias) - alias = "tmp" - - args[1] = alias - args[2] = url - - repo = get_repo(alias, url) - - debug("Got arguments %s", args[1:]) - - more = True - - sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0) - while (more): - more = read_one_line(repo) - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/git-remote-testpy.py b/git-remote-testpy.py new file mode 100644 index 0000000000..ade797bbb0 --- /dev/null +++ b/git-remote-testpy.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python + +# This command is a simple remote-helper, that is used both as a +# testcase for the remote-helper functionality, and as an example to +# show remote-helper authors one possible implementation. +# +# This is a Git <-> Git importer/exporter, that simply uses git +# fast-import and git fast-export to consume and produce fast-import +# streams. +# +# To understand better the way things work, one can activate debug +# traces by setting (to any value) the environment variables +# GIT_TRANSPORT_HELPER_DEBUG and GIT_DEBUG_TESTGIT, to see messages +# from the transport-helper side, or from this example remote-helper. + +# hashlib is only available in python >= 2.5 +try: + import hashlib + _digest = hashlib.sha1 +except ImportError: + import sha + _digest = sha.new +import sys +import os +import time +sys.path.insert(0, os.getenv("GITPYTHONLIB",".")) + +from git_remote_helpers.util import die, debug, warn +from git_remote_helpers.git.repo import GitRepo +from git_remote_helpers.git.exporter import GitExporter +from git_remote_helpers.git.importer import GitImporter +from git_remote_helpers.git.non_local import NonLocalGit + +def get_repo(alias, url): + """Returns a git repository object initialized for usage. + """ + + repo = GitRepo(url) + repo.get_revs() + repo.get_head() + + hasher = _digest() + hasher.update(repo.path) + repo.hash = hasher.hexdigest() + + repo.get_base_path = lambda base: os.path.join( + base, 'info', 'fast-import', repo.hash) + + prefix = 'refs/testgit/%s/' % alias + debug("prefix: '%s'", prefix) + + repo.gitdir = os.environ["GIT_DIR"] + repo.alias = alias + repo.prefix = prefix + + repo.exporter = GitExporter(repo) + repo.importer = GitImporter(repo) + repo.non_local = NonLocalGit(repo) + + return repo + + +def local_repo(repo, path): + """Returns a git repository object initalized for usage. + """ + + local = GitRepo(path) + + local.non_local = None + local.gitdir = repo.gitdir + local.alias = repo.alias + local.prefix = repo.prefix + local.hash = repo.hash + local.get_base_path = repo.get_base_path + local.exporter = GitExporter(local) + local.importer = GitImporter(local) + + return local + + +def do_capabilities(repo, args): + """Prints the supported capabilities. + """ + + print "import" + print "export" + print "refspec refs/heads/*:%s*" % repo.prefix + + dirname = repo.get_base_path(repo.gitdir) + + if not os.path.exists(dirname): + os.makedirs(dirname) + + path = os.path.join(dirname, 'git.marks') + + print "*export-marks %s" % path + if os.path.exists(path): + print "*import-marks %s" % path + + print # end capabilities + + +def do_list(repo, args): + """Lists all known references. + + Bug: This will always set the remote head to master for non-local + repositories, since we have no way of determining what the remote + head is at clone time. + """ + + for ref in repo.revs: + debug("? refs/heads/%s", ref) + print "? refs/heads/%s" % ref + + if repo.head: + debug("@refs/heads/%s HEAD" % repo.head) + print "@refs/heads/%s HEAD" % repo.head + else: + debug("@refs/heads/master HEAD") + print "@refs/heads/master HEAD" + + print # end list + + +def update_local_repo(repo): + """Updates (or clones) a local repo. + """ + + if repo.local: + return repo + + path = repo.non_local.clone(repo.gitdir) + repo.non_local.update(repo.gitdir) + repo = local_repo(repo, path) + return repo + + +def do_import(repo, args): + """Exports a fast-import stream from testgit for git to import. + """ + + if len(args) != 1: + die("Import needs exactly one ref") + + if not repo.gitdir: + die("Need gitdir to import") + + ref = args[0] + refs = [ref] + + while True: + line = sys.stdin.readline() + if line == '\n': + break + if not line.startswith('import '): + die("Expected import line.") + + # strip of leading 'import ' + ref = line[7:].strip() + refs.append(ref) + + repo = update_local_repo(repo) + repo.exporter.export_repo(repo.gitdir, refs) + + print "done" + + +def do_export(repo, args): + """Imports a fast-import stream from git to testgit. + """ + + if not repo.gitdir: + die("Need gitdir to export") + + update_local_repo(repo) + changed = repo.importer.do_import(repo.gitdir) + + if not repo.local: + repo.non_local.push(repo.gitdir) + + for ref in changed: + print "ok %s" % ref + print + + +COMMANDS = { + 'capabilities': do_capabilities, + 'list': do_list, + 'import': do_import, + 'export': do_export, +} + + +def sanitize(value): + """Cleans up the url. + """ + + if value.startswith('testgit::'): + value = value[9:] + + return value + + +def read_one_line(repo): + """Reads and processes one command. + """ + + sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY") + if sleepy: + debug("Sleeping %d sec before readline" % int(sleepy)) + time.sleep(int(sleepy)) + + line = sys.stdin.readline() + + cmdline = line + + if not cmdline: + warn("Unexpected EOF") + return False + + cmdline = cmdline.strip().split() + if not cmdline: + # Blank line means we're about to quit + return False + + cmd = cmdline.pop(0) + debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline)) + + if cmd not in COMMANDS: + die("Unknown command, %s", cmd) + + func = COMMANDS[cmd] + func(repo, cmdline) + sys.stdout.flush() + + return True + + +def main(args): + """Starts a new remote helper for the specified repository. + """ + + if len(args) != 3: + die("Expecting exactly three arguments.") + sys.exit(1) + + if os.getenv("GIT_DEBUG_TESTGIT"): + import git_remote_helpers.util + git_remote_helpers.util.DEBUG = True + + alias = sanitize(args[1]) + url = sanitize(args[2]) + + if not alias.isalnum(): + warn("non-alnum alias '%s'", alias) + alias = "tmp" + + args[1] = alias + args[2] = url + + repo = get_repo(alias, url) + + debug("Got arguments %s", args[1:]) + + more = True + + sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0) + while (more): + more = read_one_line(repo) + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 22f0aed6db..795edd2852 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -12,8 +12,11 @@ # But we protect ourselves from such a user mistake nevertheless. unset CDPATH -# Similarly for IFS -unset IFS +# Similarly for IFS, but some shells (e.g. FreeBSD 7.2) are buggy and +# do not equate an unset IFS with IFS with the default, so here is +# an explicit SP HT LF. +IFS=' +' git_broken_path_fix () { case ":$PATH:" in diff --git a/git-submodule.sh b/git-submodule.sh index 2365149d0b..22ec5b63b4 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -8,7 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /') USAGE="[--quiet] add [-b ] [-f|--force] [--name ] [--reference ] [--] [] or: $dashless [--quiet] status [--cached] [--recursive] [--] [...] or: $dashless [--quiet] init [--] [...] - or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference ] [--merge] [--recursive] [--] [...] + or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference ] [--merge] [--recursive] [--] [...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit ] [commit] [--] [...] or: $dashless [--quiet] foreach [--recursive] or: $dashless [--quiet] sync [--recursive] [--] [...]" @@ -26,6 +26,7 @@ cached= recursive= init= files= +remote= nofetch= update= prefix= @@ -152,6 +153,32 @@ die_if_unmatched () fi } +# +# Print a submodule configuration setting +# +# $1 = submodule name +# $2 = option name +# $3 = default value +# +# Checks in the usual git-config places first (for overrides), +# otherwise it falls back on .gitmodules. This allows you to +# distribute project-wide defaults in .gitmodules, while still +# customizing individual repositories if necessary. If the option is +# not in .gitmodules either, print a default value. +# +get_submodule_config () { + name="$1" + option="$2" + default="$3" + value=$(git config submodule."$name"."$option") + if test -z "$value" + then + value=$(git config -f .gitmodules submodule."$name"."$option") + fi + printf '%s' "${value:-$default}" +} + + # # Map submodule path to submodule name # @@ -390,6 +417,10 @@ Use -f if you really want to add it." >&2 git config -f .gitmodules submodule."$sm_name".path "$sm_path" && git config -f .gitmodules submodule."$sm_name".url "$repo" && + if test -n "$branch" + then + git config -f .gitmodules submodule."$sm_name".branch "$branch" + fi && git add --force .gitmodules || die "$(eval_gettext "Failed to register submodule '\$sm_path'")" } @@ -533,6 +564,9 @@ cmd_update() -i|--init) init=1 ;; + --remote) + remote=1 + ;; -N|--no-fetch) nofetch=1 ;; @@ -593,6 +627,7 @@ cmd_update() fi name=$(module_name "$sm_path") || exit url=$(git config submodule."$name".url) + branch=$(get_submodule_config "$name" branch master) if ! test -z "$update" then update_module=$update @@ -627,6 +662,20 @@ Maybe you want to use 'update --init'?")" die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")" fi + if test -n "$remote" + then + if test -z "$nofetch" + then + # Fetch remote before determining tracking $sha1 + (clear_local_git_env; cd "$sm_path" && git-fetch) || + die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" + fi + remote_name=$(clear_local_git_env; cd "$sm_path" && get_default_remote) + sha1=$(clear_local_git_env; cd "$sm_path" && + git rev-parse --verify "${remote_name}/${branch}") || + die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")" + fi + if test "$subsha1" != "$sha1" -o -n "$force" then subforce=$force diff --git a/git_remote_helpers/git/importer.py b/git_remote_helpers/git/importer.py index 5c6b595e16..e28cc8f986 100644 --- a/git_remote_helpers/git/importer.py +++ b/git_remote_helpers/git/importer.py @@ -39,7 +39,7 @@ def do_import(self, base): gitdir = self.repo.gitpath else: gitdir = os.path.abspath(os.path.join(dirname, '.git')) - path = os.path.abspath(os.path.join(dirname, 'git.marks')) + path = os.path.abspath(os.path.join(dirname, 'testgit.marks')) if not os.path.exists(dirname): os.makedirs(dirname) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0f207f2e20..656b324fb7 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5528,23 +5528,30 @@ sub fill_project_list_info { sub sort_projects_list { my ($projlist, $order) = @_; - my @projects; - my %order_info = ( - project => { key => 'path', type => 'str' }, - descr => { key => 'descr_long', type => 'str' }, - owner => { key => 'owner', type => 'str' }, - age => { key => 'age', type => 'num' } - ); - my $oi = $order_info{$order}; - return @$projlist unless defined $oi; - if ($oi->{'type'} eq 'str') { - @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist; - } else { - @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist; + sub order_str { + my $key = shift; + return sub { $a->{$key} cmp $b->{$key} }; } - return @projects; + sub order_num_then_undef { + my $key = shift; + return sub { + defined $a->{$key} ? + (defined $b->{$key} ? $a->{$key} <=> $b->{$key} : -1) : + (defined $b->{$key} ? 1 : 0) + }; + } + + my %orderings = ( + project => order_str('path'), + descr => order_str('descr_long'), + owner => order_str('owner'), + age => order_num_then_undef('age'), + ); + + my $ordering = $orderings{$order}; + return defined $ordering ? sort $ordering @$projlist : @$projlist; } # returns a hash of categories, containing the list of project diff --git a/graph.c b/graph.c index e864fe2c6a..391a712e5e 100644 --- a/graph.c +++ b/graph.c @@ -1227,7 +1227,7 @@ void graph_show_commit(struct git_graph *graph) if (!graph) return; - while (!shown_commit_line) { + while (!shown_commit_line && !graph_is_commit_finished(graph)) { shown_commit_line = graph_next_line(graph, &msgbuf); fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); if (!shown_commit_line) diff --git a/http-push.c b/http-push.c index 8701c1215d..9923441a4e 100644 --- a/http-push.c +++ b/http-push.c @@ -172,28 +172,7 @@ enum dav_header_flag { static char *xml_entities(const char *s) { struct strbuf buf = STRBUF_INIT; - while (*s) { - size_t len = strcspn(s, "\"<>&"); - strbuf_add(&buf, s, len); - s += len; - switch (*s) { - case '"': - strbuf_addstr(&buf, """); - break; - case '<': - strbuf_addstr(&buf, "<"); - break; - case '>': - strbuf_addstr(&buf, ">"); - break; - case '&': - strbuf_addstr(&buf, "&"); - break; - case 0: - return strbuf_detach(&buf, NULL); - } - s++; - } + strbuf_addstr_xml_quoted(&buf, s); return strbuf_detach(&buf, NULL); } diff --git a/http.c b/http.c index 0a8abf3be3..44f35256e4 100644 --- a/http.c +++ b/http.c @@ -236,6 +236,7 @@ static int has_cert_password(void) return 0; if (!cert_auth.password) { cert_auth.protocol = xstrdup("cert"); + cert_auth.username = xstrdup(""); cert_auth.path = xstrdup(ssl_cert); credential_fill(&cert_auth); } diff --git a/imap-send.c b/imap-send.c index d42e471297..e521e2fd22 100644 --- a/imap-send.c +++ b/imap-send.c @@ -69,8 +69,7 @@ struct store { }; struct msg_data { - char *data; - int len; + struct strbuf data; unsigned char flags; }; @@ -1264,45 +1263,49 @@ static int imap_make_flags(int flags, char *buf) return d; } -static void lf_to_crlf(struct msg_data *msg) +static void lf_to_crlf(struct strbuf *msg) { + size_t new_len; char *new; int i, j, lfnum = 0; - if (msg->data[0] == '\n') + if (msg->buf[0] == '\n') lfnum++; for (i = 1; i < msg->len; i++) { - if (msg->data[i - 1] != '\r' && msg->data[i] == '\n') + if (msg->buf[i - 1] != '\r' && msg->buf[i] == '\n') lfnum++; } - new = xmalloc(msg->len + lfnum); - if (msg->data[0] == '\n') { + new_len = msg->len + lfnum; + new = xmalloc(new_len + 1); + if (msg->buf[0] == '\n') { new[0] = '\r'; new[1] = '\n'; i = 1; j = 2; } else { - new[0] = msg->data[0]; + new[0] = msg->buf[0]; i = 1; j = 1; } for ( ; i < msg->len; i++) { - if (msg->data[i] != '\n') { - new[j++] = msg->data[i]; + if (msg->buf[i] != '\n') { + new[j++] = msg->buf[i]; continue; } - if (msg->data[i - 1] != '\r') + if (msg->buf[i - 1] != '\r') new[j++] = '\r'; /* otherwise it already had CR before */ new[j++] = '\n'; } - msg->len += lfnum; - free(msg->data); - msg->data = new; + strbuf_attach(msg, new, new_len, new_len + 1); } -static int imap_store_msg(struct store *gctx, struct msg_data *data) +/* + * Store msg to IMAP. Also detach and free the data from msg->data, + * leaving msg->data empty. + */ +static int imap_store_msg(struct store *gctx, struct msg_data *msg) { struct imap_store *ctx = (struct imap_store *)gctx; struct imap *imap = ctx->imap; @@ -1311,16 +1314,15 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data) int ret, d; char flagstr[128]; - lf_to_crlf(data); + lf_to_crlf(&msg->data); memset(&cb, 0, sizeof(cb)); - cb.dlen = data->len; - cb.data = xmalloc(cb.dlen); - memcpy(cb.data, data->data, data->len); + cb.dlen = msg->data.len; + cb.data = strbuf_detach(&msg->data, NULL); d = 0; - if (data->flags) { - d = imap_make_flags(data->flags, flagstr); + if (msg->flags) { + d = imap_make_flags(msg->flags, flagstr); flagstr[d++] = ' '; } flagstr[d] = 0; @@ -1337,75 +1339,46 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data) return DRV_OK; } -static void encode_html_chars(struct strbuf *p) -{ - int i; - for (i = 0; i < p->len; i++) { - if (p->buf[i] == '&') - strbuf_splice(p, i, 1, "&", 5); - if (p->buf[i] == '<') - strbuf_splice(p, i, 1, "<", 4); - if (p->buf[i] == '>') - strbuf_splice(p, i, 1, ">", 4); - if (p->buf[i] == '"') - strbuf_splice(p, i, 1, """, 6); - } -} -static void wrap_in_html(struct msg_data *msg) +static void wrap_in_html(struct strbuf *msg) { struct strbuf buf = STRBUF_INIT; - struct strbuf **lines; - struct strbuf **p; static char *content_type = "Content-Type: text/html;\n"; static char *pre_open = "
\n";
 	static char *pre_close = "
\n"; - int added_header = 0; - - strbuf_attach(&buf, msg->data, msg->len, msg->len); - lines = strbuf_split(&buf, '\n'); - strbuf_release(&buf); - for (p = lines; *p; p++) { - if (! added_header) { - if ((*p)->len == 1 && *((*p)->buf) == '\n') { - strbuf_addstr(&buf, content_type); - strbuf_addbuf(&buf, *p); - strbuf_addstr(&buf, pre_open); - added_header = 1; - continue; - } - } - else - encode_html_chars(*p); - strbuf_addbuf(&buf, *p); - } + const char *body = strstr(msg->buf, "\n\n"); + + if (!body) + return; /* Headers but no body; no wrapping needed */ + + body += 2; + + strbuf_add(&buf, msg->buf, body - msg->buf - 1); + strbuf_addstr(&buf, content_type); + strbuf_addch(&buf, '\n'); + strbuf_addstr(&buf, pre_open); + strbuf_addstr_xml_quoted(&buf, body); strbuf_addstr(&buf, pre_close); - strbuf_list_free(lines); - msg->len = buf.len; - msg->data = strbuf_detach(&buf, NULL); + + strbuf_release(msg); + *msg = buf; } #define CHUNKSIZE 0x1000 -static int read_message(FILE *f, struct msg_data *msg) +static int read_message(FILE *f, struct strbuf *all_msgs) { - struct strbuf buf = STRBUF_INIT; - - memset(msg, 0, sizeof(*msg)); - do { - if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0) + if (strbuf_fread(all_msgs, CHUNKSIZE, f) <= 0) break; } while (!feof(f)); - msg->len = buf.len; - msg->data = strbuf_detach(&buf, NULL); - return msg->len; + return ferror(f) ? -1 : 0; } -static int count_messages(struct msg_data *msg) +static int count_messages(struct strbuf *all_msgs) { int count = 0; - char *p = msg->data; + char *p = all_msgs->buf; while (1) { if (!prefixcmp(p, "From ")) { @@ -1426,34 +1399,39 @@ static int count_messages(struct msg_data *msg) return count; } -static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs) +/* + * Copy the next message from all_msgs, starting at offset *ofs, to + * msg. Update *ofs to the start of the following message. Return + * true iff a message was successfully copied. + */ +static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) { char *p, *data; + size_t len; - memset(msg, 0, sizeof *msg); if (*ofs >= all_msgs->len) return 0; - data = &all_msgs->data[*ofs]; - msg->len = all_msgs->len - *ofs; + data = &all_msgs->buf[*ofs]; + len = all_msgs->len - *ofs; - if (msg->len < 5 || prefixcmp(data, "From ")) + if (len < 5 || prefixcmp(data, "From ")) return 0; p = strchr(data, '\n'); if (p) { - p = &p[1]; - msg->len -= p-data; - *ofs += p-data; + p++; + len -= p - data; + *ofs += p - data; data = p; } p = strstr(data, "\nFrom "); if (p) - msg->len = &p[1] - data; + len = &p[1] - data; - msg->data = xmemdupz(data, msg->len); - *ofs += msg->len; + strbuf_add(msg, data, len); + *ofs += len; return 1; } @@ -1504,7 +1482,8 @@ static int git_imap_config(const char *key, const char *val, void *cb) int main(int argc, char **argv) { - struct msg_data all_msgs, msg; + struct strbuf all_msgs = STRBUF_INIT; + struct msg_data msg = {STRBUF_INIT, 0}; struct store *ctx = NULL; int ofs = 0; int r; @@ -1537,7 +1516,12 @@ int main(int argc, char **argv) } /* read the messages */ - if (!read_message(stdin, &all_msgs)) { + if (read_message(stdin, &all_msgs)) { + fprintf(stderr, "error reading input\n"); + return 1; + } + + if (all_msgs.len == 0) { fprintf(stderr, "nothing to send\n"); return 1; } @@ -1559,11 +1543,12 @@ int main(int argc, char **argv) ctx->name = imap_folder; while (1) { unsigned percent = n * 100 / total; + fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); - if (!split_msg(&all_msgs, &msg, &ofs)) + if (!split_msg(&all_msgs, &msg.data, &ofs)) break; if (server.use_html) - wrap_in_html(&msg); + wrap_in_html(&msg.data); r = imap_store_msg(ctx, &msg); if (r != DRV_OK) break; diff --git a/log-tree.c b/log-tree.c index 4f86defe32..8876c736d4 100644 --- a/log-tree.c +++ b/log-tree.c @@ -671,6 +671,7 @@ void show_log(struct rev_info *opt) ctx.preserve_subject = opt->preserve_subject; ctx.reflog_info = opt->reflog_info; ctx.fmt = opt->commit_format; + ctx.color = opt->diffopt.use_color; pretty_print_commit(&ctx, commit, &msgbuf); if (opt->add_signoff) diff --git a/mailmap.c b/mailmap.c index ea4b471ede..b16542febe 100644 --- a/mailmap.c +++ b/mailmap.c @@ -10,6 +10,7 @@ static inline void debug_mm(const char *format, ...) {} #endif const char *git_mailmap_file; +const char *git_mailmap_blob; struct mailmap_info { char *name; @@ -129,54 +130,119 @@ static char *parse_name_and_email(char *buffer, char **name, return (*right == '\0' ? NULL : right); } -static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev) +static void read_mailmap_line(struct string_list *map, char *buffer, + char **repo_abbrev) +{ + char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL; + if (buffer[0] == '#') { + static const char abbrev[] = "# repo-abbrev:"; + int abblen = sizeof(abbrev) - 1; + int len = strlen(buffer); + + if (!repo_abbrev) + return; + + if (len && buffer[len - 1] == '\n') + buffer[--len] = 0; + if (!strncmp(buffer, abbrev, abblen)) { + char *cp; + + if (repo_abbrev) + free(*repo_abbrev); + *repo_abbrev = xmalloc(len); + + for (cp = buffer + abblen; isspace(*cp); cp++) + ; /* nothing */ + strcpy(*repo_abbrev, cp); + } + return; + } + if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) + parse_name_and_email(name2, &name2, &email2, 1); + + if (email1) + add_mapping(map, name1, email1, name2, email2); +} + +static int read_mailmap_file(struct string_list *map, const char *filename, + char **repo_abbrev) { char buffer[1024]; - FILE *f = (filename == NULL ? NULL : fopen(filename, "r")); + FILE *f; - if (f == NULL) - return 1; - while (fgets(buffer, sizeof(buffer), f) != NULL) { - char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL; - if (buffer[0] == '#') { - static const char abbrev[] = "# repo-abbrev:"; - int abblen = sizeof(abbrev) - 1; - int len = strlen(buffer); - - if (!repo_abbrev) - continue; - - if (len && buffer[len - 1] == '\n') - buffer[--len] = 0; - if (!strncmp(buffer, abbrev, abblen)) { - char *cp; - - if (repo_abbrev) - free(*repo_abbrev); - *repo_abbrev = xmalloc(len); - - for (cp = buffer + abblen; isspace(*cp); cp++) - ; /* nothing */ - strcpy(*repo_abbrev, cp); - } - continue; - } - if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) - parse_name_and_email(name2, &name2, &email2, 1); + if (!filename) + return 0; - if (email1) - add_mapping(map, name1, email1, name2, email2); + f = fopen(filename, "r"); + if (!f) { + if (errno == ENOENT) + return 0; + return error("unable to open mailmap at %s: %s", + filename, strerror(errno)); } + + while (fgets(buffer, sizeof(buffer), f) != NULL) + read_mailmap_line(map, buffer, repo_abbrev); fclose(f); return 0; } +static void read_mailmap_buf(struct string_list *map, + const char *buf, unsigned long len, + char **repo_abbrev) +{ + while (len) { + const char *end = strchrnul(buf, '\n'); + unsigned long linelen = end - buf + 1; + char *line = xmemdupz(buf, linelen); + + read_mailmap_line(map, line, repo_abbrev); + + free(line); + buf += linelen; + len -= linelen; + } +} + +static int read_mailmap_blob(struct string_list *map, + const char *name, + char **repo_abbrev) +{ + unsigned char sha1[20]; + char *buf; + unsigned long size; + enum object_type type; + + if (!name) + return 0; + if (get_sha1(name, sha1) < 0) + return 0; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("unable to read mailmap object at %s", name); + if (type != OBJ_BLOB) + return error("mailmap is not a blob: %s", name); + + read_mailmap_buf(map, buf, size, repo_abbrev); + + free(buf); + return 0; +} + int read_mailmap(struct string_list *map, char **repo_abbrev) { + int err = 0; + map->strdup_strings = 1; - /* each failure returns 1, so >1 means both calls failed */ - return read_single_mailmap(map, ".mailmap", repo_abbrev) + - read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1; + + if (!git_mailmap_blob && is_bare_repository()) + git_mailmap_blob = "HEAD:.mailmap"; + + err |= read_mailmap_file(map, ".mailmap", repo_abbrev); + err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev); + err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev); + return err; } void clear_mailmap(struct string_list *map) diff --git a/mergetools/p4merge b/mergetools/p4merge index 295361a8aa..52f7c8f705 100644 --- a/mergetools/p4merge +++ b/mergetools/p4merge @@ -1,29 +1,21 @@ diff_cmd () { + empty_file= + # p4merge does not like /dev/null - rm_local= - rm_remote= if test "/dev/null" = "$LOCAL" then - LOCAL="./p4merge-dev-null.LOCAL.$$" - >"$LOCAL" - rm_local=true + LOCAL="$(create_empty_file)" fi if test "/dev/null" = "$REMOTE" then - REMOTE="./p4merge-dev-null.REMOTE.$$" - >"$REMOTE" - rm_remote=true + REMOTE="$(create_empty_file)" fi "$merge_tool_path" "$LOCAL" "$REMOTE" - if test -n "$rm_local" - then - rm -f "$LOCAL" - fi - if test -n "$rm_remote" + if test -n "$empty_file" then - rm -f "$REMOTE" + rm -f "$empty_file" fi } @@ -33,3 +25,10 @@ merge_cmd () { "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED" check_unchanged } + +create_empty_file () { + empty_file="${TMPDIR:-/tmp}/git-difftool-p4merge-empty-file.$$" + >"$empty_file" + + printf "$empty_file" +} diff --git a/parse-options.c b/parse-options.c index c1c66bd408..67e98a6323 100644 --- a/parse-options.c +++ b/parse-options.c @@ -18,15 +18,6 @@ int optbug(const struct option *opt, const char *reason) return error("BUG: switch '%c' %s", opt->short_name, reason); } -int opterror(const struct option *opt, const char *reason, int flags) -{ - if (flags & OPT_SHORT) - return error("switch `%c' %s", opt->short_name, reason); - if (flags & OPT_UNSET) - return error("option `no-%s' %s", opt->long_name, reason); - return error("option `%s' %s", opt->long_name, reason); -} - static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, int flags, const char **arg) { @@ -594,3 +585,12 @@ static int parse_options_usage(struct parse_opt_ctx_t *ctx, return usage_with_options_internal(ctx, usagestr, opts, 0, err); } +#undef opterror +int opterror(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + return error("switch `%c' %s", opt->short_name, reason); + if (flags & OPT_UNSET) + return error("option `no-%s' %s", opt->long_name, reason); + return error("option `%s' %s", opt->long_name, reason); +} diff --git a/parse-options.h b/parse-options.h index 71a39c60d9..e703853749 100644 --- a/parse-options.h +++ b/parse-options.h @@ -177,6 +177,10 @@ extern NORETURN void usage_msg_opt(const char *msg, extern int optbug(const struct option *opt, const char *reason); extern int opterror(const struct option *opt, const char *reason, int flags); +#ifdef __GNUC__ +#define opterror(o,r,f) (opterror((o),(r),(f)), -1) +#endif + /*----- incremental advanced APIs -----*/ enum { diff --git a/path.c b/path.c index cbbdf7d6ba..d3d3f8b8ad 100644 --- a/path.c +++ b/path.c @@ -12,6 +12,7 @@ */ #include "cache.h" #include "strbuf.h" +#include "string-list.h" static char bad_path[] = "/bad-path/"; @@ -569,43 +570,38 @@ int normalize_path_copy(char *dst, const char *src) /* * path = Canonical absolute path - * prefix_list = Colon-separated list of absolute paths + * prefixes = string_list containing normalized, absolute paths without + * trailing slashes (except for the root directory, which is denoted by "/"). * - * Determines, for each path in prefix_list, whether the "prefix" really + * Determines, for each path in prefixes, whether the "prefix" * is an ancestor directory of path. Returns the length of the longest * ancestor directory, excluding any trailing slashes, or -1 if no prefix - * is an ancestor. (Note that this means 0 is returned if prefix_list is - * "/".) "/foo" is not considered an ancestor of "/foobar". Directories + * is an ancestor. (Note that this means 0 is returned if prefixes is + * ["/"].) "/foo" is not considered an ancestor of "/foobar". Directories * are not considered to be their own ancestors. path must be in a * canonical form: empty components, or "." or ".." components are not - * allowed. prefix_list may be null, which is like "". + * allowed. */ -int longest_ancestor_length(const char *path, const char *prefix_list) +int longest_ancestor_length(const char *path, struct string_list *prefixes) { - char buf[PATH_MAX+1]; - const char *ceil, *colon; - int len, max_len = -1; + int i, max_len = -1; - if (prefix_list == NULL || !strcmp(path, "/")) + if (!strcmp(path, "/")) return -1; - for (colon = ceil = prefix_list; *colon; ceil = colon+1) { - for (colon = ceil; *colon && *colon != PATH_SEP; colon++); - len = colon - ceil; - if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) - continue; - strlcpy(buf, ceil, len+1); - if (normalize_path_copy(buf, buf) < 0) - continue; - len = strlen(buf); - if (len > 0 && buf[len-1] == '/') - buf[--len] = '\0'; + for (i = 0; i < prefixes->nr; i++) { + const char *ceil = prefixes->items[i].string; + int len = strlen(ceil); - if (!strncmp(path, buf, len) && - path[len] == '/' && - len > max_len) { + if (len == 1 && ceil[0] == '/') + len = 0; /* root matches anything, with length 0 */ + else if (!strncmp(path, ceil, len) && path[len] == '/') + ; /* match of length len */ + else + continue; /* no match */ + + if (len > max_len) max_len = len; - } } return max_len; diff --git a/perl/Git.pm b/perl/Git.pm index 497f420178..931047c51d 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -58,7 +58,7 @@ =head1 SYNOPSIS command_output_pipe command_input_pipe command_close_pipe command_bidi_pipe command_close_bidi_pipe version exec_path html_path hash_object git_cmd_try - remote_refs + remote_refs prompt temp_acquire temp_release temp_reset temp_path); @@ -511,6 +511,58 @@ sub version { sub html_path { command_oneline('--html-path') } +=item prompt ( PROMPT , ISPASSWORD ) + +Query user C and return answer from user. + +Honours GIT_ASKPASS and SSH_ASKPASS environment variables for querying +the user. If no *_ASKPASS variable is set or an error occoured, +the terminal is tried as a fallback. +If C is set and true, the terminal disables echo. + +=cut + +sub prompt { + my ($prompt, $isPassword) = @_; + my $ret; + if (exists $ENV{'GIT_ASKPASS'}) { + $ret = _prompt($ENV{'GIT_ASKPASS'}, $prompt); + } + if (!defined $ret && exists $ENV{'SSH_ASKPASS'}) { + $ret = _prompt($ENV{'SSH_ASKPASS'}, $prompt); + } + if (!defined $ret) { + print STDERR $prompt; + STDERR->flush; + if (defined $isPassword && $isPassword) { + require Term::ReadKey; + Term::ReadKey::ReadMode('noecho'); + $ret = ''; + while (defined(my $key = Term::ReadKey::ReadKey(0))) { + last if $key =~ /[\012\015]/; # \n\r + $ret .= $key; + } + Term::ReadKey::ReadMode('restore'); + print STDERR "\n"; + STDERR->flush; + } else { + chomp($ret = ); + } + } + return $ret; +} + +sub _prompt { + my ($askpass, $prompt) = @_; + return unless length $askpass; + $prompt =~ s/\n/ /g; + my $ret; + open my $fh, "-|", $askpass, $prompt or return; + $ret = <$fh>; + $ret =~ s/[\015\012]//g; # strip \r\n, chomp does not work on all systems (i.e. windows) as expected + close ($fh); + return $ret; +} =item repo_path () diff --git a/perl/Git/SVN/Prompt.pm b/perl/Git/SVN/Prompt.pm index 3a6f8af0d9..74daa7a597 100644 --- a/perl/Git/SVN/Prompt.pm +++ b/perl/Git/SVN/Prompt.pm @@ -62,16 +62,16 @@ sub ssl_server_trust { issuer_dname fingerprint); my $choice; prompt: - print STDERR $may_save ? + my $options = $may_save ? "(R)eject, accept (t)emporarily or accept (p)ermanently? " : "(R)eject or accept (t)emporarily? "; STDERR->flush; - $choice = lc(substr( || 'R', 0, 1)); - if ($choice =~ /^t$/i) { + $choice = lc(substr(Git::prompt("Certificate problem.\n" . $options) || 'R', 0, 1)); + if ($choice eq 't') { $cred->may_save(undef); - } elsif ($choice =~ /^r$/i) { + } elsif ($choice eq 'r') { return -1; - } elsif ($may_save && $choice =~ /^p$/i) { + } elsif ($may_save && $choice eq 'p') { $cred->may_save($may_save); } else { goto prompt; @@ -109,9 +109,7 @@ sub username { if (defined $_username) { $username = $_username; } else { - print STDERR "Username: "; - STDERR->flush; - chomp($username = ); + $username = Git::prompt("Username: "); } $cred->username($username); $cred->may_save($may_save); @@ -120,25 +118,7 @@ sub username { sub _read_password { my ($prompt, $realm) = @_; - my $password = ''; - if (exists $ENV{GIT_ASKPASS}) { - open(PH, "-|", $ENV{GIT_ASKPASS}, $prompt); - $password = ; - $password =~ s/[\012\015]//; # \n\r - close(PH); - } else { - print STDERR $prompt; - STDERR->flush; - require Term::ReadKey; - Term::ReadKey::ReadMode('noecho'); - while (defined(my $key = Term::ReadKey::ReadKey(0))) { - last if $key =~ /[\012\015]/; # \n\r - $password .= $key; - } - Term::ReadKey::ReadMode('restore'); - print STDERR "\n"; - STDERR->flush; - } + my $password = Git::prompt($prompt, 1); $password; } diff --git a/pretty.c b/pretty.c index 5bdc2e70bc..92c839fe64 100644 --- a/pretty.c +++ b/pretty.c @@ -567,7 +567,7 @@ char *logmsg_reencode(const struct commit *commit, char *encoding; char *out; - if (!*output_encoding) + if (!output_encoding || !*output_encoding) return NULL; encoding = get_header(commit, "encoding"); use_encoding = encoding ? encoding : utf8; @@ -960,12 +960,19 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder, switch (placeholder[0]) { case 'C': if (placeholder[1] == '(') { - const char *end = strchr(placeholder + 2, ')'); + const char *begin = placeholder + 2; + const char *end = strchr(begin, ')'); char color[COLOR_MAXLEN]; + if (!end) return 0; - color_parse_mem(placeholder + 2, - end - (placeholder + 2), + if (!memcmp(begin, "auto,", 5)) { + if (!want_color(c->pretty_ctx->color)) + return end - placeholder + 1; + begin += 5; + } + color_parse_mem(begin, + end - begin, "--pretty format", color); strbuf_addstr(sb, color); return end - placeholder + 1; @@ -1250,23 +1257,15 @@ void format_commit_message(const struct commit *commit, const struct pretty_print_context *pretty_ctx) { struct format_commit_context context; - static const char utf8[] = "UTF-8"; const char *output_enc = pretty_ctx->output_encoding; memset(&context, 0, sizeof(context)); context.commit = commit; context.pretty_ctx = pretty_ctx; context.wrap_start = sb->len; - context.message = commit->buffer; - if (output_enc) { - char *enc = get_header(commit, "encoding"); - if (strcmp(enc ? enc : utf8, output_enc)) { - context.message = logmsg_reencode(commit, output_enc); - if (!context.message) - context.message = commit->buffer; - } - free(enc); - } + context.message = logmsg_reencode(commit, output_enc); + if (!context.message) + context.message = commit->buffer; strbuf_expand(sb, format, format_commit_item, &context); rewrap_message_tail(sb, &context, 0, 0, 0); diff --git a/refs.c b/refs.c index 6cec1c8bdf..541fec2065 100644 --- a/refs.c +++ b/refs.c @@ -1744,7 +1744,8 @@ static struct lock_file packlock; static int repack_without_ref(const char *refname) { struct repack_without_ref_sb data; - struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL)); + struct ref_cache *refs = get_ref_cache(NULL); + struct ref_dir *packed = get_packed_refs(refs); if (find_ref(packed, refname) == NULL) return 0; data.refname = refname; @@ -1753,6 +1754,8 @@ static int repack_without_ref(const char *refname) unable_to_lock_error(git_path("packed-refs"), errno); return error("cannot delete '%s' from packed refs", refname); } + clear_packed_ref_cache(refs); + packed = get_packed_refs(refs); do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data); return commit_lock_file(&packlock); } diff --git a/remote.c b/remote.c index 6aa49c03df..4b1153f02c 100644 --- a/remote.c +++ b/remote.c @@ -1279,12 +1279,34 @@ int match_push_refs(struct ref *src, struct ref **dst, return 0; } +static inline int is_forwardable(struct ref* ref) +{ + struct object *o; + + if (!prefixcmp(ref->name, "refs/tags/")) + return 0; + + /* old object must be a commit */ + o = parse_object(ref->old_sha1); + if (!o || o->type != OBJ_COMMIT) + return 0; + + /* new object must be commit-ish */ + o = deref_tag(parse_object(ref->new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + + return 1; +} + void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, int force_update) { struct ref *ref; for (ref = remote_refs; ref; ref = ref->next) { + int force_ref_update = ref->force || force_update; + if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!send_mirror) @@ -1297,34 +1319,55 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, continue; } - /* This part determines what can overwrite what. - * The rules are: + /* + * The below logic determines whether an individual + * refspec A:B can be pushed. The push will succeed + * if any of the following are true: * - * (0) you can always use --force or +A:B notation to - * selectively force individual ref pairs. + * (1) the remote reference B does not exist * - * (1) if the old thing does not exist, it is OK. + * (2) the remote reference B is being removed (i.e., + * pushing :B where no source is specified) * - * (2) if you do not have the old thing, you are not allowed - * to overwrite it; you would not know what you are losing - * otherwise. + * (3) the update meets all fast-forwarding criteria: * - * (3) if both new and old are commit-ish, and new is a - * descendant of old, it is OK. + * (a) the destination is not under refs/tags/ + * (b) the old is a commit + * (c) the new is a descendant of the old * - * (4) regardless of all of the above, removing :B is - * always allowed. + * NOTE: We must actually have the old object in + * order to overwrite it in the remote reference, + * and the new object must be commit-ish. These are + * implied by (b) and (c) respectively. + * + * (4) it is forced using the +A:B notation, or by + * passing the --force argument */ - ref->nonfastforward = - !ref->deletion && - !is_null_sha1(ref->old_sha1) && - (!has_sha1_file(ref->old_sha1) - || !ref_newer(ref->new_sha1, ref->old_sha1)); + ref->not_forwardable = !is_forwardable(ref); - if (ref->nonfastforward && !ref->force && !force_update) { - ref->status = REF_STATUS_REJECT_NONFASTFORWARD; - continue; + ref->update = + !ref->deletion && + !is_null_sha1(ref->old_sha1); + + if (ref->update) { + ref->nonfastforward = + !has_sha1_file(ref->old_sha1) + || !ref_newer(ref->new_sha1, ref->old_sha1); + + if (ref->not_forwardable) { + ref->requires_force = 1; + if (!force_ref_update) { + ref->status = REF_STATUS_REJECT_ALREADY_EXISTS; + continue; + } + } else if (ref->nonfastforward) { + ref->requires_force = 1; + if (!force_ref_update) { + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + continue; + } + } } } } @@ -1370,6 +1413,16 @@ int branch_merge_matches(struct branch *branch, return refname_match(branch->merge[i]->src, refname, ref_fetch_rules); } +static int ignore_symref_update(const char *refname) +{ + unsigned char sha1[20]; + int flag; + + if (!resolve_ref_unsafe(refname, sha1, 0, &flag)) + return 0; /* non-existing refs are OK */ + return (flag & REF_ISSYMREF); +} + static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { @@ -1383,7 +1436,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, if (strchr(ref->name, '^')) continue; /* a dereference item */ if (match_name_with_pattern(refspec->src, ref->name, - refspec->dst, &expn_name)) { + refspec->dst, &expn_name) && + !ignore_symref_update(expn_name)) { struct ref *cpy = copy_ref(ref); cpy->peer_ref = alloc_ref(expn_name); diff --git a/run-command.c b/run-command.c index 3b982e4d55..24eaad5c66 100644 --- a/run-command.c +++ b/run-command.c @@ -226,7 +226,7 @@ static inline void set_cloexec(int fd) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } -static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure) +static int wait_or_whine(pid_t pid, const char *argv0) { int status, code = -1; pid_t waiting; @@ -242,7 +242,8 @@ static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure) error("waitpid is confused (%s)", argv0); } else if (WIFSIGNALED(status)) { code = WTERMSIG(status); - error("%s died of signal %d", argv0, code); + if (code != SIGINT && code != SIGQUIT) + error("%s died of signal %d", argv0, code); /* * This return value is chosen so that code & 0xff * mimics the exit code that a POSIX shell would report for @@ -432,8 +433,7 @@ int start_command(struct child_process *cmd) * At this point we know that fork() succeeded, but execvp() * failed. Errors have been reported to our stderr. */ - wait_or_whine(cmd->pid, cmd->argv[0], - cmd->silent_exec_failure); + wait_or_whine(cmd->pid, cmd->argv[0]); failed_errno = errno; cmd->pid = -1; } @@ -538,7 +538,7 @@ int start_command(struct child_process *cmd) int finish_command(struct child_process *cmd) { - return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure); + return wait_or_whine(cmd->pid, cmd->argv[0]); } int run_command(struct child_process *cmd) @@ -725,7 +725,7 @@ int start_async(struct async *async) int finish_async(struct async *async) { #ifdef NO_PTHREADS - return wait_or_whine(async->pid, "child process", 0); + return wait_or_whine(async->pid, "child process"); #else void *ret = (void *)(intptr_t)(-1); diff --git a/send-pack.c b/send-pack.c index f50dfd9f48..1c375f0a28 100644 --- a/send-pack.c +++ b/send-pack.c @@ -229,6 +229,7 @@ int send_pack(struct send_pack_args *args, /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_UPTODATE: continue; default: diff --git a/setup.c b/setup.c index 3a1b2fd455..f108c4b990 100644 --- a/setup.c +++ b/setup.c @@ -1,5 +1,6 @@ #include "cache.h" #include "dir.h" +#include "string-list.h" static int inside_git_dir = -1; static int inside_work_tree = -1; @@ -620,6 +621,27 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_ return buf.st_dev; } +/* + * A "string_list_each_func_t" function that canonicalizes an entry + * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or + * discards it if unusable. + */ +static int canonicalize_ceiling_entry(struct string_list_item *item, + void *unused) +{ + char *ceil = item->string; + const char *real_path; + + if (!*ceil || !is_absolute_path(ceil)) + return 0; + real_path = real_path_if_valid(ceil); + if (!real_path) + return 0; + free(item->string); + item->string = xstrdup(real_path); + return 1; +} + /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. @@ -627,10 +649,11 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_ static const char *setup_git_directory_gently_1(int *nongit_ok) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); + struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; static char cwd[PATH_MAX+1]; const char *gitdirenv, *ret; char *gitfile; - int len, offset, offset_parent, ceil_offset; + int len, offset, offset_parent, ceil_offset = -1; dev_t current_device = 0; int one_filesystem = 1; @@ -655,7 +678,14 @@ static const char *setup_git_directory_gently_1(int *nongit_ok) if (gitdirenv) return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); - ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); + if (env_ceiling_dirs) { + string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); + filter_string_list(&ceiling_dirs, 0, + canonicalize_ceiling_entry, NULL); + ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs); + string_list_clear(&ceiling_dirs, 0); + } + if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) ceil_offset = 1; diff --git a/strbuf.c b/strbuf.c index 05d0693eba..9a373bef70 100644 --- a/strbuf.c +++ b/strbuf.c @@ -425,6 +425,32 @@ void strbuf_add_lines(struct strbuf *out, const char *prefix, strbuf_complete_line(out); } +void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) +{ + while (*s) { + size_t len = strcspn(s, "\"<>&"); + strbuf_add(buf, s, len); + s += len; + switch (*s) { + case '"': + strbuf_addstr(buf, """); + break; + case '<': + strbuf_addstr(buf, "<"); + break; + case '>': + strbuf_addstr(buf, ">"); + break; + case '&': + strbuf_addstr(buf, "&"); + break; + case 0: + return; + } + s++; + } +} + static int is_rfc3986_reserved(char ch) { switch (ch) { diff --git a/strbuf.h b/strbuf.h index aa386c6074..ecae4e215f 100644 --- a/strbuf.h +++ b/strbuf.h @@ -136,6 +136,12 @@ extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size); +/* + * Append s to sb, with the characters '<', '>', '&' and '"' converted + * into XML entities. + */ +extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s); + static inline void strbuf_complete_line(struct strbuf *sb) { if (sb->len && sb->buf[sb->len - 1] != '\n') diff --git a/string-list.c b/string-list.c index 397e6cfa7d..480173fe6d 100644 --- a/string-list.c +++ b/string-list.c @@ -145,26 +145,6 @@ void string_list_remove_empty_items(struct string_list *list, int free_util) { filter_string_list(list, free_util, item_is_not_empty, NULL); } -char *string_list_longest_prefix(const struct string_list *prefixes, - const char *string) -{ - int i, max_len = -1; - char *retval = NULL; - - for (i = 0; i < prefixes->nr; i++) { - char *prefix = prefixes->items[i].string; - if (!prefixcmp(string, prefix)) { - int len = strlen(prefix); - if (len > max_len) { - retval = prefix; - max_len = len; - } - } - } - - return retval; -} - void string_list_clear(struct string_list *list, int free_util) { if (list->items) { diff --git a/string-list.h b/string-list.h index c50b0d0dea..db1284861a 100644 --- a/string-list.h +++ b/string-list.h @@ -45,15 +45,6 @@ void filter_string_list(struct string_list *list, int free_util, */ void string_list_remove_empty_items(struct string_list *list, int free_util); -/* - * Return the longest string in prefixes that is a prefix (in the - * sense of prefixcmp()) of string, or NULL if no such prefix exists. - * This function does not require the string_list to be sorted (it - * does a linear search). - */ -char *string_list_longest_prefix(const struct string_list *prefixes, const char *string); - - /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); int string_list_find_insert_index(const struct string_list *list, const char *string, diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh index 0f76f6cdc0..ae8883a075 100644 --- a/t/lib-gettext.sh +++ b/t/lib-gettext.sh @@ -14,12 +14,14 @@ export GIT_TEXTDOMAINDIR GIT_PO_PATH if test_have_prereq GETTEXT && ! test_have_prereq GETTEXT_POISON then # is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian - is_IS_locale=$(locale -a | sed -n '/^is_IS\.[uU][tT][fF]-*8$/{ + is_IS_locale=$(locale -a 2>/dev/null | + sed -n '/^is_IS\.[uU][tT][fF]-*8$/{ p q }') # is_IS.ISO8859-1 on Solaris and FreeBSD, is_IS.iso88591 on Debian - is_IS_iso_locale=$(locale -a | sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{ + is_IS_iso_locale=$(locale -a 2>/dev/null | + sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{ p q }') diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 562cf41cad..cefe33d6d1 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -45,39 +45,176 @@ test_expect_failure 'pretend we have a known breakage' ' false ' -test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' " - mkdir passing-todo && - (cd passing-todo && - cat >passing-todo.sh <<-EOF && - #!$SHELL_PATH - - test_description='A passing TODO test +run_sub_test_lib_test () { + name="$1" descr="$2" # stdin is the body of the test code + mkdir "$name" && + ( + cd "$name" && + cat >"$name.sh" <<-EOF && + #!$SHELL_PATH + + test_description='$descr (run in sub test-lib) + + This is run in a sub test-lib so that we do not get incorrect + passing metrics + ' + + # Point to the t/test-lib.sh, which isn't in ../ as usual + . "\$TEST_DIRECTORY"/test-lib.sh + EOF + cat >>"$name.sh" && + chmod +x "$name.sh" && + export TEST_DIRECTORY && + ./"$name.sh" >out 2>err + ) +} - This is run in a sub test-lib so that we do not get incorrect - passing metrics - ' +check_sub_test_lib_test () { + name="$1" # stdin is the expected output from the test + ( + cd "$name" && + ! test -s err && + sed -e 's/^> //' -e 's/Z$//' >expect && + test_cmp expect out + ) +} + +test_expect_success 'pretend we have a fully passing test suite' " + run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF && + for i in 1 2 3 + do + test_expect_success \"passing test #\$i\" 'true' + done + test_done + EOF + check_sub_test_lib_test full-pass <<-\\EOF + > ok 1 - passing test #1 + > ok 2 - passing test #2 + > ok 3 - passing test #3 + > # passed all 3 test(s) + > 1..3 + EOF +" - # Point to the t/test-lib.sh, which isn't in ../ as usual - TEST_DIRECTORY=\"$TEST_DIRECTORY\" - . \"\$TEST_DIRECTORY\"/test-lib.sh +test_expect_success 'pretend we have a partially passing test suite' " + test_must_fail run_sub_test_lib_test \ + partial-pass '2/3 tests passing' <<-\\EOF && + test_expect_success 'passing test #1' 'true' + test_expect_success 'failing test #2' 'false' + test_expect_success 'passing test #3' 'true' + test_done + EOF + check_sub_test_lib_test partial-pass <<-\\EOF + > ok 1 - passing test #1 + > not ok 2 - failing test #2 + # false + > ok 3 - passing test #3 + > # failed 1 among 3 test(s) + > 1..3 + EOF +" - test_expect_failure 'pretend we have fixed a known breakage' ' - : - ' +test_expect_success 'pretend we have a known breakage' " + run_sub_test_lib_test failing-todo 'A failing TODO test' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_failure 'pretend we have a known breakage' 'false' + test_done + EOF + check_sub_test_lib_test failing-todo <<-\\EOF + > ok 1 - passing test + > not ok 2 - pretend we have a known breakage # TODO known breakage + > # still have 1 known breakage(s) + > # passed all remaining 1 test(s) + > 1..2 + EOF +" +test_expect_success 'pretend we have fixed a known breakage' " + run_sub_test_lib_test passing-todo 'A passing TODO test' <<-\\EOF && + test_expect_failure 'pretend we have fixed a known breakage' 'true' test_done EOF - chmod +x passing-todo.sh && - ./passing-todo.sh >out 2>err && - ! test -s err && - sed -e 's/^> //' >expect <<-\\EOF && - > ok 1 - pretend we have fixed a known breakage # TODO known breakage - > # fixed 1 known breakage(s) - > # passed all 1 test(s) + check_sub_test_lib_test passing-todo <<-\\EOF + > ok 1 - pretend we have fixed a known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) > 1..1 EOF - test_cmp expect out) " + +test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' " + run_sub_test_lib_test partially-passing-todos \ + '2 TODO tests, one passing' <<-\\EOF && + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_success 'pretend we have a passing test' 'true' + test_expect_failure 'pretend we have fixed another known breakage' 'true' + test_done + EOF + check_sub_test_lib_test partially-passing-todos <<-\\EOF + > not ok 1 - pretend we have a known breakage # TODO known breakage + > ok 2 - pretend we have a passing test + > ok 3 - pretend we have fixed another known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > # still have 1 known breakage(s) + > # passed all remaining 1 test(s) + > 1..3 + EOF +" + +test_expect_success 'pretend we have a pass, fail, and known breakage' " + test_must_fail run_sub_test_lib_test \ + mixed-results1 'mixed results #1' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_success 'failing test' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_done + EOF + check_sub_test_lib_test mixed-results1 <<-\\EOF + > ok 1 - passing test + > not ok 2 - failing test + > # false + > not ok 3 - pretend we have a known breakage # TODO known breakage + > # still have 1 known breakage(s) + > # failed 1 among remaining 2 test(s) + > 1..3 + EOF +" + +test_expect_success 'pretend we have a mix of all possible results' " + test_must_fail run_sub_test_lib_test \ + mixed-results2 'mixed results #2' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'failing test' 'false' + test_expect_success 'failing test' 'false' + test_expect_success 'failing test' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_failure 'pretend we have fixed a known breakage' 'true' + test_done + EOF + check_sub_test_lib_test mixed-results2 <<-\\EOF + > ok 1 - passing test + > ok 2 - passing test + > ok 3 - passing test + > ok 4 - passing test + > not ok 5 - failing test + > # false + > not ok 6 - failing test + > # false + > not ok 7 - failing test + > # false + > not ok 8 - pretend we have a known breakage # TODO known breakage + > not ok 9 - pretend we have a known breakage # TODO known breakage + > ok 10 - pretend we have fixed a known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > # still have 2 known breakage(s) + > # failed 3 among remaining 7 test(s) + > 1..10 + EOF +" + test_set_prereq HAVEIT haveit=no test_expect_success HAVEIT 'test runs if prerequisite is satisfied' ' @@ -159,19 +296,8 @@ then fi test_expect_success 'tests clean up even on failures' " - mkdir failing-cleanup && - ( - cd failing-cleanup && - - cat >failing-cleanup.sh <<-EOF && - #!$SHELL_PATH - - test_description='Failing tests with cleanup commands' - - # Point to the t/test-lib.sh, which isn't in ../ as usual - TEST_DIRECTORY=\"$TEST_DIRECTORY\" - . \"\$TEST_DIRECTORY\"/test-lib.sh - + test_must_fail run_sub_test_lib_test \ + failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF && test_expect_success 'tests clean up even after a failure' ' touch clean-after-failure && test_when_finished rm clean-after-failure && @@ -181,29 +307,21 @@ test_expect_success 'tests clean up even on failures' " test_when_finished \"(exit 2)\" ' test_done - EOF - - chmod +x failing-cleanup.sh && - test_must_fail ./failing-cleanup.sh >out 2>err && - ! test -s err && - ! test -f \"trash directory.failing-cleanup/clean-after-failure\" && - sed -e 's/Z$//' -e 's/^> //' >expect <<-\\EOF && - > not ok - 1 tests clean up even after a failure + check_sub_test_lib_test failing-cleanup <<-\\EOF + > not ok 1 - tests clean up even after a failure > # Z > # touch clean-after-failure && > # test_when_finished rm clean-after-failure && > # (exit 1) > # Z - > not ok - 2 failure to clean up causes the test to fail + > not ok 2 - failure to clean up causes the test to fail > # Z > # test_when_finished \"(exit 2)\" > # Z > # failed 2 among 2 test(s) > 1..2 EOF - test_cmp expect out - ) " ################################################################ diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 4ef2345982..09a42a428e 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -93,47 +93,32 @@ norm_path /d1/s1//../s2/../../d2 /d2 POSIX norm_path /d1/.../d2 /d1/.../d2 POSIX norm_path /d1/..././../d2 /d1/d2 POSIX -ancestor / "" -1 ancestor / / -1 -ancestor /foo "" -1 -ancestor /foo : -1 -ancestor /foo ::. -1 -ancestor /foo ::..:: -1 ancestor /foo / 0 ancestor /foo /fo -1 ancestor /foo /foo -1 -ancestor /foo /foo/ -1 ancestor /foo /bar -1 -ancestor /foo /bar/ -1 ancestor /foo /foo/bar -1 -ancestor /foo /foo:/bar/ -1 -ancestor /foo /foo/:/bar/ -1 -ancestor /foo /foo::/bar/ -1 -ancestor /foo /:/foo:/bar/ 0 -ancestor /foo /foo:/:/bar/ 0 -ancestor /foo /:/bar/:/foo 0 -ancestor /foo/bar "" -1 +ancestor /foo /foo:/bar -1 +ancestor /foo /:/foo:/bar 0 +ancestor /foo /foo:/:/bar 0 +ancestor /foo /:/bar:/foo 0 ancestor /foo/bar / 0 ancestor /foo/bar /fo -1 -ancestor /foo/bar foo -1 ancestor /foo/bar /foo 4 -ancestor /foo/bar /foo/ 4 ancestor /foo/bar /foo/ba -1 ancestor /foo/bar /:/fo 0 ancestor /foo/bar /foo:/foo/ba 4 ancestor /foo/bar /bar -1 -ancestor /foo/bar /bar/ -1 -ancestor /foo/bar /fo: -1 -ancestor /foo/bar :/fo -1 -ancestor /foo/bar /foo:/bar/ 4 -ancestor /foo/bar /:/foo:/bar/ 4 -ancestor /foo/bar /foo:/:/bar/ 4 -ancestor /foo/bar /:/bar/:/fo 0 -ancestor /foo/bar /:/bar/ 0 -ancestor /foo/bar .:/foo/. 4 -ancestor /foo/bar .:/foo/.:.: 4 -ancestor /foo/bar /foo/./:.:/bar 4 -ancestor /foo/bar .:/bar -1 +ancestor /foo/bar /fo -1 +ancestor /foo/bar /foo:/bar 4 +ancestor /foo/bar /:/foo:/bar 4 +ancestor /foo/bar /foo:/:/bar 4 +ancestor /foo/bar /:/bar:/fo 0 +ancestor /foo/bar /:/bar 0 +ancestor /foo/bar /foo 4 +ancestor /foo/bar /foo:/bar 4 +ancestor /foo/bar /bar -1 test_expect_success 'strip_path_suffix' ' test c:/msysgit = $(test-path-utils strip_path_suffix \ diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh index 41c8826a74..dbfc05ebdc 100755 --- a/t/t0063-string-list.sh +++ b/t/t0063-string-list.sh @@ -17,14 +17,6 @@ test_split () { " } -test_longest_prefix () { - test "$(test-string-list longest_prefix "$1" "$2")" = "$3" -} - -test_no_longest_prefix () { - test_must_fail test-string-list longest_prefix "$1" "$2" -} - test_split "foo:bar:baz" ":" "-1" <out && + cat out && + grep "warning.*\\." out + ) +' + +test_expect_success 'fsck notices ".git" in trees' ' + ( + git init dotgit && + cd dotgit && + blob=$(echo foo | git hash-object -w --stdin) && + tab=$(printf "\\t") && + git mktree <<-EOF && + 100644 blob $blob$tab.git + EOF + git fsck 2>out && + cat out && + grep "warning.*\\.git" out + ) +' + test_done diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index ec35409f9c..2a4a749b4f 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -62,5 +62,25 @@ test_expect_success 'can "commit -a" with an i-t-a entry' ' git commit -a -m all ' +test_expect_success 'cache-tree invalidates i-t-a paths' ' + git reset --hard && + mkdir dir && + : >dir/foo && + git add dir/foo && + git commit -m foo && + + : >dir/bar && + git add -N dir/bar && + git diff --cached --name-only >actual && + echo dir/bar >expect && + test_cmp expect actual && + + git write-tree >/dev/null && + + git diff --cached --name-only >actual && + echo dir/bar >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 06f63848ea..37bf5f13b0 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -474,7 +474,7 @@ test_expect_success 'rm of a conflicted populated submodule with a .git director git submodule update && (cd submod && rm .git && - cp -a ../.git/modules/sub .git && + cp -R ../.git/modules/sub .git && GIT_WORK_TREE=. git config --unset core.worktree ) && test_must_fail git merge conflict2 && @@ -508,7 +508,7 @@ test_expect_success 'rm of a populated submodule with a .git directory fails eve git submodule update && (cd submod && rm .git && - cp -a ../.git/modules/sub .git && + cp -R ../.git/modules/sub .git && GIT_WORK_TREE=. git config --unset core.worktree ) && test_must_fail git rm submod && @@ -606,7 +606,7 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc git submodule update --recursive && (cd submod/subsubmod && rm .git && - cp -a ../../.git/modules/sub/modules/sub .git && + cp -R ../../.git/modules/sub/modules/sub .git && GIT_WORK_TREE=. git config --unset core.worktree ) && test_must_fail git rm submod && diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 16a4ca1d60..90fd598c74 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -155,7 +155,7 @@ test_expect_failure 'additional command line cc (rfc822)' ' git config --replace-all format.headers "Cc: R E Cipient " && git format-patch --cc="S. E. Cipient " --stdout master..side | sed -e "/^\$/q" >patch5 && grep "^Cc: R E Cipient ,\$" patch5 && - grep "^ *"S. E. Cipient" \$" patch5 + grep "^ *\"S. E. Cipient\" \$" patch5 ' test_expect_success 'command line headers' ' @@ -183,7 +183,7 @@ test_expect_success 'command line To: header (ascii)' ' test_expect_failure 'command line To: header (rfc822)' ' git format-patch --to="R. E. Cipient " --stdout master..side | sed -e "/^\$/q" >patch8 && - grep "^To: "R. E. Cipient" \$" patch8 + grep "^To: \"R. E. Cipient\" \$" patch8 ' test_expect_failure 'command line To: header (rfc2047)' ' @@ -203,7 +203,7 @@ test_expect_failure 'configuration To: header (rfc822)' ' git config format.to "R. E. Cipient " && git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && - grep "^To: "R. E. Cipient" \$" patch9 + grep "^To: \"R. E. Cipient\" \$" patch9 ' test_expect_failure 'configuration To: header (rfc2047)' ' diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 6872ba1a42..5493500ef1 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -120,6 +120,30 @@ test_expect_success 'shortlog from non-git directory' ' test_cmp expect out ' +test_expect_success 'shortlog should add newline when input line matches wraplen' ' + cat >expect <<\EOF && +A U Thor (2): + bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb + aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa + +EOF + git shortlog -w >out <<\EOF && +commit 0000000000000000000000000000000000000001 +Author: A U Thor +Date: Thu Apr 7 15:14:13 2005 -0700 + + aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa + +commit 0000000000000000000000000000000000000002 +Author: A U Thor +Date: Thu Apr 7 15:14:13 2005 -0700 + + bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb + +EOF + test_cmp expect out +' + iconvfromutf8toiso88591() { printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1 } diff --git a/t/t4202-log.sh b/t/t4202-log.sh index a343bf6c62..fa686b887d 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -280,6 +280,16 @@ test_expect_success 'log --graph with merge' ' test_cmp expect actual ' +test_expect_success 'log --raw --graph -m with merge' ' + git log --raw --graph --oneline -m master | head -n 500 >actual && + grep "initial" actual +' + +test_expect_success 'diff-tree --graph' ' + git diff-tree --graph master^ | head -n 500 >actual && + grep "one" actual +' + cat > expect <<\EOF * commit master |\ Merge: A B diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 1f182f612c..aae30d97b1 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -149,6 +149,104 @@ test_expect_success 'No mailmap files, but configured' ' test_cmp expect actual ' +test_expect_success 'setup mailmap blob tests' ' + git checkout -b map && + test_when_finished "git checkout master" && + cat >just-bugs <<-\EOF && + Blob Guy + EOF + cat >both <<-\EOF && + Blob Guy + Blob Guy + EOF + git add just-bugs both && + git commit -m "my mailmaps" && + echo "Repo Guy " >.mailmap && + echo "Internal Guy " >internal.map +' + +test_expect_success 'mailmap.blob set' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Repo Guy (1): + initial + + EOF + git -c mailmap.blob=map:just-bugs shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob overrides .mailmap' ' + cat >expect <<-\EOF && + Blob Guy (2): + initial + second + + EOF + git -c mailmap.blob=map:both shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.file overrides mailmap.blob' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Internal Guy (1): + initial + + EOF + git \ + -c mailmap.blob=map:both \ + -c mailmap.file=internal.map \ + shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob can be missing' ' + cat >expect <<-\EOF && + Repo Guy (1): + initial + + nick1 (1): + second + + EOF + git -c mailmap.blob=map:nonexistent shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob defaults to off in non-bare repo' ' + git init non-bare && + ( + cd non-bare && + test_commit one .mailmap "Fake Name " && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual && + rm .mailmap && + echo " 1 A U Thor" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' ' + git clone --bare non-bare bare && + ( + cd bare && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'cleanup after mailmap.blob tests' ' + rm -f .mailmap +' + # Extended mailmap configurations should give us the following output for shortlog cat >expect <<\EOF A U Thor (1): diff --git a/t/t5002-archive-attr-pattern.sh b/t/t5002-archive-attr-pattern.sh new file mode 100755 index 0000000000..0c847fb454 --- /dev/null +++ b/t/t5002-archive-attr-pattern.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +test_description='git archive attribute pattern tests' + +. ./test-lib.sh + +test_expect_exists() { + test_expect_success " $1 exists" "test -e $1" +} + +test_expect_missing() { + test_expect_success " $1 does not exist" "test ! -e $1" +} + +test_expect_success 'setup' ' + echo ignored >ignored && + echo ignored export-ignore >>.git/info/attributes && + git add ignored && + + mkdir not-ignored-dir && + echo ignored-in-tree >not-ignored-dir/ignored && + echo not-ignored-in-tree >not-ignored-dir/ignored-only-if-dir && + git add not-ignored-dir && + + mkdir ignored-only-if-dir && + echo ignored by ignored dir >ignored-only-if-dir/ignored-by-ignored-dir && + echo ignored-only-if-dir/ export-ignore >>.git/info/attributes && + git add ignored-only-if-dir && + + + mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir && + echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir && + git add one-level-lower && + + git commit -m. && + + git clone --bare . bare && + cp .git/info/attributes bare/info/attributes +' + +test_expect_success 'git archive' ' + git archive HEAD >archive.tar && + (mkdir archive && cd archive && "$TAR" xf -) file1 && + git add file1 && + git commit -m "file1" && + git tag -f Tag && + test_must_fail git push ../child2 Tag && + git push --force ../child2 Tag && + git tag -f Tag && + test_must_fail git push ../child2 Tag HEAD~ && + git push --force ../child2 Tag + ) +' + +test_expect_success 'push requires --force to update annotated tag' ' + mk_test heads/master && + mk_child child1 && + mk_child child2 && + ( + cd child1 && + git tag -a -m "message 1" Tag && + git push ../child2 Tag:refs/tmp/Tag && + git push ../child2 Tag:refs/tmp/Tag && + >file1 && + git add file1 && + git commit -m "file1" && + git tag -f -a -m "message 2" Tag && + test_must_fail git push ../child2 Tag:refs/tmp/Tag && + git push --force ../child2 Tag:refs/tmp/Tag && + git tag -f -a -m "message 3" Tag HEAD~ && + test_must_fail git push ../child2 Tag:refs/tmp/Tag && + git push --force ../child2 Tag:refs/tmp/Tag + ) +' + test_expect_success 'push --porcelain' ' mk_empty && echo >.git/foo "To testrepo" && diff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh new file mode 100755 index 0000000000..8ed58d27f2 --- /dev/null +++ b/t/t5535-fetch-push-symref.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='avoiding conflicting update thru symref aliasing' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit one && + git clone . src && + git clone src dst1 && + git clone src dst2 && + test_commit two && + ( cd src && git pull ) +' + +test_expect_success 'push' ' + ( + cd src && + git push ../dst1 "refs/remotes/*:refs/remotes/*" + ) && + git ls-remote src "refs/remotes/*" >expect && + git ls-remote dst1 "refs/remotes/*" >actual && + test_cmp expect actual && + ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect && + ( cd dst1 && git symbolic-ref refs/remotes/origin/HEAD ) >actual && + test_cmp expect actual +' + +test_expect_success 'fetch' ' + ( + cd dst2 && + git fetch ../src "refs/remotes/*:refs/remotes/*" + ) && + git ls-remote src "refs/remotes/*" >expect && + git ls-remote dst2 "refs/remotes/*" >actual && + test_cmp expect actual && + ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect && + ( cd dst2 && git symbolic-ref refs/remotes/origin/HEAD ) >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh deleted file mode 100755 index e7dc668cef..0000000000 --- a/t/t5800-remote-helpers.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2010 Sverre Rabbelier -# - -test_description='Test remote-helper import and export commands' - -. ./test-lib.sh - -if ! test_have_prereq PYTHON ; then - skip_all='skipping git-remote-hg tests, python not available' - test_done -fi - -"$PYTHON_PATH" -c ' -import sys -if sys.hexversion < 0x02040000: - sys.exit(1) -' || { - skip_all='skipping git-remote-hg tests, python version < 2.4' - test_done -} - -compare_refs() { - git --git-dir="$1/.git" rev-parse --verify $2 >expect && - git --git-dir="$3/.git" rev-parse --verify $4 >actual && - test_cmp expect actual -} - -test_expect_success 'setup repository' ' - git init --bare server/.git && - git clone server public && - (cd public && - echo content >file && - git add file && - git commit -m one && - git push origin master) -' - -test_expect_success 'cloning from local repo' ' - git clone "testgit::${PWD}/server" localclone && - test_cmp public/file localclone/file -' - -test_expect_success 'cloning from remote repo' ' - git clone "testgit::file://${PWD}/server" clone && - test_cmp public/file clone/file -' - -test_expect_success 'create new commit on remote' ' - (cd public && - echo content >>file && - git commit -a -m two && - git push) -' - -test_expect_success 'pulling from local repo' ' - (cd localclone && git pull) && - test_cmp public/file localclone/file -' - -test_expect_success 'pulling from remote remote' ' - (cd clone && git pull) && - test_cmp public/file clone/file -' - -test_expect_success 'pushing to local repo' ' - (cd localclone && - echo content >>file && - git commit -a -m three && - git push) && - compare_refs localclone HEAD server HEAD -' - -# Generally, skip this test. It demonstrates a now-fixed race in -# git-remote-testgit, but is too slow to leave in for general use. -: test_expect_success 'racily pushing to local repo' ' - test_when_finished "rm -rf server2 localclone2" && - cp -R server server2 && - git clone "testgit::${PWD}/server2" localclone2 && - (cd localclone2 && - echo content >>file && - git commit -a -m three && - GIT_REMOTE_TESTGIT_SLEEPY=2 git push) && - compare_refs localclone2 HEAD server2 HEAD -' - -test_expect_success 'synch with changes from localclone' ' - (cd clone && - git pull) -' - -test_expect_success 'pushing remote local repo' ' - (cd clone && - echo content >>file && - git commit -a -m four && - git push) && - compare_refs clone HEAD server HEAD -' - -test_expect_success 'fetch new branch' ' - (cd public && - git checkout -b new && - echo content >>file && - git commit -a -m five && - git push origin new - ) && - (cd localclone && - git fetch origin new - ) && - compare_refs public HEAD localclone FETCH_HEAD -' - -test_expect_success 'fetch multiple branches' ' - (cd localclone && - git fetch - ) && - compare_refs server master localclone refs/remotes/origin/master && - compare_refs server new localclone refs/remotes/origin/new -' - -test_expect_success 'push when remote has extra refs' ' - (cd clone && - echo content >>file && - git commit -a -m six && - git push - ) && - compare_refs clone master server master -' - -test_expect_success 'push new branch by name' ' - (cd clone && - git checkout -b new-name && - echo content >>file && - git commit -a -m seven && - git push origin new-name - ) && - compare_refs clone HEAD server refs/heads/new-name -' - -test_expect_failure 'push new branch with old:new refspec' ' - (cd clone && - git push origin new-name:new-refspec - ) && - compare_refs clone HEAD server refs/heads/new-refspec -' - -test_done diff --git a/t/t5800-remote-testpy.sh b/t/t5800-remote-testpy.sh new file mode 100755 index 0000000000..6750961507 --- /dev/null +++ b/t/t5800-remote-testpy.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# +# Copyright (c) 2010 Sverre Rabbelier +# + +test_description='Test python remote-helper framework' + +. ./test-lib.sh + +if ! test_have_prereq PYTHON ; then + skip_all='skipping python remote-helper tests, python not available' + test_done +fi + +"$PYTHON_PATH" -c ' +import sys +if sys.hexversion < 0x02040000: + sys.exit(1) +' || { + skip_all='skipping python remote-helper tests, python version < 2.4' + test_done +} + +compare_refs() { + git --git-dir="$1/.git" rev-parse --verify $2 >expect && + git --git-dir="$3/.git" rev-parse --verify $4 >actual && + test_cmp expect actual +} + +test_expect_success 'setup repository' ' + git init --bare server/.git && + git clone server public && + (cd public && + echo content >file && + git add file && + git commit -m one && + git push origin master) +' + +test_expect_success 'cloning from local repo' ' + git clone "testpy::${PWD}/server" localclone && + test_cmp public/file localclone/file +' + +test_expect_success 'cloning from remote repo' ' + git clone "testpy::file://${PWD}/server" clone && + test_cmp public/file clone/file +' + +test_expect_success 'create new commit on remote' ' + (cd public && + echo content >>file && + git commit -a -m two && + git push) +' + +test_expect_success 'pulling from local repo' ' + (cd localclone && git pull) && + test_cmp public/file localclone/file +' + +test_expect_success 'pulling from remote remote' ' + (cd clone && git pull) && + test_cmp public/file clone/file +' + +test_expect_success 'pushing to local repo' ' + (cd localclone && + echo content >>file && + git commit -a -m three && + git push) && + compare_refs localclone HEAD server HEAD +' + +# Generally, skip this test. It demonstrates a now-fixed race in +# git-remote-testpy, but is too slow to leave in for general use. +: test_expect_success 'racily pushing to local repo' ' + test_when_finished "rm -rf server2 localclone2" && + cp -R server server2 && + git clone "testpy::${PWD}/server2" localclone2 && + (cd localclone2 && + echo content >>file && + git commit -a -m three && + GIT_REMOTE_TESTGIT_SLEEPY=2 git push) && + compare_refs localclone2 HEAD server2 HEAD +' + +test_expect_success 'synch with changes from localclone' ' + (cd clone && + git pull) +' + +test_expect_success 'pushing remote local repo' ' + (cd clone && + echo content >>file && + git commit -a -m four && + git push) && + compare_refs clone HEAD server HEAD +' + +test_expect_success 'fetch new branch' ' + (cd public && + git checkout -b new && + echo content >>file && + git commit -a -m five && + git push origin new + ) && + (cd localclone && + git fetch origin new + ) && + compare_refs public HEAD localclone FETCH_HEAD +' + +test_expect_success 'fetch multiple branches' ' + (cd localclone && + git fetch + ) && + compare_refs server master localclone refs/remotes/origin/master && + compare_refs server new localclone refs/remotes/origin/new +' + +test_expect_success 'push when remote has extra refs' ' + (cd clone && + echo content >>file && + git commit -a -m six && + git push + ) && + compare_refs clone master server master +' + +test_expect_success 'push new branch by name' ' + (cd clone && + git checkout -b new-name && + echo content >>file && + git commit -a -m seven && + git push origin new-name + ) && + compare_refs clone HEAD server refs/heads/new-name +' + +test_expect_failure 'push new branch with old:new refspec' ' + (cd clone && + git push origin new-name:new-refspec + ) && + compare_refs clone HEAD server refs/heads/new-refspec +' + +test_done diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh new file mode 100755 index 0000000000..f387027c05 --- /dev/null +++ b/t/t5801-remote-helpers.sh @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2010 Sverre Rabbelier +# + +test_description='Test remote-helper import and export commands' + +. ./test-lib.sh + +if ! type "${BASH-bash}" >/dev/null 2>&1; then + skip_all='skipping remote-testgit tests, bash not available' + test_done +fi + +compare_refs() { + git --git-dir="$1/.git" rev-parse --verify $2 >expect && + git --git-dir="$3/.git" rev-parse --verify $4 >actual && + test_cmp expect actual +} + +test_expect_success 'setup repository' ' + git init server && + (cd server && + echo content >file && + git add file && + git commit -m one) +' + +test_expect_success 'cloning from local repo' ' + git clone "testgit::${PWD}/server" local && + test_cmp server/file local/file +' + +test_expect_success 'create new commit on remote' ' + (cd server && + echo content >>file && + git commit -a -m two) +' + +test_expect_success 'pulling from local repo' ' + (cd local && git pull) && + test_cmp server/file local/file +' + +test_expect_success 'pushing to local repo' ' + (cd local && + echo content >>file && + git commit -a -m three && + git push) && + compare_refs local HEAD server HEAD +' + +test_expect_success 'fetch new branch' ' + (cd server && + git reset --hard && + git checkout -b new && + echo content >>file && + git commit -a -m five + ) && + (cd local && + git fetch origin new + ) && + compare_refs server HEAD local FETCH_HEAD +' + +test_expect_success 'fetch multiple branches' ' + (cd local && + git fetch + ) && + compare_refs server master local refs/remotes/origin/master && + compare_refs server new local refs/remotes/origin/new +' + +test_expect_success 'push when remote has extra refs' ' + (cd local && + git reset --hard origin/master && + echo content >>file && + git commit -a -m six && + git push + ) && + compare_refs local master server master +' + +test_expect_success 'push new branch by name' ' + (cd local && + git checkout -b new-name && + echo content >>file && + git commit -a -m seven && + git push origin new-name + ) && + compare_refs local HEAD server refs/heads/new-name +' + +test_expect_failure 'push new branch with old:new refspec' ' + (cd local && + git push origin new-name:new-refspec + ) && + compare_refs local HEAD server refs/heads/new-refspec +' + +test_expect_success 'cloning without refspec' ' + GIT_REMOTE_TESTGIT_REFSPEC="" \ + git clone "testgit::${PWD}/server" local2 && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling without refspecs' ' + (cd local2 && + git reset --hard && + GIT_REMOTE_TESTGIT_REFSPEC="" git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing without refspecs' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m ten && + GIT_REMOTE_TESTGIT_REFSPEC="" git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling with straight refspec' ' + (cd local2 && + GIT_REMOTE_TESTGIT_REFSPEC="*:*" git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing with straight refspec' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m eleven && + GIT_REMOTE_TESTGIT_REFSPEC="*:*" git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling without marks' ' + (cd local2 && + GIT_REMOTE_TESTGIT_NO_MARKS=1 git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing without marks' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m twelve && + GIT_REMOTE_TESTGIT_NO_MARKS=1 git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'push all with existing object' ' + (cd local && + git branch dup2 master && + git push origin --all + ) && + compare_refs local dup2 server dup2 +' + +test_expect_success 'push ref with existing object' ' + (cd local && + git branch dup master && + git push origin dup + ) && + compare_refs local dup server dup +' + +test_done diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index f94f0c48e6..3fc3b74c8e 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -3,6 +3,7 @@ test_description='git rev-list --pretty=format test' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-terminal.sh test_tick test_expect_success 'setup' ' @@ -11,12 +12,24 @@ touch foo && git add foo && git commit -m "added foo" && ' # usage: test_format name format_string expect.$1 test_expect_success "format $1" " -git rev-list --pretty=format:'$2' master >output.$1 && -test_cmp expect.$1 output.$1 -" + git rev-list --pretty=format:'$2' master >output.$1 && + test_cmp expect.$1 output.$1 + " +} + +# Feed to --format to provide predictable colored sequences. +AUTO_COLOR='%C(auto,red)foo%C(auto,reset)' +has_color () { + printf '\033[31mfoo\033[m\n' >expect && + test_cmp expect "$1" +} + +has_no_color () { + echo foo >expect && + test_cmp expect "$1" } test_format percent %%h <<'EOF' @@ -124,6 +137,48 @@ commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 foo EOF +test_expect_success '%C(auto) does not enable color by default' ' + git log --format=$AUTO_COLOR -1 >actual && + has_no_color actual +' + +test_expect_success '%C(auto) enables colors for color.diff' ' + git -c color.diff=always log --format=$AUTO_COLOR -1 >actual && + has_color actual +' + +test_expect_success '%C(auto) enables colors for color.ui' ' + git -c color.ui=always log --format=$AUTO_COLOR -1 >actual && + has_color actual +' + +test_expect_success '%C(auto) respects --color' ' + git log --format=$AUTO_COLOR -1 --color >actual && + has_color actual +' + +test_expect_success '%C(auto) respects --no-color' ' + git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual && + has_no_color actual +' + +test_expect_success TTY '%C(auto) respects --color=auto (stdout is tty)' ' + ( + TERM=vt100 && export TERM && + test_terminal \ + git log --format=$AUTO_COLOR -1 --color=auto >actual && + has_color actual + ) +' + +test_expect_success '%C(auto) respects --color=auto (stdout not tty)' ' + ( + TERM=vt100 && export TERM && + git log --format=$AUTO_COLOR -1 --color=auto >actual && + has_no_color actual + ) +' + cat >commit-msg <<'EOF' Test printing of complex bodies diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index de7d45352e..2683cba7e3 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -133,6 +133,7 @@ test_expect_success 'submodule add --branch' ' ( cd addtest && git submodule add -b initial "$submodurl" submod-branch && + test "initial" = "$(git config -f .gitmodules submodule.submod-branch.branch)" && git submodule init ) && diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index feaec6cdf4..4975ec07ce 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -135,6 +135,37 @@ test_expect_success 'submodule update --force forcibly checks out submodules' ' ) ' +test_expect_success 'submodule update --remote should fetch upstream changes' ' + (cd submodule && + echo line4 >> file && + git add file && + test_tick && + git commit -m "upstream line4" + ) && + (cd super && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline)" + ) +' + +test_expect_success 'local config should override .gitmodules branch' ' + (cd submodule && + git checkout -b test-branch && + echo line5 >> file && + git add file && + test_tick && + git commit -m "upstream line5" && + git checkout master + ) && + (cd super && + git config submodule.submodule.branch test-branch && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline test-branch)" + ) +' + test_expect_success 'submodule update --rebase staying on master' ' (cd super/submodule && git checkout master diff --git a/t/t9020-remote-svn.sh b/t/t9020-remote-svn.sh index 4f2dfe0e3d..d7be66a1d6 100755 --- a/t/t9020-remote-svn.sh +++ b/t/t9020-remote-svn.sh @@ -12,9 +12,13 @@ then test_done fi -# We override svnrdump by placing a symlink to the svnrdump-emulator in . -export PATH="$HOME:$PATH" -ln -sf $GIT_BUILD_DIR/contrib/svn-fe/svnrdump_sim.py "$HOME/svnrdump" +# Override svnrdump with our simulator +PATH="$HOME:$PATH" +export PATH PYTHON_PATH GIT_BUILD_DIR + +write_script "$HOME/svnrdump" <<\EOF +exec "$PYTHON_PATH" "$GIT_BUILD_DIR/contrib/svn-fe/svnrdump_sim.py" "$@" +EOF init_git () { rm -fr .git && diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 69934b2e77..3fb3368903 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -25,8 +25,9 @@ GIT_DIR=$PWD/.git export CVSROOT CVSWORK GIT_DIR rm -rf "$CVSROOT" "$CVSWORK" -mkdir "$CVSROOT" && + cvs init && +test -d "$CVSROOT" && cvs -Q co -d "$CVSWORK" . && echo >empty && git add empty && diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 3e821f958b..9320b4f94c 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -303,7 +303,7 @@ test_expect_success 'dropping tag of filtered out object' ' ( cd limit-by-paths && git fast-export --tag-of-filtered-object=drop mytag -- there > output && - test_cmp output expected + test_cmp expected output ) ' @@ -320,7 +320,7 @@ test_expect_success 'rewriting tag of filtered out object' ' ( cd limit-by-paths && git fast-export --tag-of-filtered-object=rewrite mytag -- there > output && - test_cmp output expected + test_cmp expected output ) ' @@ -351,7 +351,7 @@ test_expect_failure 'no exact-ref revisions included' ' ( cd limit-by-paths && git fast-export master~2..master~1 > output && - test_cmp output expected + test_cmp expected output ) ' @@ -440,4 +440,63 @@ test_expect_success 'fast-export quotes pathnames' ' ) ' +test_expect_success 'test bidirectionality' ' + >marks-cur && + >marks-new && + git init marks-test && + git fast-export --export-marks=marks-cur --import-marks=marks-cur --branches | \ + git --git-dir=marks-test/.git fast-import --export-marks=marks-new --import-marks=marks-new && + (cd marks-test && + git reset --hard && + echo Wohlauf > file && + git commit -a -m "back in time") && + git --git-dir=marks-test/.git fast-export --export-marks=marks-new --import-marks=marks-new --branches | \ + git fast-import --export-marks=marks-cur --import-marks=marks-cur +' + +cat > expected << EOF +blob +mark :13 +data 5 +bump + +commit refs/heads/master +mark :14 +author A U Thor 1112912773 -0700 +committer C O Mitter 1112912773 -0700 +data 5 +bump +from :12 +M 100644 :13 file + +EOF + +test_expect_success 'avoid uninteresting refs' ' + > tmp-marks && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > /dev/null && + git tag v1.0 && + git branch uninteresting && + echo bump > file && + git commit -a -m bump && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks ^uninteresting ^v1.0 master > actual && + test_cmp expected actual +' + +cat > expected << EOF +reset refs/heads/master +from :14 + +EOF + +test_expect_success 'refs are updated even if no commits need to be exported' ' + > tmp-marks && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > /dev/null && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > actual && + test_cmp expected actual +' + test_done diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh index 3a8e7d3f5a..86dfee2e4f 100755 --- a/t/t9502-gitweb-standalone-parse-output.sh +++ b/t/t9502-gitweb-standalone-parse-output.sh @@ -40,7 +40,7 @@ check_snapshot () { echo "basename=$basename" grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 && "$TAR" tf gitweb.body >file_list && - ! grep -v "^$prefix/" file_list + ! grep -v -e "^$prefix$" -e "^$prefix/" -e "^pax_global_header$" file_list } test_expect_success setup ' diff --git a/t/test-lib.sh b/t/test-lib.sh index f50f8341d4..8a12cbb86a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -212,11 +212,13 @@ then error) tput bold; tput setaf 1;; # bold red skip) - tput bold; tput setaf 2;; # bold green + tput setaf 4;; # blue + warn) + tput setaf 3;; # brown/yellow pass) - tput setaf 2;; # green + tput setaf 2;; # green info) - tput setaf 3;; # brown + tput setaf 6;; # cyan *) test -n "$quiet" && return;; esac @@ -298,7 +300,7 @@ test_ok_ () { test_failure_ () { test_failure=$(($test_failure + 1)) - say_color error "not ok - $test_count $1" + say_color error "not ok $test_count - $1" shift echo "$@" | sed -e 's/^/# /' test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } @@ -306,12 +308,12 @@ test_failure_ () { test_known_broken_ok_ () { test_fixed=$(($test_fixed+1)) - say_color "" "ok $test_count - $@ # TODO known breakage" + say_color error "ok $test_count - $@ # TODO known breakage vanished" } test_known_broken_failure_ () { test_broken=$(($test_broken+1)) - say_color skip "not ok $test_count - $@ # TODO known breakage" + say_color warn "not ok $test_count - $@ # TODO known breakage" } test_debug () { @@ -404,13 +406,18 @@ test_done () { if test "$test_fixed" != 0 then - say_color pass "# fixed $test_fixed known breakage(s)" + say_color error "# $test_fixed known breakage(s) vanished; please update test(s)" fi if test "$test_broken" != 0 then - say_color error "# still have $test_broken known breakage(s)" - msg="remaining $(($test_count-$test_broken)) test(s)" + say_color warn "# still have $test_broken known breakage(s)" + fi + if test "$test_broken" != 0 || test "$test_fixed" != 0 + then + test_remaining=$(( $test_count - $test_broken - $test_fixed )) + msg="remaining $test_remaining test(s)" else + test_remaining=$test_count msg="$test_count test(s)" fi case "$test_failure" in @@ -424,7 +431,7 @@ test_done () { if test $test_external_has_tap -eq 0 then - if test $test_count -gt 0 + if test $test_remaining -gt 0 then say_color pass "# passed all $msg" fi @@ -615,7 +622,7 @@ for skp in $GIT_SKIP_TESTS do case "$this_test" in $skp) - say_color skip >&3 "skipping test $this_test altogether" + say_color info >&3 "skipping test $this_test altogether" skip_all="skip all tests in $this_test" test_done esac diff --git a/test-path-utils.c b/test-path-utils.c index 3bc20e91da..0092cbf354 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -1,4 +1,32 @@ #include "cache.h" +#include "string-list.h" + +/* + * A "string_list_each_func_t" function that normalizes an entry from + * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason, + * die with an explanation. + */ +static int normalize_ceiling_entry(struct string_list_item *item, void *unused) +{ + const char *ceil = item->string; + int len = strlen(ceil); + char buf[PATH_MAX+1]; + + if (len == 0) + die("Empty path is not supported"); + if (len > PATH_MAX) + die("Path \"%s\" is too long", ceil); + if (!is_absolute_path(ceil)) + die("Path \"%s\" is not absolute", ceil); + if (normalize_path_copy(buf, ceil) < 0) + die("Path \"%s\" could not be normalized", ceil); + len = strlen(buf); + if (len > 1 && buf[len-1] == '/') + die("Normalized path \"%s\" ended with slash", buf); + free(item->string); + item->string = xstrdup(buf); + return 1; +} int main(int argc, char **argv) { @@ -30,7 +58,28 @@ int main(int argc, char **argv) } if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { - int len = longest_ancestor_length(argv[2], argv[3]); + int len; + struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; + char *path = xstrdup(argv[2]); + + /* + * We have to normalize the arguments because under + * Windows, bash mangles arguments that look like + * absolute POSIX paths or colon-separate lists of + * absolute POSIX paths into DOS paths (e.g., + * "/foo:/foo/bar" might be converted to + * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"), + * whereas longest_ancestor_length() requires paths + * that use forward slashes. + */ + if (normalize_path_copy(path, path)) + die("Path \"%s\" could not be normalized", argv[2]); + string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1); + filter_string_list(&ceiling_dirs, 0, + normalize_ceiling_entry, NULL); + len = longest_ancestor_length(path, &ceiling_dirs); + string_list_clear(&ceiling_dirs, 0); + free(path); printf("%d\n", len); return 0; } diff --git a/test-string-list.c b/test-string-list.c index 4693295a98..00ce6c9a12 100644 --- a/test-string-list.c +++ b/test-string-list.c @@ -97,26 +97,6 @@ int main(int argc, char **argv) return 0; } - if (argc == 4 && !strcmp(argv[1], "longest_prefix")) { - /* arguments: |- */ - struct string_list prefixes = STRING_LIST_INIT_DUP; - int retval; - const char *prefix_string = argv[2]; - const char *string = argv[3]; - const char *match; - - parse_string_list(&prefixes, prefix_string); - match = string_list_longest_prefix(&prefixes, string); - if (match) { - printf("%s\n", match); - retval = 0; - } - else - retval = 1; - string_list_clear(&prefixes, 0); - return retval; - } - fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/transport-helper.c b/transport-helper.c index 4713b69302..965b778cb3 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -661,6 +661,11 @@ static void push_update_ref_status(struct strbuf *buf, free(msg); msg = NULL; } + else if (!strcmp(msg, "already exists")) { + status = REF_STATUS_REJECT_ALREADY_EXISTS; + free(msg); + msg = NULL; + } } if (*ref) @@ -720,6 +725,7 @@ static int push_refs_with_push(struct transport *transport, /* Check for statuses set by set_ref_status_for_push() */ switch (ref->status) { case REF_STATUS_REJECT_NONFASTFORWARD: + case REF_STATUS_REJECT_ALREADY_EXISTS: case REF_STATUS_UPTODATE: continue; default: diff --git a/transport.c b/transport.c index 9932f402df..2673d273ff 100644 --- a/transport.c +++ b/transport.c @@ -659,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain) const char *msg; strcpy(quickref, status_abbrev(ref->old_sha1)); - if (ref->nonfastforward) { + if (ref->requires_force) { strcat(quickref, "..."); type = '+'; msg = "forced update"; @@ -695,6 +695,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast-forward", porcelain); break; + case REF_STATUS_REJECT_ALREADY_EXISTS: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "already exists", porcelain); + break; case REF_STATUS_REMOTE_REJECT: print_ref_status('!', "[remote rejected]", ref, ref->deletion ? NULL : ref->peer_ref, @@ -714,7 +718,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i } void transport_print_push_status(const char *dest, struct ref *refs, - int verbose, int porcelain, int *nonfastforward) + int verbose, int porcelain, unsigned int *reject_reasons) { struct ref *ref; int n = 0; @@ -733,18 +737,19 @@ void transport_print_push_status(const char *dest, struct ref *refs, if (ref->status == REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); - *nonfastforward = 0; + *reject_reasons = 0; for (ref = refs; ref; ref = ref->next) { if (ref->status != REF_STATUS_NONE && ref->status != REF_STATUS_UPTODATE && ref->status != REF_STATUS_OK) n += print_one_push_status(ref, dest, n, porcelain); - if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD && - *nonfastforward != NON_FF_HEAD) { + if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) { if (!strcmp(head, ref->name)) - *nonfastforward = NON_FF_HEAD; + *reject_reasons |= REJECT_NON_FF_HEAD; else - *nonfastforward = NON_FF_OTHER; + *reject_reasons |= REJECT_NON_FF_OTHER; + } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) { + *reject_reasons |= REJECT_ALREADY_EXISTS; } } } @@ -1031,9 +1036,9 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing) int transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags, - int *nonfastforward) + unsigned int *reject_reasons) { - *nonfastforward = 0; + *reject_reasons = 0; transport_verify_remote_names(refspec_nr, refspec); if (transport->push) { @@ -1099,7 +1104,7 @@ int transport_push(struct transport *transport, if (!quiet || err) transport_print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, - nonfastforward); + reject_reasons); if (flags & TRANSPORT_PUSH_SET_UPSTREAM) set_upstreams(transport, remote_refs, pretend); diff --git a/transport.h b/transport.h index 4a61c0c3f2..bfd2df5823 100644 --- a/transport.h +++ b/transport.h @@ -140,11 +140,13 @@ int transport_set_option(struct transport *transport, const char *name, void transport_set_verbosity(struct transport *transport, int verbosity, int force_progress); -#define NON_FF_HEAD 1 -#define NON_FF_OTHER 2 +#define REJECT_NON_FF_HEAD 0x01 +#define REJECT_NON_FF_OTHER 0x02 +#define REJECT_ALREADY_EXISTS 0x04 + int transport_push(struct transport *connection, int refspec_nr, const char **refspec, int flags, - int * nonfastforward); + unsigned int * reject_reasons); const struct ref *transport_get_remote_refs(struct transport *transport); @@ -170,7 +172,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v int transport_refs_pushed(struct ref *ref); void transport_print_push_status(const char *dest, struct ref *refs, - int verbose, int porcelain, int *nonfastforward); + int verbose, int porcelain, unsigned int *reject_reasons); typedef void alternate_ref_fn(const struct ref *, void *); extern void for_each_alternate_ref(alternate_ref_fn, void *); diff --git a/tree-walk.c b/tree-walk.c index 3f54c02d76..6e30ef9d04 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -572,6 +572,54 @@ static int match_dir_prefix(const char *base, return 0; } +/* + * Perform matching on the leading non-wildcard part of + * pathspec. item->nowildcard_len must be greater than zero. Return + * non-zero if base is matched. + */ +static int match_wildcard_base(const struct pathspec_item *item, + const char *base, int baselen, + int *matched) +{ + const char *match = item->match; + /* the wildcard part is not considered in this function */ + int matchlen = item->nowildcard_len; + + if (baselen) { + int dirlen; + /* + * Return early if base is longer than the + * non-wildcard part but it does not match. + */ + if (baselen >= matchlen) { + *matched = matchlen; + return !strncmp(base, match, matchlen); + } + + dirlen = matchlen; + while (dirlen && match[dirlen - 1] != '/') + dirlen--; + + /* + * Return early if base is shorter than the + * non-wildcard part but it does not match. Note that + * base ends with '/' so we are sure it really matches + * directory + */ + if (strncmp(base, match, baselen)) + return 0; + *matched = baselen; + } else + *matched = 0; + /* + * we could have checked entry against the non-wildcard part + * that is not in base and does similar never_interesting + * optimization as in match_entry. For now just be happy with + * base comparison. + */ + return entry_interesting; +} + /* * Is a tree entry interesting given the pathspec we have? * @@ -602,7 +650,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, const struct pathspec_item *item = ps->items+i; const char *match = item->match; const char *base_str = base->buf + base_offset; - int matchlen = item->len; + int matchlen = item->len, matched = 0; if (baselen >= matchlen) { /* If it doesn't match, move along... */ @@ -626,8 +674,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, &never_interesting)) return entry_interesting; - if (item->use_wildcard) { - if (!fnmatch(match + baselen, entry->path, 0)) + if (item->nowildcard_len < item->len) { + if (!git_fnmatch(match + baselen, entry->path, + item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + item->nowildcard_len - baselen)) return entry_interesting; /* @@ -642,17 +692,34 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, } match_wildcards: - if (!item->use_wildcard) + if (item->nowildcard_len == item->len) continue; + if (item->nowildcard_len && + !match_wildcard_base(item, base_str, baselen, &matched)) + return entry_not_interesting; + /* * Concatenate base and entry->path into one and do * fnmatch() on it. + * + * While we could avoid concatenation in certain cases + * [1], which saves a memcpy and potentially a + * realloc, it turns out not worth it. Measurement on + * linux-2.6 does not show any clear improvements, + * partly because of the nowildcard_len optimization + * in git_fnmatch(). Avoid micro-optimizations here. + * + * [1] if match_wildcard_base() says the base + * directory is already matched, we only need to match + * the rest, which is shorter so _in theory_ faster. */ strbuf_add(base, entry->path, pathlen); - if (!fnmatch(match, base->buf + base_offset, 0)) { + if (!git_fnmatch(match, base->buf + base_offset, + item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, + item->nowildcard_len)) { strbuf_setlen(base, base_offset + baselen); return entry_interesting; } diff --git a/unpack-trees.c b/unpack-trees.c index 6d9636623a..61acc5e564 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1834,7 +1834,7 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o) if (old && same(old, a)) { int update = 0; - if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) { + if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) { struct stat st; if (lstat(old->name, &st) || ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE)) diff --git a/usage.c b/usage.c index 8eab28113a..40b3de51c7 100644 --- a/usage.c +++ b/usage.c @@ -130,6 +130,7 @@ void NORETURN die_errno(const char *fmt, ...) va_end(params); } +#undef error int error(const char *err, ...) { va_list params; diff --git a/utf8.c b/utf8.c index 5c61bbe113..a4ee6650ef 100644 --- a/utf8.c +++ b/utf8.c @@ -323,7 +323,7 @@ static size_t display_mode_esc_sequence_len(const char *s) * If indent is negative, assume that already -indent columns have been * consumed (and no extra indent is necessary for the first line). */ -int strbuf_add_wrapped_text(struct strbuf *buf, +void strbuf_add_wrapped_text(struct strbuf *buf, const char *text, int indent1, int indent2, int width) { int indent, w, assume_utf8 = 1; @@ -332,7 +332,7 @@ int strbuf_add_wrapped_text(struct strbuf *buf, if (width <= 0) { strbuf_add_indented_text(buf, text, indent1, indent2); - return 1; + return; } retry: @@ -356,14 +356,14 @@ int strbuf_add_wrapped_text(struct strbuf *buf, if (w <= width || !space) { const char *start = bol; if (!c && text == start) - return w; + return; if (space) start = space; else strbuf_addchars(buf, ' ', indent); strbuf_add(buf, start, text - start); if (!c) - return w; + return; space = text; if (c == '\t') w |= 0x07; @@ -405,13 +405,12 @@ int strbuf_add_wrapped_text(struct strbuf *buf, } } -int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len, +void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len, int indent, int indent2, int width) { char *tmp = xstrndup(data, len); - int r = strbuf_add_wrapped_text(buf, tmp, indent, indent2, width); + strbuf_add_wrapped_text(buf, tmp, indent, indent2, width); free(tmp); - return r; } int is_encoding_utf8(const char *name) diff --git a/utf8.h b/utf8.h index 93ef60042c..a214238bdd 100644 --- a/utf8.h +++ b/utf8.h @@ -9,9 +9,9 @@ int is_utf8(const char *text); int is_encoding_utf8(const char *name); int same_encoding(const char *, const char *); -int strbuf_add_wrapped_text(struct strbuf *buf, +void strbuf_add_wrapped_text(struct strbuf *buf, const char *text, int indent, int indent2, int width); -int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len, +void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len, int indent, int indent2, int width); #ifndef NO_ICONV diff --git a/wrapper.c b/wrapper.c index 68739aaa3b..bac59d2c41 100644 --- a/wrapper.c +++ b/wrapper.c @@ -229,7 +229,7 @@ int xmkstemp(char *template) int saved_errno = errno; const char *nonrelative_template; - if (!template[0]) + if (strlen(template) != strlen(origtemplate)) template = origtemplate; nonrelative_template = absolute_path(template); @@ -411,11 +411,19 @@ void warn_on_inaccessible(const char *path) int access_or_warn(const char *path, int mode) { int ret = access(path, mode); - if (ret && errno != ENOENT) + if (ret && errno != ENOENT && errno != ENOTDIR) warn_on_inaccessible(path); return ret; } +int access_or_die(const char *path, int mode) +{ + int ret = access(path, mode); + if (ret && errno != ENOENT && errno != ENOTDIR) + die_errno(_("unable to access '%s'"), path); + return ret; +} + struct passwd *xgetpwuid_self(void) { struct passwd *pw;