diff options
142 files changed, 3175 insertions, 901 deletions
diff --git a/.gitignore b/.gitignore index d9adce585a..1c57d4c958 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,7 @@ git-core-*/?* gitk-wish gitweb/gitweb.cgi test-chmtime +test-ctype test-date test-delta test-dump-cache-tree @@ -152,6 +153,7 @@ test-match-trees test-parse-options test-path-utils test-sha1 +test-sigchain common-cmds.h *.tar.gz *.dsc diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index f628c1f3b7..0d7fa9cca9 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -21,8 +21,13 @@ code. For git in general, three rough rules are: As for more concrete guidelines, just imitate the existing code (this is a good guideline, no matter which project you are -contributing to). But if you must have a list of rules, -here they are. +contributing to). It is always preferable to match the _local_ +convention. New code added to git suite is expected to match +the overall style of existing code. Modifications to existing +code is expected to match the style the surrounding code already +uses (even if it doesn't match the overall style of existing code). + +But if you must have a list of rules, here they are. For shell scripts specifically (not exhaustive): diff --git a/Documentation/RelNotes-1.6.1.1.txt b/Documentation/RelNotes-1.6.1.1.txt index 5cd1ca9cc6..88454c1973 100644 --- a/Documentation/RelNotes-1.6.1.1.txt +++ b/Documentation/RelNotes-1.6.1.1.txt @@ -4,9 +4,14 @@ GIT v1.6.1.1 Release Notes Fixes since v1.6.1 ------------------ +* "git add frotz/nitfol" when "frotz" is a submodule should have errored + out, but it didn't. + * "git apply" took file modes from the patch text and updated the mode bits of the target tree even when the patch was not about mode changes. +* "git bisect view" on Cygwin did not launch gitk + * "git checkout $tree" did not trigger an error. * "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake. @@ -14,6 +19,12 @@ Fixes since v1.6.1 * "git describe --all" complained when a commit is described with a tag, which was nonsense. +* "git diff --no-index --" did not trigger no-index (aka "use git-diff as + a replacement of diff on untracked files") behaviour. + +* "git format-patch -1 HEAD" on a root commit failed to produce patch + text. + * "git fsck branch" did not work as advertised; instead it behaved the same way as "git fsck". @@ -36,14 +47,13 @@ Fixes since v1.6.1 * "git mv -k" with more than one errorneous paths misbehaved. +* "git read-tree -m -u" hence branch switching incorrectly lost a + subdirectory in rare cases. + * "git rebase -i" issued an unnecessary error message upon a user error of marking the first commit to be "squash"ed. -Other documentation updates. - ---- -exec >/var/tmp/1 -O=v1.6.1-60-g78f111e -echo O=$(git describe maint) -git shortlog --no-merges $O..maint +* "git shortlog" did not format a commit message with multi-line + subject correctly. +Many documentation updates. diff --git a/Documentation/RelNotes-1.6.1.2.txt b/Documentation/RelNotes-1.6.1.2.txt new file mode 100644 index 0000000000..230aa3d8e8 --- /dev/null +++ b/Documentation/RelNotes-1.6.1.2.txt @@ -0,0 +1,39 @@ +GIT v1.6.1.2 Release Notes +========================== + +Fixes since v1.6.1.1 +-------------------- + +* The logic for rename detectin in internal diff used by commands like + "git diff" and "git blame" have been optimized to avoid loading the same + blob repeatedly. + +* We did not allow writing out a blob that is larger than 2GB for no good + reason. + +* "git format-patch -o $dir", when $dir is a relative directory, used it + as relative to the root of the work tree, not relative to the current + directory. + +* v1.6.1 introduced an optimization for "git push" into a repository (A) + that borrows its objects from another repository (B) to avoid sending + objects that are available in repository B, when they are not yet used + by repository A. However the code on the "git push" sender side was + buggy and did not work when repository B had new objects that are not + known by the sender. This caused pushing into a "forked" repository + served by v1.6.1 software using "git push" from v1.6.1 sometimes did not + work. The bug was purely on the "git push" sender side, and has been + corrected. + +* "git status -v" did not paint its diff output in colour even when + color.ui configuration was set. + +* "git ls-tree" learned --full-tree option to help Porcelain scripts that + want to always see the full path regardless of the current working + directory. + +* "git grep" incorrectly searched in work tree paths even when they are + marked as assume-unchanged. It now searches in the index entries. + +* "git gc" with no grace period needlessly ejected packed but unreachable + objects in their loose form, only to delete them right away. diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt index 296804301f..3151c85d88 100644 --- a/Documentation/RelNotes-1.6.2.txt +++ b/Documentation/RelNotes-1.6.2.txt @@ -6,6 +6,11 @@ Updates since v1.6.1 (subsystems) +* git-svn updates. + +* gitweb updates, including a new patch view and RSS/Atom feed + improvements. + (portability) (performance) @@ -15,25 +20,63 @@ Updates since v1.6.1 (usability, bells and whistles) -* "git-add -p" learned 'g'oto action to jump directly to a hunk. +* automatic typo correction works on aliases as well + +* @{-1} is a way to refer to the last branch you were on. This is + accepted not only where an object name is expected, but anywhere + a branch name is expected. E.g. "git branch --track mybranch @{-1}" + "git rev-parse --symbolic-full-name @{-1}". + +* "git add -p" learned 'g'oto action to jump directly to a hunk. + +* when "git am" stops upon a patch that does not apply, it shows the + title of the offending patch. + +* "git am --directory=<dir>" and "git am --reject" passes these options + to underlying "git apply". + +* "git clone" now makes its best effort when cloning from an empty + repository to set up configuration variables to refer to the remote + repository. + +* "git checkout -" is a shorthand for "git checkout @{-1}". -* git-cherry defaults to HEAD when the <upstream> argument is not given. +* "git cherry" defaults to whatever the current branch is tracking (if + exists) when the <upstream> argument is not given. -* git-cvsserver can be told not to add extra "via git-CVS emulator" to the - commit log message it serves via gitcvs.commitmsgannotation configuration. +* "git cvsserver" can be told not to add extra "via git-CVS emulator" to + the commit log message it serves via gitcvs.commitmsgannotation + configuration. -* git-diff learned a new option --inter-hunk-context to coalesce close +* "git diff" learned a new option --inter-hunk-context to coalesce close hunks together and show context between them. -* git-filter-branch learned --prune-empty option that discards commits +* The definition of what constitutes a word for "git diff --color-words" + can be customized via gitattributes, command line or a configuration. + +* "git diff" learned --patience to run "patience diff" algorithm. + +* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did + not work as expected. + +* "git filter-branch" learned --prune-empty option that discards commits that do not change the contents. -* git-ls-tree learned --full-tree option that shows the path in full +* "git grep -w" and "git grep" for fixed strings have been optimized. + +* "git log" and friends include HEAD to the set of starting points + when --all is given. This makes a difference when you are not on + any branch. + +* "git ls-tree" learned --full-tree option that shows the path in full regardless of where in the work tree hierarchy the command was started. -* git-mergetool learned -y(--no-prompt) option to disable prompting. +* "git mergetool" learned -y(--no-prompt) option to disable prompting. + +* "git rebase -i" can transplant a history down to root to elsewhere + with --root option. -* "git-reset --merge" is a new mode that works similar to the way +* "git reset --merge" is a new mode that works similar to the way "git checkout" switches branches, taking the local changes while switching to another commit. @@ -52,14 +95,12 @@ release, unless otherwise noted. * git-bundle did not exclude annotated tags even when a range given from the command line wanted to. -* git-grep did not work correctly for index entries with assume-unchanged bit. - * branch switching and merges had a silly bug that did not validate the correct directory when making sure an existing subdirectory is clean. -- exec >/var/tmp/1 -O=v1.6.1-134-ge98c6a1 +O=v1.6.1.2-252-g8c95d3c echo O=$(git describe master) git shortlog --no-merges $O..master ^maint diff --git a/Documentation/config.txt b/Documentation/config.txt index 290cb48eb9..e2b8775dd3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -639,6 +639,12 @@ diff.suppressBlankEmpty:: A boolean to inhibit the standard behavior of printing a space before each empty output line. Defaults to false. +diff.wordRegex:: + A POSIX Extended Regular Expression used to determine what is a "word" + when performing word-by-word difference calculations. Character + sequences that match the regular expression are "words", all other + characters are *ignorable* whitespace. + fetch.unpackLimit:: If the number of objects fetched over the git native transfer is below this diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 1f8ce979bb..813a7b11b9 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -36,7 +36,7 @@ endif::git-format-patch[] --patch-with-raw:: Synonym for "-p --raw". ---patience: +--patience:: Generate a diff using the "patience diff" algorithm. --stat[=width[,name-width]]:: @@ -94,8 +94,22 @@ endif::git-format-patch[] Turn off colored diff, even when the configuration file gives the default to color output. ---color-words:: - Show colored word diff, i.e. color words which have changed. +--color-words[=<regex>]:: + Show colored word diff, i.e., color words which have changed. + By default, words are separated by whitespace. ++ +When a <regex> is specified, every non-overlapping match of the +<regex> is considered a word. Anything between these matches is +considered whitespace and ignored(!) for the purposes of finding +differences. You may want to append `|[^[:space:]]` to your regular +expression to make sure that it matches all non-whitespace characters. +A match that contains a newline is silently truncated(!) at the +newline. ++ +The regex can also be set via a diff driver or configuration option, see +linkgit:gitattributes[1] or linkgit:git-config[1]. Giving it explicitly +overrides any diff driver or configuration setting. Diff drivers +override configuration settings. --no-renames:: Turn off rename detection, even when the configuration diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 9cd51514db..3bccffae62 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -133,6 +133,10 @@ the conflicted merge in the specified paths. + When this parameter names a non-branch (but still a valid commit object), your HEAD becomes 'detached'. ++ +As a special case, the "`@\{-N\}`" syntax for the N-th last branch +checks out the branch (instead of detaching). You may also specify +"`-`" which is synonymous with "`@\{-1\}`". Detached HEAD diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 7b27dc60bd..7d1eced7d2 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -33,25 +33,27 @@ OPTIONS of a remote (see the section <<REMOTES,REMOTES>> below). <refspec>...:: - The canonical format of a <refspec> parameter is - `+?<src>:<dst>`; that is, an optional plus `{plus}`, followed - by the source ref, followed by a colon `:`, followed by - the destination ref. + The format of a <refspec> parameter is an optional plus + `{plus}`, followed by the source ref <src>, followed + by a colon `:`, followed by the destination ref <dst>. + It is used to specify with what <src> object the <dst> ref + in the remote repository is to be updated. + -The <src> side represents the source branch (or arbitrary -"SHA1 expression", such as `master~4` (four parents before the -tip of `master` branch); see linkgit:git-rev-parse[1]) that you -want to push. The <dst> side represents the destination location. +The <src> is often the name of the branch you would want to push, but +it can be any arbitrary "SHA-1 expression", such as `master~4` or +`HEAD` (see linkgit:git-rev-parse[1]). + -The local ref that matches <src> is used -to fast forward the remote ref that matches <dst>. If -the optional leading plus `+` is used, the remote ref is updated -even if it does not result in a fast forward update. +The <dst> tells which ref on the remote side is updated with this +push. Arbitrary expressions cannot be used here, an actual ref must +be named. If `:`<dst> is omitted, the same ref as <src> will be +updated. + -`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`. +The object referenced by <src> is used to fast forward the ref <dst> +on the remote side. If the optional leading plus `{plus}` is used, the +remote ref is updated even if it does not result in a fast forward +update. + -A lonely <src> parameter (without a colon and a destination) pushes -the <src> to the same name in the destination repository. +`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`. + Pushing an empty <src> allows you to delete the <dst> ref from the remote repository. @@ -190,9 +192,9 @@ git push origin master:: with it. If `master` did not exist remotely, it would be created. -git push origin :experimental:: - Find a ref that matches `experimental` in the `origin` repository - (e.g. `refs/heads/experimental`), and delete it. +git push origin HEAD:: + A handy way to push the current branch to the same name on the + remote. git push origin master:satellite/master dev:satellite/dev:: Use the source ref that matches `master` (e.g. `refs/heads/master`) @@ -200,6 +202,11 @@ git push origin master:satellite/master dev:satellite/dev:: `refs/remotes/satellite/master`) in the `origin` repository, then do the same for `dev` and `satellite/dev`. +git push origin HEAD:master:: + Push the current branch to the remote ref matching `master` in the + `origin` repository. This form is convenient to push the current + branch without thinking about its local name. + git push origin master:refs/heads/experimental:: Create the branch `experimental` in the `origin` repository by copying the current `master` branch. This form is only @@ -207,6 +214,11 @@ git push origin master:refs/heads/experimental:: the local name and the remote name are different; otherwise, the ref name on its own will work. +git push origin :experimental:: + Find a ref that matches `experimental` in the `origin` repository + (e.g. `refs/heads/experimental`), and delete it. + + Author ------ Written by Junio C Hamano <gitster@pobox.com>, later rewritten in C diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 2921da320d..3ccef2f2b3 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -212,6 +212,9 @@ when you run 'git-merge'. reflog of the current branch. For example, if you are on the branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'. +* The special construct '@\{-<n>\}' means the <n>th branch checked out + before the current one. + * A suffix '{caret}' to a revision parameter means the first parent of that commit object. '{caret}<n>' means the <n>th parent (i.e. 'rev{caret}' diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 8f7c0e226d..498bd28929 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -82,7 +82,7 @@ her family name fully spelled out, a proper `.mailmap` file would look like: # Note how we don't need an entry for <jane@laptop.(none)>, because the # real name of that author is correct already, and coalesced directly. Jane Doe <jane@desktop.(none)> -Joe R. Developer <joe@random.com> +Joe R. Developer <joe@example.com> ------------ Author diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 63d2f5e962..7b654f7928 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -103,6 +103,19 @@ repository to be able to interoperate with someone else's local Git repository, either don't use this option or you should both use it in the same local timezone. +--ignore-paths=<regex>;; + This allows one to specify Perl regular expression that will + cause skipping of all matching paths from checkout from SVN. + Examples: + + --ignore-paths="^doc" - skip "doc*" directory for every fetch. + + --ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches" + and "tags" of first level directories. + + Regular expression is not persistent, you should specify + it every time when fetching. + 'clone':: Runs 'init' and 'fetch'. It will automatically create a directory based on the basename of the URL passed to it; diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index e44f543025..533d18bbd5 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <name> [<commit> | <object>] 'git tag' -d <name>... -'git tag' [-n[<num>]] -l [<pattern>] +'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>] 'git tag' -v <name>... DESCRIPTION @@ -68,6 +68,9 @@ OPTIONS List tags with names that match the given pattern (or all if no pattern is given). Typing "git tag" without arguments, also lists all tags. +--contains <commit>:: + Only list tags which contain the specified commit. + -m <msg>:: Use the given tag message (instead of prompting). If multiple `-m` options are given, their values are diff --git a/Documentation/git.txt b/Documentation/git.txt index 17dc8b2019..cd527c6252 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.6.1/git.html[documentation for release 1.6.1] +* link:v1.6.1.1/git.html[documentation for release 1.6.1.1] * release notes for + link:RelNotes-1.6.1.1.txt[1.6.1.1], link:RelNotes-1.6.1.txt[1.6.1]. * link:v1.6.0.6/git.html[documentation for release 1.6.0.6] diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 8af22eccac..227934f59a 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -317,6 +317,8 @@ patterns are available: - `bibtex` suitable for files with BibTeX coded references. +- `cpp` suitable for source code in the C and C++ languages. + - `html` suitable for HTML/XHTML documents. - `java` suitable for source code in the Java language. @@ -334,6 +336,25 @@ patterns are available: - `tex` suitable for source code for LaTeX documents. +Customizing word diff +^^^^^^^^^^^^^^^^^^^^^ + +You can customize the rules that `git diff --color-words` uses to +split words in a line, by specifying an appropriate regular expression +in the "diff.*.wordRegex" configuration variable. For example, in TeX +a backslash followed by a sequence of letters forms a command, but +several such commands can be run together without intervening +whitespace. To separate them, use a regular expression such as + +------------------------ +[diff "tex"] + wordRegex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+" +------------------------ + +A built-in pattern is provided for all languages listed in the +previous section. + + Performing text diffs of binary files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt index 458fafdb2c..c5d5596d89 100644 --- a/Documentation/gittutorial.txt +++ b/Documentation/gittutorial.txt @@ -308,9 +308,7 @@ alice$ git pull /home/bob/myrepo master This merges the changes from Bob's "master" branch into Alice's current branch. If Alice has made her own changes in the meantime, -then she may need to manually fix any conflicts. (Note that the -"master" argument in the above command is actually unnecessary, as it -is the default.) +then she may need to manually fix any conflicts. The "pull" command thus performs two operations: it fetches changes from a remote branch, then merges them into the current branch. diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt index ebdd948cd2..f9811f2473 100644 --- a/Documentation/pull-fetch-param.txt +++ b/Documentation/pull-fetch-param.txt @@ -5,15 +5,14 @@ of a remote (see the section <<REMOTES,REMOTES>> below). <refspec>:: - The canonical format of a <refspec> parameter is - `+?<src>:<dst>`; that is, an optional plus `{plus}`, followed - by the source ref, followed by a colon `:`, followed by - the destination ref. + The format of a <refspec> parameter is an optional plus + `{plus}`, followed by the source ref <src>, followed + by a colon `:`, followed by the destination ref <dst>. + The remote ref that matches <src> is fetched, and if <dst> is not empty string, the local ref that matches it is fast forwarded using <src>. -Again, if the optional plus `+` is used, the local ref +If the optional plus `+` is used, the local ref is updated even if it does not result in a fast forward update. + diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt index 9a4e3ea92c..ac56d1c477 100644 --- a/Documentation/technical/api-strbuf.txt +++ b/Documentation/technical/api-strbuf.txt @@ -21,7 +21,7 @@ allocated memory or not), use `strbuf_detach()` to unwrap a memory buffer from its strbuf shell in a safe way. That is the sole supported way. This will give you a malloced buffer that you can later `free()`. + -However, it it totally safe to modify anything in the string pointed by +However, it is totally safe to modify anything in the string pointed by the `buf` member, between the indices `0` and `len-1` (inclusive). . The `buf` member is a byte array that has at least `len + 1` bytes diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 19f571ae3b..96af8977f6 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1507,7 +1507,7 @@ so on a different branch and then coming back), unstash the work-in-progress changes. ------------------------------------------------ -$ git stash "work in progress for foo feature" +$ git stash save "work in progress for foo feature" ------------------------------------------------ This command will save your changes away to the `stash`, and diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6c7465c758..9c9fe640fb 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.0.2.GIT +DEF_VER=v1.6.1.GIT LF=' ' @@ -23,6 +23,9 @@ all:: # Define NO_EXPAT if you do not have expat installed. git-http-push is # not built, and you cannot push using http:// and https:// transports. # +# Define EXPATDIR=/foo/bar if your expat header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +# # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent. # # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks @@ -179,28 +182,32 @@ STRIP ?= strip # Among the variables below, these: # gitexecdir # template_dir +# mandir +# infodir # htmldir # ETC_GITCONFIG (but not sysconfdir) -# can be specified as a relative path ../some/where/else (which must begin -# with ../); this is interpreted as relative to $(bindir) and "git" at +# can be specified as a relative path some/where/else; +# this is interpreted as relative to $(prefix) and "git" at # runtime figures out where they are based on the path to the executable. # This can help installing the suite in a relocatable way. prefix = $(HOME) -bindir = $(prefix)/bin -mandir = $(prefix)/share/man -infodir = $(prefix)/share/info -gitexecdir = $(prefix)/libexec/git-core +bindir_relative = bin +bindir = $(prefix)/$(bindir_relative) +mandir = share/man +infodir = share/info +gitexecdir = libexec/git-core sharedir = $(prefix)/share -template_dir = $(sharedir)/git-core/templates -htmldir=$(sharedir)/doc/git-doc +template_dir = share/git-core/templates +htmldir = share/doc/git-doc ifeq ($(prefix),/usr) sysconfdir = /etc +ETC_GITCONFIG = $(sysconfdir)/gitconfig else sysconfdir = $(prefix)/etc +ETC_GITCONFIG = etc/gitconfig endif lib = lib -ETC_GITCONFIG = $(sysconfdir)/gitconfig # DESTDIR= # default configuration for gitweb @@ -388,6 +395,7 @@ LIB_H += revision.h LIB_H += run-command.h LIB_H += sha1-lookup.h LIB_H += sideband.h +LIB_H += sigchain.h LIB_H += strbuf.h LIB_H += tag.h LIB_H += transport.h @@ -481,6 +489,7 @@ LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1_name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o +LIB_OBJS += sigchain.o LIB_OBJS += strbuf.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o @@ -785,6 +794,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease + RUNTIME_PREFIX = YesPlease NO_POSIX_ONLY_PROGRAMS = YesPlease NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch @@ -793,9 +803,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o EXTLIBS += -lws2_32 X = .exe - gitexecdir = ../libexec/git-core - template_dir = ../share/git-core/templates/ - ETC_GITCONFIG = ../etc/gitconfig endif ifneq (,$(findstring arm,$(uname_M))) ARM_SHA1 = YesPlease @@ -817,6 +824,7 @@ ifeq ($(uname_S),Darwin) BASIC_LDFLAGS += -L/opt/local/lib endif endif + PTHREAD_LIBS = endif ifndef CC_LD_DYNPATH @@ -849,7 +857,12 @@ else endif endif ifndef NO_EXPAT - EXPAT_LIBEXPAT = -lexpat + ifdef EXPATDIR + BASIC_CFLAGS += -I$(EXPATDIR)/include + EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat + else + EXPAT_LIBEXPAT = -lexpat + endif endif endif @@ -1027,6 +1040,9 @@ ifdef INTERNAL_QSORT COMPAT_CFLAGS += -DINTERNAL_QSORT COMPAT_OBJS += compat/qsort.o endif +ifdef RUNTIME_PREFIX + COMPAT_CFLAGS += -DRUNTIME_PREFIX +endif ifdef NO_PTHREADS THREADED_DELTA_SEARCH = @@ -1086,6 +1102,7 @@ ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG)) DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) bindir_SQ = $(subst ','\'',$(bindir)) +bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) mandir_SQ = $(subst ','\'',$(mandir)) infodir_SQ = $(subst ','\'',$(infodir)) gitexecdir_SQ = $(subst ','\'',$(gitexecdir)) @@ -1251,7 +1268,12 @@ git.o git.spec \ $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< exec_cmd.o: exec_cmd.c GIT-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ + '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \ + '-DBINDIR="$(bindir_relative_SQ)"' \ + '-DPREFIX="$(prefix_SQ)"' \ + $< + builtin-init-db.o: builtin-init-db.c GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $< @@ -1360,11 +1382,13 @@ TEST_PROGRAMS += test-chmtime$X TEST_PROGRAMS += test-ctype$X TEST_PROGRAMS += test-date$X TEST_PROGRAMS += test-delta$X +TEST_PROGRAMS += test-dump-cache-tree$X TEST_PROGRAMS += test-genrandom$X TEST_PROGRAMS += test-match-trees$X TEST_PROGRAMS += test-parse-options$X TEST_PROGRAMS += test-path-utils$X TEST_PROGRAMS += test-sha1$X +TEST_PROGRAMS += test-sigchain$X all:: $(TEST_PROGRAMS) @@ -1410,17 +1434,17 @@ remove-dashes: ### Installation rules -ifeq ($(firstword $(subst /, ,$(template_dir))),..) -template_instdir = $(bindir)/$(template_dir) -else +ifeq ($(abspath $(template_dir)),$(template_dir)) template_instdir = $(template_dir) +else +template_instdir = $(prefix)/$(template_dir) endif export template_instdir -ifeq ($(firstword $(subst /, ,$(gitexecdir))),..) -gitexec_instdir = $(bindir)/$(gitexecdir) -else +ifeq ($(abspath $(gitexecdir)),$(gitexecdir)) gitexec_instdir = $(gitexecdir) +else +gitexec_instdir = $(prefix)/$(gitexecdir) endif gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir)) export gitexec_instdir diff --git a/builtin-apply.c b/builtin-apply.c index b415daf07e..f312798af3 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2441,7 +2441,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return error("%s: %s", old_name, strerror(errno)); } - if (!cached) + if (!cached && !tpatch) st_mode = ce_mode_from_stat(*ce, st->st_mode); if (patch->is_new < 0) @@ -2453,7 +2453,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s if (st_mode != patch->old_mode) fprintf(stderr, "warning: %s has type %o, expected %o\n", old_name, st_mode, patch->old_mode); - if (!patch->new_mode) + if (!patch->new_mode && !patch->is_delete) patch->new_mode = st_mode; return 0; diff --git a/builtin-branch.c b/builtin-branch.c index 02fa38fd3b..56a1971d69 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -193,21 +193,6 @@ struct ref_list { int kinds; }; -static int has_commit(struct commit *commit, struct commit_list *with_commit) -{ - if (!with_commit) - return 1; - while (with_commit) { - struct commit *other; - - other = with_commit->item; - with_commit = with_commit->next; - if (in_merge_bases(other, &commit, 1)) - return 1; - } - return 0; -} - static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct ref_list *ref_list = (struct ref_list*)(cb_data); @@ -231,7 +216,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, return error("branch '%s' does not point at a commit", refname); /* Filter with with_commit if specified */ - if (!has_commit(commit, ref_list->with_commit)) + if (!is_descendant_of(commit, ref_list->with_commit)) return 0; /* Don't add types the caller doesn't want */ @@ -401,7 +386,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached && head_commit && has_commit(head_commit, with_commit)) { + if (detached && head_commit && + is_descendant_of(head_commit, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); item.kind = REF_LOCAL_BRANCH; @@ -466,22 +452,6 @@ static void rename_branch(const char *oldname, const char *newname, int force) strbuf_release(&newsection); } -static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset) -{ - unsigned char sha1[20]; - struct commit *commit; - - if (!arg) - return -1; - if (get_sha1(arg, sha1)) - die("malformed object name %s", arg); - commit = lookup_commit_reference(sha1); - if (!commit) - die("no such commit %s", arg); - commit_list_insert(commit, opt->value); - return 0; -} - static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset) { merge_filter = ((opt->long_name[0] == 'n') @@ -517,13 +487,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPTION_CALLBACK, 0, "contains", &with_commit, "commit", "print only branches that contain the commit", PARSE_OPT_LASTARG_DEFAULT, - opt_parse_with_commit, (intptr_t)"HEAD", + parse_opt_with_commit, (intptr_t)"HEAD", }, { OPTION_CALLBACK, 0, "with", &with_commit, "commit", "print only branches that contain the commit", PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT, - opt_parse_with_commit, (intptr_t) "HEAD", + parse_opt_with_commit, (intptr_t) "HEAD", }, OPT__ABBREV(&abbrev), diff --git a/builtin-checkout.c b/builtin-checkout.c index 275176d15d..20b34ce6e1 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -230,7 +230,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - pathspec_match(pathspec, ps_matched, ce->name, 0); + match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched); } if (report_path_error(ps_matched, pathspec, 0)) @@ -239,7 +239,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, /* Any unmerged paths? */ for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (pathspec_match(pathspec, NULL, ce->name, 0)) { + if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) { if (!ce_stage(ce)) continue; if (opts->force) { @@ -264,7 +264,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, state.refresh_cache = 1; for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (pathspec_match(pathspec, NULL, ce->name, 0)) { + if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) { if (!ce_stage(ce)) { errs |= checkout_entry(ce, &state, NULL); continue; @@ -351,8 +351,16 @@ struct branch_info { static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; - strbuf_addstr(&buf, "refs/heads/"); - strbuf_addstr(&buf, branch->name); + int ret; + + if ((ret = interpret_nth_last_branch(branch->name, &buf)) + && ret == strlen(branch->name)) { + branch->name = xstrdup(buf.buf); + strbuf_splice(&buf, 0, 0, "refs/heads/", 11); + } else { + strbuf_addstr(&buf, "refs/heads/"); + strbuf_addstr(&buf, branch->name); + } branch->path = strbuf_detach(&buf, NULL); } @@ -661,6 +669,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) arg = argv[0]; has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + if (!strcmp(arg, "-")) + arg = "@{-1}"; + if (get_sha1(arg, rev)) { if (has_dash_dash) /* case (1) */ die("invalid reference: %s", arg); diff --git a/builtin-clone.c b/builtin-clone.c index f7e5a7b0a0..f73029e2ba 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -19,6 +19,7 @@ #include "strbuf.h" #include "dir.h" #include "pack-refs.h" +#include "sigchain.h" /* * Overall FIXMEs: @@ -288,7 +289,7 @@ static void remove_junk(void) static void remove_junk_on_signal(int signo) { remove_junk(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -441,7 +442,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } junk_git_dir = git_dir; atexit(remove_junk); - signal(SIGINT, remove_junk_on_signal); + sigchain_push_common(remove_junk_on_signal); setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); @@ -522,14 +523,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix) option_upload_pack); refs = transport_get_remote_refs(transport); - transport_fetch_refs(transport, refs); + if(refs) + transport_fetch_refs(transport, refs); } - clear_extra_refs(); + if (refs) { + clear_extra_refs(); - mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); + mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); - head_points_at = locate_head(refs, mapped_refs, &remote_head); + head_points_at = locate_head(refs, mapped_refs, &remote_head); + } + else { + warning("You appear to have cloned an empty repository."); + head_points_at = NULL; + remote_head = NULL; + option_no_checkout = 1; + } if (head_points_at) { /* Local default branch link */ diff --git a/builtin-commit.c b/builtin-commit.c index 7aaa5304c7..d6a3a6203a 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -166,7 +166,7 @@ static int list_paths(struct string_list *list, const char *with_tree, struct cache_entry *ce = active_cache[i]; if (ce->ce_flags & CE_UPDATE) continue; - if (!pathspec_match(pattern, m, ce->name, 0)) + if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m)) continue; string_list_insert(ce->name, list); } diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 469b07e240..29356d25db 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -2,6 +2,7 @@ #include "cache.h" #include "refs.h" #include "commit.h" +#include "sigchain.h" static char *get_stdin(void) { @@ -186,7 +187,7 @@ static void remove_keep(void) static void remove_keep_on_signal(int signo) { remove_keep(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -245,7 +246,7 @@ static int fetch_native_store(FILE *fp, char buffer[1024]; int err = 0; - signal(SIGINT, remove_keep_on_signal); + sigchain_push_common(remove_keep_on_signal); atexit(remove_keep); while (fgets(buffer, sizeof(buffer), stdin)) { diff --git a/builtin-fetch.c b/builtin-fetch.c index de6f3074b1..1e4a3d9c51 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -10,6 +10,7 @@ #include "transport.h" #include "run-command.h" #include "parse-options.h" +#include "sigchain.h" static const char * const builtin_fetch_usage[] = { "git fetch [options] [<repository> <refspec>...]", @@ -58,7 +59,7 @@ static void unlock_pack(void) static void unlock_pack_on_signal(int signo) { unlock_pack(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -672,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) ref_nr = j; } - signal(SIGINT, unlock_pack_on_signal); + sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); exit_code = do_fetch(transport, parse_fetch_refspec(ref_nr, refs), ref_nr); diff --git a/builtin-grep.c b/builtin-grep.c index bebf15cd6f..3f12ba3826 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -291,6 +291,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-E"); if (opt->regflags & REG_ICASE) push_arg("-i"); + if (opt->binary == GREP_BINARY_NOMATCH) + push_arg("-I"); if (opt->word_regexp) push_arg("-w"); if (opt->name_only) diff --git a/builtin-help.c b/builtin-help.c index f076efa921..9b57a74618 100644 --- a/builtin-help.c +++ b/builtin-help.c @@ -329,7 +329,7 @@ static void setup_man_path(void) * old_path, the ':' at the end will let 'man' to try * system-wide paths after ours to find the manual page. If * there is old_path, we need ':' as delimiter. */ - strbuf_addstr(&new_path, GIT_MAN_PATH); + strbuf_addstr(&new_path, system_path(GIT_MAN_PATH)); strbuf_addch(&new_path, ':'); if (old_path) strbuf_addstr(&new_path, old_path); @@ -375,7 +375,7 @@ static void show_man_page(const char *git_cmd) static void show_info_page(const char *git_cmd) { const char *page = cmd_to_page(git_cmd); - setenv("INFOPATH", GIT_INFO_PATH, 1); + setenv("INFOPATH", system_path(GIT_INFO_PATH), 1); execlp("info", "info", "gitman", page, NULL); } diff --git a/builtin-ls-files.c b/builtin-ls-files.c index f72eb85475..3434031295 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -36,42 +36,6 @@ static const char *tag_other = ""; static const char *tag_killed = ""; static const char *tag_modified = ""; - -/* - * Match a pathspec against a filename. The first "skiplen" characters - * are the common prefix - */ -int pathspec_match(const char **spec, char *ps_matched, - const char *filename, int skiplen) -{ - const char *m; - - while ((m = *spec++) != NULL) { - int matchlen = strlen(m + skiplen); - - if (!matchlen) - goto matched; - if (!strncmp(m + skiplen, filename + skiplen, matchlen)) { - if (m[skiplen + matchlen - 1] == '/') - goto matched; - switch (filename[skiplen + matchlen]) { - case '/': case '\0': - goto matched; - } - } - if (!fnmatch(m + skiplen, filename + skiplen, 0)) - goto matched; - if (ps_matched) - ps_matched++; - continue; - matched: - if (ps_matched) - *ps_matched = 1; - return 1; - } - return 0; -} - static void show_dir_entry(const char *tag, struct dir_entry *ent) { int len = prefix_len; @@ -80,7 +44,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (len >= ent->len) die("git ls-files: internal error - directory entry not superset of prefix"); - if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len)) + if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched)) return; fputs(tag, stdout); @@ -156,7 +120,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git ls-files: internal error - cache entry not superset of prefix"); - if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len)) + if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched)) return; if (tag && *tag && show_valid_bit && diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index f7c8c08b32..2789ccdf7d 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -29,6 +29,9 @@ static struct strbuf **p_hdr_data, **s_hdr_data; #define MAX_HDR_PARSED 10 #define MAX_BOUNDARIES 5 +static void cleanup_space(struct strbuf *sb); + + static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email) { struct strbuf *src = name; @@ -109,11 +112,19 @@ static void handle_from(const struct strbuf *from) strbuf_add(&email, at, el); strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0)); - /* The remainder is name. It could be "John Doe <john.doe@xz>" - * or "john.doe@xz (John Doe)", but we have removed the - * email part, so trim from both ends, possibly removing - * the () pair at the end. + /* The remainder is name. It could be + * + * - "John Doe <john.doe@xz>" (a), or + * - "john.doe@xz (John Doe)" (b), or + * - "John (zzz) Doe <john.doe@xz> (Comment)" (c) + * + * but we have removed the email part, so + * + * - remove extra spaces which could stay after email (case 'c'), and + * - trim from both ends, possibly removing the () pair at the end + * (cases 'a' and 'b'). */ + cleanup_space(&f); strbuf_trim(&f); if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') { strbuf_remove(&f, 0, 1); @@ -430,13 +441,6 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg) c -= 'a' - 26; else if ('0' <= c && c <= '9') c -= '0' - 52; - else if (c == '=') { - /* padding is almost like (c == 0), except we do - * not output NUL resulting only from it; - * for now we just trust the data. - */ - c = 0; - } else continue; /* garbage */ switch (pos++) { @@ -514,7 +518,25 @@ static int decode_header_bq(struct strbuf *it) rfc2047 = 1; if (in != ep) { - strbuf_add(&outbuf, in, ep - in); + /* + * We are about to process an encoded-word + * that begins at ep, but there is something + * before the encoded word. + */ + char *scan; + for (scan = in; scan < ep; scan++) + if (!isspace(*scan)) + break; + + if (scan != ep || in == it->buf) { + /* + * We should not lose that "something", + * unless we have just processed an + * encoded-word, and there is only LWS + * before the one we are about to process. + */ + strbuf_add(&outbuf, in, ep - in); + } in = ep; } /* E.g. @@ -860,6 +882,7 @@ static void handle_info(void) } output_header_lines(fout, "Subject", hdr); } else if (!memcmp(header[i], "From", 4)) { + cleanup_space(hdr); handle_from(hdr); fprintf(fout, "Author: %s\n", name.buf); fprintf(fout, "Email: %s\n", email.buf); diff --git a/builtin-send-pack.c b/builtin-send-pack.c index a9fdbf9d45..d65d019692 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -15,6 +15,20 @@ static struct send_pack_args args = { /* .receivepack = */ "git-receive-pack", }; +static int feed_object(const unsigned char *sha1, int fd, int negative) +{ + char buf[42]; + + if (negative && !has_sha1_file(sha1)) + return 1; + + memcpy(buf + negative, sha1_to_hex(sha1), 40); + if (negative) + buf[0] = '^'; + buf[40 + negative] = '\n'; + return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs"); +} + /* * Make a pack stream and spit it out into file descriptor fd */ @@ -35,7 +49,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext }; struct child_process po; int i; - char buf[42]; if (args.use_thin_pack) argv[4] = "--thin"; @@ -51,31 +64,17 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext * We feed the pack-objects we just spawned with revision * parameters by writing to the pipe. */ - for (i = 0; i < extra->nr; i++) { - memcpy(buf + 1, sha1_to_hex(&extra->array[i][0]), 40); - buf[0] = '^'; - buf[41] = '\n'; - if (!write_or_whine(po.in, buf, 42, "send-pack: send refs")) + for (i = 0; i < extra->nr; i++) + if (!feed_object(extra->array[i], po.in, 1)) break; - } while (refs) { if (!is_null_sha1(refs->old_sha1) && - has_sha1_file(refs->old_sha1)) { - memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40); - buf[0] = '^'; - buf[41] = '\n'; - if (!write_or_whine(po.in, buf, 42, - "send-pack: send refs")) - break; - } - if (!is_null_sha1(refs->new_sha1)) { - memcpy(buf, sha1_to_hex(refs->new_sha1), 40); - buf[40] = '\n'; - if (!write_or_whine(po.in, buf, 41, - "send-pack: send refs")) - break; - } + !feed_object(refs->old_sha1, po.in, 1)) + break; + if (!is_null_sha1(refs->new_sha1) && + !feed_object(refs->new_sha1, po.in, 0)) + break; refs = refs->next; } diff --git a/builtin-tag.c b/builtin-tag.c index a398499891..01e73747d0 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -26,6 +26,7 @@ static char signingkey[1000]; struct tag_filter { const char *pattern; int lines; + struct commit_list *with_commit; }; #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" @@ -42,6 +43,16 @@ static int show_reference(const char *refname, const unsigned char *sha1, char *buf, *sp, *eol; size_t len; + if (filter->with_commit) { + struct commit *commit; + + commit = lookup_commit_reference_gently(sha1, 1); + if (!commit) + return 0; + if (!is_descendant_of(commit, filter->with_commit)) + return 0; + } + if (!filter->lines) { printf("%s\n", refname); return 0; @@ -79,7 +90,8 @@ static int show_reference(const char *refname, const unsigned char *sha1, return 0; } -static int list_tags(const char *pattern, int lines) +static int list_tags(const char *pattern, int lines, + struct commit_list *with_commit) { struct tag_filter filter; @@ -88,6 +100,7 @@ static int list_tags(const char *pattern, int lines) filter.pattern = pattern; filter.lines = lines; + filter.with_commit = with_commit; for_each_tag_ref(show_reference, (void *) &filter); @@ -360,6 +373,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) list = 0, delete = 0, verify = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; + struct commit_list *with_commit = NULL; struct option options[] = { OPT_BOOLEAN('l', NULL, &list, "list tag names"), { OPTION_INTEGER, 'n', NULL, &lines, NULL, @@ -378,6 +392,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_STRING('u', NULL, &keyid, "key-id", "use another key to sign the tag"), OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"), + + OPT_GROUP("Tag listing options"), + { + OPTION_CALLBACK, 0, "contains", &with_commit, "commit", + "print only tags that contain the commit", + PARSE_OPT_LASTARG_DEFAULT, + parse_opt_with_commit, (intptr_t)"HEAD", + }, OPT_END() }; @@ -402,9 +424,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (list + delete + verify > 1) usage_with_options(git_tag_usage, options); if (list) - return list_tags(argv[0], lines == -1 ? 0 : lines); + return list_tags(argv[0], lines == -1 ? 0 : lines, + with_commit); if (lines != -1) die("-n option is only allowed with -l."); + if (with_commit) + die("--contains option is only allowed with -l."); if (delete) return for_each_tag_name(argv, delete_tag); if (verify) @@ -266,6 +266,8 @@ int create_bundle(struct bundle_header *header, const char *path, return error("unrecognized argument: %s'", argv[i]); } + object_array_remove_duplicates(&revs.pending); + for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; @@ -667,6 +667,7 @@ extern int read_ref(const char *filename, unsigned char *sha1); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +extern int interpret_nth_last_branch(const char *str, struct strbuf *); extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); extern const char *ref_rev_parse_rules[]; @@ -721,6 +722,10 @@ struct checkout { extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); extern int has_symlink_leading_path(int len, const char *name); +extern int has_symlink_or_noent_leading_path(int len, const char *name); +extern int has_dirs_only_path(int len, const char *name, int prefix_len); +extern void invalidate_lstat_cache(int len, const char *name); +extern void clear_lstat_cache(void); extern struct alternate_object_database { struct alternate_object_database *next; @@ -937,7 +942,6 @@ extern int ws_fix_copy(char *, const char *, int, unsigned, int *); extern int ws_blank_line(const char *line, int len, unsigned ws_rule); /* ls-files */ -int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen); int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset); void overlay_tree_on_cache(const char *tree_name, const char *prefix); @@ -202,3 +202,31 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) va_end(args); return r; } + +/* + * This function splits the buffer by newlines and colors the lines individually. + * + * Returns 0 on success. + */ +int color_fwrite_lines(FILE *fp, const char *color, + size_t count, const char *buf) +{ + if (!*color) + return fwrite(buf, count, 1, fp) != 1; + while (count) { + char *p = memchr(buf, '\n', count); + if (p != buf && (fputs(color, fp) < 0 || + fwrite(buf, p ? p - buf : count, 1, fp) != 1 || + fputs(COLOR_RESET, fp) < 0)) + return -1; + if (!p) + return 0; + if (fputc('\n', fp) < 0) + return -1; + count -= p + 1 - buf; + buf = p + 1; + } + return 0; +} + + @@ -20,5 +20,6 @@ void color_parse(const char *value, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); +int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); #endif /* COLOR_H */ @@ -705,6 +705,21 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two, return get_merge_bases_many(one, 1, &two, cleanup); } +int is_descendant_of(struct commit *commit, struct commit_list *with_commit) +{ + if (!with_commit) + return 1; + while (with_commit) { + struct commit *other; + + other = with_commit->item; + with_commit = with_commit->next; + if (in_merge_bases(other, &commit, 1)) + return 1; + } + return 0; +} + int in_merge_bases(struct commit *commit, struct commit **reference, int num) { struct commit_list *bases, *b; @@ -133,6 +133,7 @@ extern int is_repository_shallow(void); extern struct commit_list *get_shallow_commits(struct object_array *heads, int depth, int shallow_flag, int not_shallow_flag); +int is_descendant_of(struct commit *, struct commit_list *); int in_merge_bases(struct commit *, struct commit **, int); extern int interactive_add(int argc, const char **argv, const char *prefix); diff --git a/compat/mingw.h b/compat/mingw.h index 4f275cb8e6..a255898801 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -21,12 +21,12 @@ typedef int pid_t; #define WEXITSTATUS(x) ((x) & 0xff) #define WIFSIGNALED(x) ((unsigned)(x) > 259) -#define SIGKILL 0 -#define SIGCHLD 0 -#define SIGPIPE 0 -#define SIGHUP 0 -#define SIGQUIT 0 -#define SIGALRM 100 +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGKILL 9 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGCHLD 17 #define F_GETFD 1 #define F_SETFD 2 diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 81f70ec644..307bf5d4f9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,6 +34,12 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -116,10 +122,26 @@ __git_ps1 () fi fi + local w + local i + + if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then + if test "$(git config --bool bash.showDirtyState)" != "false"; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi + fi + fi + if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$r" + printf "$1" "${b##refs/heads/}$w$i$r" else - printf " (%s)" "${b##refs/heads/}$r" + printf " (%s)" "${b##refs/heads/}$w$i$r" fi fi } diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 0c48506eeb..db3af6a833 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -1,7 +1,7 @@ #!/bin/sh # git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge, -# vimdiff, gvimdiff, and custom user-configurable tools. +# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff, +# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools. # This script is typically launched by using the 'git difftool' # convenience command. # @@ -73,6 +73,10 @@ launch_merge_tool () { > /dev/null 2>&1 ;; + kompare) + "$merge_tool_path" "$LOCAL" "$REMOTE" + ;; + tkdiff) "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" ;; @@ -134,7 +138,7 @@ valid_custom_tool() { # Built-in merge tools are always valid. valid_tool() { case "$1" in - kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) + kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) ;; # happy *) if ! valid_custom_tool "$1" @@ -177,31 +181,24 @@ fi # Try to guess an appropriate merge tool if no tool has been set. if test -z "$merge_tool"; then - # We have a $DISPLAY so try some common UNIX merge tools if test -n "$DISPLAY"; then - merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" - # If gnome then prefer meld - if test -n "$GNOME_DESKTOP_SESSION_ID"; then - merge_tool_candidates="meld $merge_tool_candidates" - fi - # If KDE then prefer kdiff3 - if test "$KDE_FULL_SESSION" = "true"; then - merge_tool_candidates="kdiff3 $merge_tool_candidates" + # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare + if test -n "$GNOME_DESKTOP_SESSION_ID" ; then + merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff" + else + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" fi fi - - # $EDITOR is emacs so add emerge as a candidate if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates emerge" + # $EDITOR is emacs so add emerge as a candidate + merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff" + elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then + # $EDITOR is vim so add vimdiff as a candidate + merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge" + else + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" fi - - # $EDITOR is vim so add vimdiff as a candidate - if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates vimdiff" - fi - - merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" echo "merge tool candidates: $merge_tool_candidates" # Loop over each candidate and stop when a valid merge tool is found. diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index ca3dbd2465..6e2610cda6 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -28,7 +28,8 @@ OPTIONS --tool=<tool>:: Use the merge resolution program specified by <tool>. Valid merge tools are: - kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + kdiff3, kompare, tkdiff, meld, xxdiff, emerge, + vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' will use the configuration variable `merge.tool`. If the @@ -937,6 +937,8 @@ int main(int argc, char **argv) gid_t gid = 0; int i; + git_extract_argv0_path(argv[0]); + for (i = 1; i < argc; i++) { char *arg = argv[i]; diff --git a/diff-no-index.c b/diff-no-index.c index 60ed17470a..0dbd9dad8b 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -40,7 +40,7 @@ static int get_mode(const char *path, int *mode) *mode = 0; else if (!strcmp(path, "-")) *mode = create_ce_mode(0666); - else if (stat(path, &st)) + else if (lstat(path, &st)) return error("Could not access '%s'", path); else *mode = st.st_mode; @@ -12,6 +12,7 @@ #include "run-command.h" #include "utf8.h" #include "userdiff.h" +#include "sigchain.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -23,6 +24,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = 200; static int diff_suppress_blank_empty; int diff_use_color_default = -1; +static const char *diff_word_regex_cfg; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; @@ -92,6 +94,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) } if (!strcmp(var, "diff.external")) return git_config_string(&external_diff_cmd_cfg, var, value); + if (!strcmp(var, "diff.wordregex")) + return git_config_string(&diff_word_regex_cfg, var, value); return git_diff_basic_config(var, value, cb); } @@ -167,6 +171,33 @@ static struct diff_tempfile { char tmp_path[PATH_MAX]; } diff_temp[2]; +static struct diff_tempfile *claim_diff_tempfile(void) { + int i; + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) + if (!diff_temp[i].name) + return diff_temp + i; + die("BUG: diff is failing to clean up its tempfiles"); +} + +static int remove_tempfile_installed; + +static void remove_tempfile(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) + if (diff_temp[i].name == diff_temp[i].tmp_path) { + unlink(diff_temp[i].name); + diff_temp[i].name = NULL; + } +} + +static void remove_tempfile_on_signal(int signo) +{ + remove_tempfile(); + sigchain_pop(signo); + raise(signo); +} + static int count_lines(const char *data, int size) { int count, ch, completely_empty = 1, nl_just_seen = 0; @@ -321,82 +352,138 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) struct diff_words_buffer { mmfile_t text; long alloc; - long current; /* output pointer */ - int suppressed_newline; + struct diff_words_orig { + const char *begin, *end; + } *orig; + int orig_nr, orig_alloc; }; static void diff_words_append(char *line, unsigned long len, struct diff_words_buffer *buffer) { - if (buffer->text.size + len > buffer->alloc) { - buffer->alloc = (buffer->text.size + len) * 3 / 2; - buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc); - } + ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc); line++; len--; memcpy(buffer->text.ptr + buffer->text.size, line, len); buffer->text.size += len; + buffer->text.ptr[buffer->text.size] = '\0'; } struct diff_words_data { struct diff_words_buffer minus, plus; + const char *current_plus; FILE *file; + regex_t *word_regex; }; -static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color, - int suppress_newline) +static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) { - const char *ptr; - int eol = 0; + struct diff_words_data *diff_words = priv; + int minus_first, minus_len, plus_first, plus_len; + const char *minus_begin, *minus_end, *plus_begin, *plus_end; - if (len == 0) + if (line[0] != '@' || parse_hunk_header(line, len, + &minus_first, &minus_len, &plus_first, &plus_len)) return; - ptr = buffer->text.ptr + buffer->current; - buffer->current += len; + /* POSIX requires that first be decremented by one if len == 0... */ + if (minus_len) { + minus_begin = diff_words->minus.orig[minus_first].begin; + minus_end = + diff_words->minus.orig[minus_first + minus_len - 1].end; + } else + minus_begin = minus_end = + diff_words->minus.orig[minus_first].end; - if (ptr[len - 1] == '\n') { - eol = 1; - len--; + if (plus_len) { + plus_begin = diff_words->plus.orig[plus_first].begin; + plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end; + } else + plus_begin = plus_end = diff_words->plus.orig[plus_first].end; + + if (diff_words->current_plus != plus_begin) + fwrite(diff_words->current_plus, + plus_begin - diff_words->current_plus, 1, + diff_words->file); + if (minus_begin != minus_end) + color_fwrite_lines(diff_words->file, + diff_get_color(1, DIFF_FILE_OLD), + minus_end - minus_begin, minus_begin); + if (plus_begin != plus_end) + color_fwrite_lines(diff_words->file, + diff_get_color(1, DIFF_FILE_NEW), + plus_end - plus_begin, plus_begin); + + diff_words->current_plus = plus_end; +} + +/* This function starts looking at *begin, and returns 0 iff a word was found. */ +static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex, + int *begin, int *end) +{ + if (word_regex && *begin < buffer->size) { + regmatch_t match[1]; + if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) { + char *p = memchr(buffer->ptr + *begin + match[0].rm_so, + '\n', match[0].rm_eo - match[0].rm_so); + *end = p ? p - buffer->ptr : match[0].rm_eo + *begin; + *begin += match[0].rm_so; + return *begin >= *end; + } + return -1; } - fputs(diff_get_color(1, color), file); - fwrite(ptr, len, 1, file); - fputs(diff_get_color(1, DIFF_RESET), file); + /* find the next word */ + while (*begin < buffer->size && isspace(buffer->ptr[*begin])) + (*begin)++; + if (*begin >= buffer->size) + return -1; - if (eol) { - if (suppress_newline) - buffer->suppressed_newline = 1; - else - putc('\n', file); - } + /* find the end of the word */ + *end = *begin + 1; + while (*end < buffer->size && !isspace(buffer->ptr[*end])) + (*end)++; + + return 0; } -static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len) +/* + * This function splits the words in buffer->text, stores the list with + * newline separator into out, and saves the offsets of the original words + * in buffer->orig. + */ +static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out, + regex_t *word_regex) { - struct diff_words_data *diff_words = priv; + int i, j; + long alloc = 0; - if (diff_words->minus.suppressed_newline) { - if (line[0] != '+') - putc('\n', diff_words->file); - diff_words->minus.suppressed_newline = 0; - } + out->size = 0; + out->ptr = NULL; - len--; - switch (line[0]) { - case '-': - print_word(diff_words->file, - &diff_words->minus, len, DIFF_FILE_OLD, 1); - break; - case '+': - print_word(diff_words->file, - &diff_words->plus, len, DIFF_FILE_NEW, 0); - break; - case ' ': - print_word(diff_words->file, - &diff_words->plus, len, DIFF_PLAIN, 0); - diff_words->minus.current += len; - break; + /* fake an empty "0th" word */ + ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc); + buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr; + buffer->orig_nr = 1; + + for (i = 0; i < buffer->text.size; i++) { + if (find_word_boundaries(&buffer->text, word_regex, &i, &j)) + return; + + /* store original boundaries */ + ALLOC_GROW(buffer->orig, buffer->orig_nr + 1, + buffer->orig_alloc); + buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i; + buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j; + buffer->orig_nr++; + + /* store one word */ + ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc); + memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i); + out->ptr[out->size + j - i] = '\n'; + out->size += j - i + 1; + + i = j - 1; } } @@ -407,38 +494,36 @@ static void diff_words_show(struct diff_words_data *diff_words) xdemitconf_t xecfg; xdemitcb_t ecb; mmfile_t minus, plus; - int i; + + /* special case: only removal */ + if (!diff_words->plus.text.size) { + color_fwrite_lines(diff_words->file, + diff_get_color(1, DIFF_FILE_OLD), + diff_words->minus.text.size, diff_words->minus.text.ptr); + diff_words->minus.text.size = 0; + return; + } + + diff_words->current_plus = diff_words->plus.text.ptr; memset(&xpp, 0, sizeof(xpp)); memset(&xecfg, 0, sizeof(xecfg)); - minus.size = diff_words->minus.text.size; - minus.ptr = xmalloc(minus.size); - memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size); - for (i = 0; i < minus.size; i++) - if (isspace(minus.ptr[i])) - minus.ptr[i] = '\n'; - diff_words->minus.current = 0; - - plus.size = diff_words->plus.text.size; - plus.ptr = xmalloc(plus.size); - memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size); - for (i = 0; i < plus.size; i++) - if (isspace(plus.ptr[i])) - plus.ptr[i] = '\n'; - diff_words->plus.current = 0; - + diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex); + diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex); xpp.flags = XDF_NEED_MINIMAL; - xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc; + /* as only the hunk header will be parsed, we need a 0-context */ + xecfg.ctxlen = 0; xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, &xpp, &xecfg, &ecb); free(minus.ptr); free(plus.ptr); + if (diff_words->current_plus != diff_words->plus.text.ptr + + diff_words->plus.text.size) + fwrite(diff_words->current_plus, + diff_words->plus.text.ptr + diff_words->plus.text.size + - diff_words->current_plus, 1, + diff_words->file); diff_words->minus.text.size = diff_words->plus.text.size = 0; - - if (diff_words->minus.suppressed_newline) { - putc('\n', diff_words->file); - diff_words->minus.suppressed_newline = 0; - } } typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len); @@ -462,7 +547,10 @@ static void free_diff_words_data(struct emit_callback *ecbdata) diff_words_show(ecbdata->diff_words); free (ecbdata->diff_words->minus.text.ptr); + free (ecbdata->diff_words->minus.orig); free (ecbdata->diff_words->plus.text.ptr); + free (ecbdata->diff_words->plus.orig); + free(ecbdata->diff_words->word_regex); free(ecbdata->diff_words); ecbdata->diff_words = NULL; } @@ -1325,6 +1413,12 @@ static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespe return one->driver->funcname.pattern ? &one->driver->funcname : NULL; } +static const char *userdiff_word_regex(struct diff_filespec *one) +{ + diff_filespec_load_driver(one); + return one->driver->word_regex; +} + void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b) { if (!options->a_prefix) @@ -1485,6 +1579,21 @@ static void builtin_diff(const char *name_a, ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); ecbdata.diff_words->file = o->file; + if (!o->word_regex) + o->word_regex = userdiff_word_regex(one); + if (!o->word_regex) + o->word_regex = userdiff_word_regex(two); + if (!o->word_regex) + o->word_regex = diff_word_regex_cfg; + if (o->word_regex) { + ecbdata.diff_words->word_regex = (regex_t *) + xmalloc(sizeof(regex_t)); + if (regcomp(ecbdata.diff_words->word_regex, + o->word_regex, + REG_EXTENDED | REG_NEWLINE)) + die ("Invalid regular expression: %s", + o->word_regex); + } } xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, &xpp, &xecfg, &ecb); @@ -1859,10 +1968,11 @@ static void prep_temp_blob(struct diff_tempfile *temp, sprintf(temp->mode, "%06o", mode); } -static void prepare_temp_file(const char *name, - struct diff_tempfile *temp, - struct diff_filespec *one) +static struct diff_tempfile *prepare_temp_file(const char *name, + struct diff_filespec *one) { + struct diff_tempfile *temp = claim_diff_tempfile(); + if (!DIFF_FILE_VALID(one)) { not_a_valid_file: /* A '-' entry produces this for file-2, and @@ -1871,7 +1981,13 @@ static void prepare_temp_file(const char *name, temp->name = "/dev/null"; strcpy(temp->hex, "."); strcpy(temp->mode, "."); - return; + return temp; + } + + if (!remove_tempfile_installed) { + atexit(remove_tempfile); + sigchain_push_common(remove_tempfile_on_signal); + remove_tempfile_installed = 1; } if (!one->sha1_valid || @@ -1911,7 +2027,7 @@ static void prepare_temp_file(const char *name, */ sprintf(temp->mode, "%06o", one->mode); } - return; + return temp; } else { if (diff_populate_filespec(one, 0)) @@ -1919,24 +2035,7 @@ static void prepare_temp_file(const char *name, prep_temp_blob(temp, one->data, one->size, one->sha1, one->mode); } -} - -static void remove_tempfile(void) -{ - int i; - - for (i = 0; i < 2; i++) - if (diff_temp[i].name == diff_temp[i].tmp_path) { - unlink(diff_temp[i].name); - diff_temp[i].name = NULL; - } -} - -static void remove_tempfile_on_signal(int signo) -{ - remove_tempfile(); - signal(SIGINT, SIG_DFL); - raise(signo); + return temp; } /* An external diff command takes: @@ -1954,34 +2053,22 @@ static void run_external_diff(const char *pgm, int complete_rewrite) { const char *spawn_arg[10]; - struct diff_tempfile *temp = diff_temp; int retval; - static int atexit_asked = 0; - const char *othername; const char **arg = &spawn_arg[0]; - othername = (other? other : name); - if (one && two) { - prepare_temp_file(name, &temp[0], one); - prepare_temp_file(othername, &temp[1], two); - if (! atexit_asked && - (temp[0].name == temp[0].tmp_path || - temp[1].name == temp[1].tmp_path)) { - atexit_asked = 1; - atexit(remove_tempfile); - } - signal(SIGINT, remove_tempfile_on_signal); - } - if (one && two) { + struct diff_tempfile *temp_one, *temp_two; + const char *othername = (other ? other : name); + temp_one = prepare_temp_file(name, one); + temp_two = prepare_temp_file(othername, two); *arg++ = pgm; *arg++ = name; - *arg++ = temp[0].name; - *arg++ = temp[0].hex; - *arg++ = temp[0].mode; - *arg++ = temp[1].name; - *arg++ = temp[1].hex; - *arg++ = temp[1].mode; + *arg++ = temp_one->name; + *arg++ = temp_one->hex; + *arg++ = temp_one->mode; + *arg++ = temp_two->name; + *arg++ = temp_two->hex; + *arg++ = temp_two->mode; if (other) { *arg++ = other; *arg++ = xfrm_msg; @@ -2000,16 +2087,86 @@ static void run_external_diff(const char *pgm, } } +static int similarity_index(struct diff_filepair *p) +{ + return p->score * 100 / MAX_SCORE; +} + +static void fill_metainfo(struct strbuf *msg, + const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + struct diff_options *o, + struct diff_filepair *p) +{ + strbuf_init(msg, PATH_MAX * 2 + 300); + switch (p->status) { + case DIFF_STATUS_COPIED: + strbuf_addf(msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(msg, "\ncopy from "); + quote_c_style(name, msg, NULL, 0); + strbuf_addstr(msg, "\ncopy to "); + quote_c_style(other, msg, NULL, 0); + strbuf_addch(msg, '\n'); + break; + case DIFF_STATUS_RENAMED: + strbuf_addf(msg, "similarity index %d%%", similarity_index(p)); + strbuf_addstr(msg, "\nrename from "); + quote_c_style(name, msg, NULL, 0); + strbuf_addstr(msg, "\nrename to "); + quote_c_style(other, msg, NULL, 0); + strbuf_addch(msg, '\n'); + break; + case DIFF_STATUS_MODIFIED: + if (p->score) { + strbuf_addf(msg, "dissimilarity index %d%%\n", + similarity_index(p)); + break; + } + /* fallthru */ + default: + /* nothing */ + ; + } + if (one && two && hashcmp(one->sha1, two->sha1)) { + int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; + + if (DIFF_OPT_TST(o, BINARY)) { + mmfile_t mf; + if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || + (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) + abbrev = 40; + } + strbuf_addf(msg, "index %.*s..%.*s", + abbrev, sha1_to_hex(one->sha1), + abbrev, sha1_to_hex(two->sha1)); + if (one->mode == two->mode) + strbuf_addf(msg, " %06o", one->mode); + strbuf_addch(msg, '\n'); + } + if (msg->len) + strbuf_setlen(msg, msg->len - 1); +} + static void run_diff_cmd(const char *pgm, const char *name, const char *other, const char *attr_path, struct diff_filespec *one, struct diff_filespec *two, - const char *xfrm_msg, + struct strbuf *msg, struct diff_options *o, - int complete_rewrite) + struct diff_filepair *p) { + const char *xfrm_msg = NULL; + int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score; + + if (msg) { + fill_metainfo(msg, name, other, one, two, o, p); + xfrm_msg = msg->len ? msg->buf : NULL; + } + if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { @@ -2049,11 +2206,6 @@ static void diff_fill_sha1_info(struct diff_filespec *one) hashclr(one->sha1); } -static int similarity_index(struct diff_filepair *p) -{ - return p->score * 100 / MAX_SCORE; -} - static void strip_prefix(int prefix_length, const char **namep, const char **otherp) { /* Strip the prefix but do not molest /dev/null and absolute paths */ @@ -2067,13 +2219,11 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) { const char *pgm = external_diff(); struct strbuf msg; - char *xfrm_msg; struct diff_filespec *one = p->one; struct diff_filespec *two = p->two; const char *name; const char *other; const char *attr_path; - int complete_rewrite = 0; name = p->one->path; other = (strcmp(name, p->two->path) ? p->two->path : NULL); @@ -2083,83 +2233,34 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (DIFF_PAIR_UNMERGED(p)) { run_diff_cmd(pgm, name, NULL, attr_path, - NULL, NULL, NULL, o, 0); + NULL, NULL, NULL, o, p); return; } diff_fill_sha1_info(one); diff_fill_sha1_info(two); - strbuf_init(&msg, PATH_MAX * 2 + 300); - switch (p->status) { - case DIFF_STATUS_COPIED: - strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); - strbuf_addstr(&msg, "\ncopy from "); - quote_c_style(name, &msg, NULL, 0); - strbuf_addstr(&msg, "\ncopy to "); - quote_c_style(other, &msg, NULL, 0); - strbuf_addch(&msg, '\n'); - break; - case DIFF_STATUS_RENAMED: - strbuf_addf(&msg, "similarity index %d%%", similarity_index(p)); - strbuf_addstr(&msg, "\nrename from "); - quote_c_style(name, &msg, NULL, 0); - strbuf_addstr(&msg, "\nrename to "); - quote_c_style(other, &msg, NULL, 0); - strbuf_addch(&msg, '\n'); - break; - case DIFF_STATUS_MODIFIED: - if (p->score) { - strbuf_addf(&msg, "dissimilarity index %d%%\n", - similarity_index(p)); - complete_rewrite = 1; - break; - } - /* fallthru */ - default: - /* nothing */ - ; - } - - if (hashcmp(one->sha1, two->sha1)) { - int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; - - if (DIFF_OPT_TST(o, BINARY)) { - mmfile_t mf; - if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || - (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) - abbrev = 40; - } - strbuf_addf(&msg, "index %.*s..%.*s", - abbrev, sha1_to_hex(one->sha1), - abbrev, sha1_to_hex(two->sha1)); - if (one->mode == two->mode) - strbuf_addf(&msg, " %06o", one->mode); - strbuf_addch(&msg, '\n'); - } - - if (msg.len) - strbuf_setlen(&msg, msg.len - 1); - xfrm_msg = msg.len ? msg.buf : NULL; - if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && (S_IFMT & one->mode) != (S_IFMT & two->mode)) { - /* a filepair that changes between file and symlink + /* + * a filepair that changes between file and symlink * needs to be split into deletion and creation. */ struct diff_filespec *null = alloc_filespec(two->path); run_diff_cmd(NULL, name, other, attr_path, - one, null, xfrm_msg, o, 0); + one, null, &msg, o, p); free(null); + strbuf_release(&msg); + null = alloc_filespec(one->path); run_diff_cmd(NULL, name, other, attr_path, - null, two, xfrm_msg, o, 0); + null, two, &msg, o, p); free(null); } else run_diff_cmd(pgm, name, other, attr_path, - one, two, xfrm_msg, o, complete_rewrite); + one, two, &msg, o, p); strbuf_release(&msg); } @@ -2498,6 +2599,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_CLR(options, COLOR_DIFF); else if (!strcmp(arg, "--color-words")) options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + else if (!prefixcmp(arg, "--color-words=")) { + options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + options->word_regex = arg + 14; + } else if (!strcmp(arg, "--exit-code")) DIFF_OPT_SET(options, EXIT_WITH_STATUS); else if (!strcmp(arg, "--quiet")) @@ -3452,15 +3557,15 @@ void diff_unmerge(struct diff_options *options, static char *run_textconv(const char *pgm, struct diff_filespec *spec, size_t *outsize) { - struct diff_tempfile temp; + struct diff_tempfile *temp; const char *argv[3]; const char **arg = argv; struct child_process child; struct strbuf buf = STRBUF_INIT; - prepare_temp_file(spec->path, &temp, spec); + temp = prepare_temp_file(spec->path, spec); *arg++ = pgm; - *arg++ = temp.name; + *arg++ = temp->name; *arg = NULL; memset(&child, 0, sizeof(child)); @@ -3469,13 +3574,11 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, if (start_command(&child) != 0 || strbuf_read(&buf, child.out, 0) < 0 || finish_command(&child) != 0) { - if (temp.name == temp.tmp_path) - unlink(temp.name); + remove_tempfile(); error("error running textconv command '%s'", pgm); return NULL; } - if (temp.name == temp.tmp_path) - unlink(temp.name); + remove_tempfile(); return strbuf_detach(&buf, outsize); } @@ -98,6 +98,7 @@ struct diff_options { int stat_width; int stat_name_width; + const char *word_regex; /* this is set by diffcore for DIFF_FORMAT_PATCH */ int found_changes; @@ -108,25 +108,28 @@ static int match_one(const char *match, const char *name, int namelen) * and a mark is left in seen[] array for pathspec element that * actually matched anything. */ -int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen) +int match_pathspec(const char **pathspec, const char *name, int namelen, + int prefix, char *seen) { - int retval; - const char *match; + int i, retval = 0; + + if (!pathspec) + return 1; name += prefix; namelen -= prefix; - for (retval = 0; (match = *pathspec++) != NULL; seen++) { + for (i = 0; pathspec[i] != NULL; i++) { int how; - if (retval && *seen == MATCHED_EXACTLY) + const char *match = pathspec[i] + prefix; + if (seen && seen[i] == MATCHED_EXACTLY) continue; - match += prefix; how = match_one(match, name, namelen); if (how) { if (retval < how) retval = how; - if (*seen < how) - *seen = how; + if (seen && seen[i] < how) + seen[i] = how; } } return retval; @@ -9,35 +9,25 @@ static void create_directories(const char *path, const struct checkout *state) const char *slash = path; while ((slash = strchr(slash+1, '/')) != NULL) { - struct stat st; - int stat_status; - len = slash - path; memcpy(buf, path, len); buf[len] = 0; - if (len <= state->base_dir_len) - /* - * checkout-index --prefix=<dir>; <dir> is - * allowed to be a symlink to an existing - * directory. - */ - stat_status = stat(buf, &st); - else - /* - * if there currently is a symlink, we would - * want to replace it with a real directory. - */ - stat_status = lstat(buf, &st); - - if (!stat_status && S_ISDIR(st.st_mode)) + /* + * For 'checkout-index --prefix=<dir>', <dir> is + * allowed to be a symlink to an existing directory, + * and we set 'state->base_dir_len' below, such that + * we test the path components of the prefix with the + * stat() function instead of the lstat() function. + */ + if (has_dirs_only_path(len, buf, state->base_dir_len)) continue; /* ok, it is already a directory. */ /* - * We know stat_status == 0 means something exists - * there and this mkdir would fail, but that is an - * error codepath; we do not care, as we unlink and - * mkdir again in such a case. + * If this mkdir() would fail, it could be that there + * is already a symlink or something else exists + * there, therefore we then try to unlink it and try + * one more time to create the directory. */ if (mkdir(buf, 0777)) { if (errno == EEXIST && state->force && diff --git a/exec_cmd.c b/exec_cmd.c index cdd35f9195..f234066def 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -9,17 +9,78 @@ static const char *argv0_path; const char *system_path(const char *path) { - if (!is_absolute_path(path) && argv0_path) { - struct strbuf d = STRBUF_INIT; - strbuf_addf(&d, "%s/%s", argv0_path, path); - path = strbuf_detach(&d, NULL); +#ifdef RUNTIME_PREFIX + static const char *prefix; +#else + static const char *prefix = PREFIX; +#endif + struct strbuf d = STRBUF_INIT; + + if (is_absolute_path(path)) + return path; + +#ifdef RUNTIME_PREFIX + assert(argv0_path); + assert(is_absolute_path(argv0_path)); + + if (!prefix) { + const char *strip[] = { + GIT_EXEC_PATH, + BINDIR, + 0 + }; + const char **s; + + for (s = strip; *s; s++) { + const char *sargv = argv0_path + strlen(argv0_path); + const char *ss = *s + strlen(*s); + while (argv0_path < sargv && *s < ss + && (*sargv == *ss || + (is_dir_sep(*sargv) && is_dir_sep(*ss)))) { + sargv--; + ss--; + } + if (*s == ss) { + struct strbuf d = STRBUF_INIT; + /* We also skip the trailing directory separator. */ + assert(sargv - argv0_path - 1 >= 0); + strbuf_add(&d, argv0_path, sargv - argv0_path - 1); + prefix = strbuf_detach(&d, NULL); + break; + } + } } + + if (!prefix) { + prefix = PREFIX; + fprintf(stderr, "RUNTIME_PREFIX requested, " + "but prefix computation failed. " + "Using static fallback '%s'.\n", prefix); + } +#endif + + strbuf_addf(&d, "%s/%s", prefix, path); + path = strbuf_detach(&d, NULL); return path; } -void git_set_argv0_path(const char *path) +const char *git_extract_argv0_path(const char *argv0) { - argv0_path = path; + const char *slash; + + if (!argv0 || !*argv0) + return NULL; + slash = argv0 + strlen(argv0); + + while (argv0 <= slash && !is_dir_sep(*slash)) + slash--; + + if (slash >= argv0) { + argv0_path = xstrndup(argv0, slash - argv0); + return slash + 1; + } + + return argv0; } void git_set_argv_exec_path(const char *exec_path) @@ -61,9 +122,7 @@ void setup_path(void) const char *old_path = getenv("PATH"); struct strbuf new_path = STRBUF_INIT; - add_path(&new_path, argv_exec_path); - add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT)); - add_path(&new_path, system_path(GIT_EXEC_PATH)); + add_path(&new_path, git_exec_path()); add_path(&new_path, argv0_path); if (old_path) diff --git a/exec_cmd.h b/exec_cmd.h index 594f961387..e2b546b615 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -2,8 +2,8 @@ #define GIT_EXEC_CMD_H extern void git_set_argv_exec_path(const char *exec_path); -extern void git_set_argv0_path(const char *path); -extern const char* git_exec_path(void); +extern const char *git_extract_argv0_path(const char *path); +extern const char *git_exec_path(void); extern void setup_path(void); extern const char **prepare_git_cmd(const char **argv); extern int execv_git_cmd(const char **argv); /* NULL terminated */ diff --git a/fast-import.c b/fast-import.c index f0e08aca70..1935206be0 100644 --- a/fast-import.c +++ b/fast-import.c @@ -150,6 +150,7 @@ Format of STDIN stream: #include "refs.h" #include "csum-file.h" #include "quote.h" +#include "exec_cmd.h" #define PACK_ID_BITS 16 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) @@ -2406,6 +2407,8 @@ int main(int argc, const char **argv) { unsigned int i, show_stats = 1; + git_extract_argv0_path(argv[0]); + setup_git_directory(); git_config(git_pack_config, NULL); if (!pack_compression_seen && core_compression_seen) @@ -8,9 +8,8 @@ OPTIONS_SPEC="\ git am [options] [<mbox>|<Maildir>...] git am [options] (--resolved | --skip | --abort) -- -d,dotest= (removed -- do not use) i,interactive run interactively -b,binary (historical option -- no-op) +b,binary* (historical option -- no-op) 3,3way allow fall back on 3way merging if needed s,signoff add a Signed-off-by line to the commit message u,utf8 recode into utf8 (default) @@ -26,7 +25,7 @@ skip skip the current patch abort restore the original branch and abort the patching operation. committer-date-is-author-date lie about committer date ignore-date use current timestamp for author date -rebasing (internal use for git-rebase)" +rebasing* (internal use for git-rebase)" . git-sh-setup prefix=$(git rev-parse --show-prefix) @@ -212,7 +211,7 @@ then # unreliable -- stdin could be /dev/null for example # and the caller did not intend to feed us a patch but # wanted to continue unattended. - tty -s + test -t 0 ;; *) false @@ -288,10 +287,7 @@ fi case "$resolved" in '') files=$(git diff-index --cached --name-only HEAD --) || exit - if [ "$files" ]; then - echo "Dirty index: cannot apply patches (dirty: $files)" >&2 - exit 1 - fi + test "$files" && die "Dirty index: cannot apply patches (dirty: $files)" esac if test "$(cat "$dotest/utf8")" = t diff --git a/git-cvsserver.perl b/git-cvsserver.perl index fef7faf339..ab6cea3e53 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -76,6 +76,7 @@ my $methods = { 'history' => \&req_CATCHALL, 'watchers' => \&req_EMPTY, 'editors' => \&req_EMPTY, + 'noop' => \&req_EMPTY, 'annotate' => \&req_annotate, 'Global_option' => \&req_Globaloption, #'annotate' => \&req_CATCHALL, @@ -1413,14 +1414,14 @@ sub req_ci close $pipe || die "bad pipe: $! $?"; } + $updater->update(); + ### Then hooks/post-update $hook = $ENV{GIT_DIR}.'hooks/post-update'; if (-x $hook) { system($hook, "refs/heads/$state->{module}"); } - $updater->update(); - # foreach file specified on the command line ... foreach my $filename ( @committedfiles ) { diff --git a/git-mergetool.sh b/git-mergetool.sh index 00e1337306..09f3a1068f 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -390,21 +390,19 @@ fi if test -z "$merge_tool" ; then if test -n "$DISPLAY"; then - merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" if test -n "$GNOME_DESKTOP_SESSION_ID" ; then - merge_tool_candidates="meld $merge_tool_candidates" - fi - if test "$KDE_FULL_SESSION" = "true"; then - merge_tool_candidates="kdiff3 $merge_tool_candidates" + merge_tool_candidates="meld kdiff3 tkdiff xxdiff gvimdiff" + else + merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" fi fi if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates emerge" - fi - if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates vimdiff" + merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff" + elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then + merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge" + else + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" fi - merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" echo "merge tool candidates: $merge_tool_candidates" for i in $merge_tool_candidates; do init_merge_tool_path $i diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 21ac20c305..3dc659dd58 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -373,17 +373,15 @@ do_next () { pick_one -n $sha1 || failed=t case "$(peek_next_command)" in squash|s) - EDIT_COMMIT= USE_OUTPUT=output MSG_OPT=-F - MSG_FILE="$MSG" + EDIT_OR_FILE="$MSG" cp "$MSG" "$SQUASH_MSG" ;; *) - EDIT_COMMIT=-e USE_OUTPUT= MSG_OPT= - MSG_FILE= + EDIT_OR_FILE=-e rm -f "$SQUASH_MSG" || exit cp "$MSG" "$GIT_DIR"/SQUASH_MSG rm -f "$GIT_DIR"/MERGE_MSG || exit @@ -397,7 +395,8 @@ do_next () { GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ - $USE_OUTPUT git commit --no-verify $MSG_OPT "$MSG_FILE" $EDIT_COMMIT || failed=t + $USE_OUTPUT git commit --no-verify \ + $MSG_OPT "$EDIT_OR_FILE" || failed=t fi if test $failed = t then @@ -456,7 +455,7 @@ get_saved_options () { test -d "$REWRITTEN" && PRESERVE_MERGES=t test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" test -f "$DOTEST"/verbose && VERBOSE=t - test ! -s "$DOTEST"/upstream && REBASE_ROOT=t + test -f "$DOTEST"/rebase-root && REBASE_ROOT=t } while test $# != 0 @@ -571,7 +570,8 @@ first and then run 'git rebase --continue' again." ;; --) shift - test ! -z "$REBASE_ROOT" -o $# -eq 1 -o $# -eq 2 || usage + test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 || + test ! -z "$REBASE_ROOT" -a $# -le 1 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@ -585,6 +585,7 @@ first and then run 'git rebase --continue' again." test -z "$ONTO" && ONTO=$UPSTREAM shift else + UPSTREAM= UPSTREAM_ARG=--root test -z "$ONTO" && die "You must specify --onto when using --root" @@ -611,7 +612,12 @@ first and then run 'git rebase --continue' again." echo "detached HEAD" > "$DOTEST"/head-name echo $HEAD > "$DOTEST"/head - echo $UPSTREAM > "$DOTEST"/upstream + case "$REBASE_ROOT" in + '') + rm -f "$DOTEST"/rebase-root ;; + *) + : >"$DOTEST"/rebase-root ;; + esac echo $ONTO > "$DOTEST"/onto test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy test t = "$VERBOSE" && : > "$DOTEST"/verbose diff --git a/git-svn.perl b/git-svn.perl index d4cb538b93..79888a05c4 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -70,7 +70,8 @@ my ($_stdin, $_help, $_edit, $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, - 'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache ); + 'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache, + 'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex ); my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'authors-file|A=s' => \$_authors, 'repack:i' => \$Git::SVN::_repack, @@ -3245,6 +3246,7 @@ use warnings; use Carp qw/croak/; use File::Temp qw/tempfile/; use IO::File qw//; +use vars qw/$_ignore_regex/; # file baton members: path, mode_a, mode_b, pool, fh, blob, base sub new { @@ -3297,6 +3299,15 @@ sub in_dot_git { $_[0] =~ m{(?:^|/)\.git(?:/|$)}; } +# return value: 0 -- don't ignore, 1 -- ignore +sub is_path_ignored { + my ($path) = @_; + return 1 if in_dot_git($path); + return 0 unless defined($_ignore_regex); + return 1 if $path =~ m!$_ignore_regex!o; + return 0; +} + sub set_path_strip { my ($self, $path) = @_; $self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path; @@ -3322,7 +3333,7 @@ sub git_path { sub delete_entry { my ($self, $path, $rev, $pb) = @_; - return undef if in_dot_git($path); + return undef if is_path_ignored($path); my $gpath = $self->git_path($path); return undef if ($gpath eq ''); @@ -3352,7 +3363,7 @@ sub open_file { my ($self, $path, $pb, $rev) = @_; my ($mode, $blob); - goto out if in_dot_git($path); + goto out if is_path_ignored($path); my $gpath = $self->git_path($path); ($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath) @@ -3372,7 +3383,7 @@ sub add_file { my ($self, $path, $pb, $cp_path, $cp_rev) = @_; my $mode; - if (!in_dot_git($path)) { + if (!is_path_ignored($path)) { my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#); delete $self->{empty}->{$dir}; $mode = '100644'; @@ -3383,7 +3394,7 @@ sub add_file { sub add_directory { my ($self, $path, $cp_path, $cp_rev) = @_; - goto out if in_dot_git($path); + goto out if is_path_ignored($path); my $gpath = $self->git_path($path); if ($gpath eq '') { my ($ls, $ctx) = command_output_pipe(qw/ls-tree @@ -3407,7 +3418,7 @@ out: sub change_dir_prop { my ($self, $db, $prop, $value) = @_; - return undef if in_dot_git($db->{path}); + return undef if is_path_ignored($db->{path}); $self->{dir_prop}->{$db->{path}} ||= {}; $self->{dir_prop}->{$db->{path}}->{$prop} = $value; undef; @@ -3415,7 +3426,7 @@ sub change_dir_prop { sub absent_directory { my ($self, $path, $pb) = @_; - return undef if in_dot_git($pb->{path}); + return undef if is_path_ignored($path); $self->{absent_dir}->{$pb->{path}} ||= []; push @{$self->{absent_dir}->{$pb->{path}}}, $path; undef; @@ -3423,7 +3434,7 @@ sub absent_directory { sub absent_file { my ($self, $path, $pb) = @_; - return undef if in_dot_git($pb->{path}); + return undef if is_path_ignored($path); $self->{absent_file}->{$pb->{path}} ||= []; push @{$self->{absent_file}->{$pb->{path}}}, $path; undef; @@ -3431,7 +3442,7 @@ sub absent_file { sub change_file_prop { my ($self, $fb, $prop, $value) = @_; - return undef if in_dot_git($fb->{path}); + return undef if is_path_ignored($fb->{path}); if ($prop eq 'svn:executable') { if ($fb->{mode_b} != 120000) { $fb->{mode_b} = defined $value ? 100755 : 100644; @@ -3447,7 +3458,7 @@ sub change_file_prop { sub apply_textdelta { my ($self, $fb, $exp) = @_; - return undef if (in_dot_git($fb->{path})); + return undef if is_path_ignored($fb->{path}); my $fh = $::_repository->temp_acquire('svn_delta'); # $fh gets auto-closed() by SVN::TxDelta::apply(), # (but $base does not,) so dup() it for reading in close_file @@ -3494,7 +3505,7 @@ sub apply_textdelta { sub close_file { my ($self, $fb, $exp) = @_; - return undef if (in_dot_git($fb->{path})); + return undef if is_path_ignored($fb->{path}); my $hash; my $path = $self->git_path($fb->{path}); @@ -4021,7 +4032,8 @@ my ($ra_invalid, $can_do_switch, %ignored_err, $RA); BEGIN { # enforce temporary pool usage for some simple functions no strict 'refs'; - for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) { + for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root + get_file/) { my $SUPER = "SUPER::$f"; *$f = sub { my $self = shift; @@ -2,6 +2,7 @@ #include "exec_cmd.h" #include "cache.h" #include "quote.h" +#include "run-command.h" const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; @@ -219,7 +220,7 @@ struct cmd_struct { int option; }; -static int run_command(struct cmd_struct *p, int argc, const char **argv) +static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@ -384,7 +385,7 @@ static void handle_internal_command(int argc, const char **argv) struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) continue; - exit(run_command(p, argc, argv)); + exit(run_builtin(p, argc, argv)); } } @@ -392,6 +393,7 @@ static void execv_dashed_external(const char **argv) { struct strbuf cmd = STRBUF_INIT; const char *tmp; + int status; strbuf_addf(&cmd, "git-%s", argv[0]); @@ -406,10 +408,17 @@ static void execv_dashed_external(const char **argv) trace_argv_printf(argv, "trace: exec:"); - /* execvp() can only ever return if it fails */ - execvp(cmd.buf, (char **)argv); - - trace_printf("trace: exec failed: %s\n", strerror(errno)); + /* + * if we fail because the command is not found, it is + * OK to return. Otherwise, we just pass along the status code. + */ + status = run_command_v_opt(argv, 0); + if (status != -ERR_RUN_COMMAND_EXEC) { + if (IS_RUN_COMMAND_ERR(status)) + die("unable to run '%s'", argv[0]); + exit(-status); + } + errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; @@ -442,21 +451,11 @@ static int run_argv(int *argcp, const char ***argv) int main(int argc, const char **argv) { - const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help"; - char *slash = (char *)cmd + strlen(cmd); + const char *cmd; - /* - * Take the basename of argv[0] as the command - * name, and the dirname as the default exec_path - * if we don't have anything better. - */ - while (cmd <= slash && !is_dir_sep(*slash)) - slash--; - if (cmd <= slash) { - *slash++ = 0; - git_set_argv0_path(cmd); - cmd = slash; - } + cmd = git_extract_argv0_path(argv[0]); + if (!cmd) + cmd = "git-help"; /* * "git-xxxx" is the same as "git xxxx", but we obviously: diff --git a/gitweb/README b/gitweb/README index 825162a0b6..a9dc2e57d9 100644 --- a/gitweb/README +++ b/gitweb/README @@ -162,14 +162,12 @@ not include variables usually directly set during build): $GITWEB_LIST during installation. If empty, $projectroot is used to scan for repositories. * $my_url, $my_uri - URL and absolute URL of gitweb script; you might need to set those - variables if you are using 'pathinfo' feature: see also below. + Full URL and absolute URL of gitweb script; + in earlier versions of gitweb you might have need to set those + variables, now there should be no need to do it. * $home_link Target of the home link on top of all pages (the first part of view - "breadcrumbs"). By default set to absolute URI of a page; you might - need to set it up to [base] gitweb URI if you use 'pathinfo' feature - (alternative format of the URLs, with project name embedded directly - in the path part of URL). + "breadcrumbs"). By default set to absolute URI of a page ($my_uri). * @stylesheets List of URIs of stylesheets (relative to base URI of a page). You might specify more than one stylesheet, for example use gitweb.css @@ -322,6 +320,82 @@ something like the following in your gitweb.conf (or gitweb_config.perl) file: $home_link = "/"; +PATH_INFO usage +----------------------- +If you enable PATH_INFO usage in gitweb by putting + + $feature{'pathinfo'}{'default'} = [1]; + +in your gitweb.conf, it is possible to set up your server so that it +consumes and produces URLs in the form + +http://git.example.com/project.git/shortlog/sometag + +by using a configuration such as the following, that assumes that +/var/www/gitweb is the DocumentRoot of your webserver, and that it +contains the gitweb.cgi script and complementary static files +(stylesheet, favicon): + +<VirtualHost *:80> + ServerAlias git.example.com + + DocumentRoot /var/www/gitweb + + <Directory /var/www/gitweb> + Options ExecCGI + AddHandler cgi-script cgi + + DirectoryIndex gitweb.cgi + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^.* /gitweb.cgi/$0 [L,PT] + </Directory> +</VirtualHost> + +The rewrite rule guarantees that existing static files will be properly +served, whereas any other URL will be passed to gitweb as PATH_INFO +parameter. + +Notice that in this case you don't need special settings for +@stylesheets, $my_uri and $home_link, but you lose "dumb client" access +to your project .git dirs. A possible workaround for the latter is the +following: in your project root dir (e.g. /pub/git) have the projects +named without a .git extension (e.g. /pub/git/project instead of +/pub/git/project.git) and configure Apache as follows: + +<VirtualHost *:80> + ServerAlias git.example.com + + DocumentRoot /var/www/gitweb + + AliasMatch ^(/.*?)(\.git)(/.*)? /pub/git$1$3 + <Directory /var/www/gitweb> + Options ExecCGI + AddHandler cgi-script cgi + + DirectoryIndex gitweb.cgi + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^.* /gitweb.cgi/$0 [L,PT] + </Directory> +</VirtualHost> + +The additional AliasMatch makes it so that + +http://git.example.com/project.git + +will give raw access to the project's git dir (so that the project can +be cloned), while + +http://git.example.com/project + +will provide human-friendly gitweb access. + + Originally written by: Kay Sievers <kay.sievers@vrfy.org> diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 931db4f7eb..f27dbb6bf4 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2901,9 +2901,14 @@ sub git_header_html { <meta name="robots" content="index, nofollow"/> <title>$title</title> EOF -# print out each stylesheet that exist + # the stylesheet, favicon etc urls won't work correctly with path_info + # unless we set the appropriate base URL + if ($ENV{'PATH_INFO'}) { + print '<base href="'.esc_url($my_url).'" />\n'; + } + # print out each stylesheet that exist, providing backwards capability + # for those people who defined $stylesheet in a config file if (defined $stylesheet) { -#provides backwards capability for those people who define style sheet in a config file print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n"; } else { foreach my $stylesheet (@stylesheets) { @@ -6015,7 +6020,25 @@ sub git_feed { } if (defined($commitlist[0])) { %latest_commit = %{$commitlist[0]}; - %latest_date = parse_date($latest_commit{'author_epoch'}); + my $latest_epoch = $latest_commit{'committer_epoch'}; + %latest_date = parse_date($latest_epoch); + my $if_modified = $cgi->http('IF_MODIFIED_SINCE'); + if (defined $if_modified) { + my $since; + if (eval { require HTTP::Date; 1; }) { + $since = HTTP::Date::str2time($if_modified); + } elsif (eval { require Time::ParseDate; 1; }) { + $since = Time::ParseDate::parsedate($if_modified, GMT => 1); + } + if (defined $since && $latest_epoch <= $since) { + print $cgi->header( + -type => $content_type, + -charset => 'utf-8', + -last_modified => $latest_date{'rfc2822'}, + -status => '304 Not Modified'); + return; + } + } print $cgi->header( -type => $content_type, -charset => 'utf-8', @@ -6074,7 +6097,24 @@ XML print "<title>$title</title>\n" . "<link>$alt_url</link>\n" . "<description>$descr</description>\n" . - "<language>en</language>\n"; + "<language>en</language>\n" . + # project owner is responsible for 'editorial' content + "<managingEditor>$owner</managingEditor>\n"; + if (defined $logo || defined $favicon) { + # prefer the logo to the favicon, since RSS + # doesn't allow both + my $img = esc_url($logo || $favicon); + print "<image>\n" . + "<url>$img</url>\n" . + "<title>$title</title>\n" . + "<link>$alt_url</link>\n" . + "</image>\n"; + } + if (%latest_date) { + print "<pubDate>$latest_date{'rfc2822'}</pubDate>\n"; + print "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n"; + } + print "<generator>gitweb v.$version/$git_version</generator>\n"; } elsif ($format eq 'atom') { print <<XML; <feed xmlns="http://www.w3.org/2005/Atom"> @@ -6101,6 +6141,7 @@ XML } else { print "<updated>$latest_date{'iso-8601'}</updated>\n"; } + print "<generator version='$version/$git_version'>gitweb</generator>\n"; } # contents diff --git a/hash-object.c b/hash-object.c index 846e91a231..37e66779ab 100644 --- a/hash-object.c +++ b/hash-object.c @@ -8,6 +8,7 @@ #include "blob.h" #include "quote.h" #include "parse-options.h" +#include "exec_cmd.h" static void hash_fd(int fd, const char *type, int write_object, const char *path) { @@ -81,6 +82,8 @@ int main(int argc, const char **argv) type = blob_type; + git_extract_argv0_path(argv[0]); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0); diff --git a/http-push.c b/http-push.c index eca4a8e5e9..18d81acd1b 100644 --- a/http-push.c +++ b/http-push.c @@ -10,6 +10,7 @@ #include "exec_cmd.h" #include "remote.h" #include "list-objects.h" +#include "sigchain.h" #include <expat.h> @@ -184,21 +185,22 @@ enum dav_header_flag { DAV_HEADER_TIMEOUT = (1u << 2) }; -static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options) { +static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options) +{ struct strbuf buf = STRBUF_INIT; struct curl_slist *dav_headers = NULL; - if(options & DAV_HEADER_IF) { + if (options & DAV_HEADER_IF) { strbuf_addf(&buf, "If: (<%s>)", lock->token); dav_headers = curl_slist_append(dav_headers, buf.buf); strbuf_reset(&buf); } - if(options & DAV_HEADER_LOCK) { + if (options & DAV_HEADER_LOCK) { strbuf_addf(&buf, "Lock-Token: <%s>", lock->token); dav_headers = curl_slist_append(dav_headers, buf.buf); strbuf_reset(&buf); } - if(options & DAV_HEADER_TIMEOUT) { + if (options & DAV_HEADER_TIMEOUT) { strbuf_addf(&buf, "Timeout: Second-%ld", lock->timeout); dav_headers = curl_slist_append(dav_headers, buf.buf); strbuf_reset(&buf); @@ -1383,7 +1385,7 @@ static void remove_locks(void) static void remove_locks_on_signal(int signo) { remove_locks(); - signal(signo, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -2194,6 +2196,8 @@ int main(int argc, char **argv) struct ref *ref; char *rewritten_url = NULL; + git_extract_argv0_path(argv[0]); + setup_git_directory(); remote = xcalloc(sizeof(*remote), 1); @@ -2276,10 +2280,7 @@ int main(int argc, char **argv) goto cleanup; } - signal(SIGINT, remove_locks_on_signal); - signal(SIGHUP, remove_locks_on_signal); - signal(SIGQUIT, remove_locks_on_signal); - signal(SIGTERM, remove_locks_on_signal); + sigchain_push_common(remove_locks_on_signal); /* Check whether the remote has server info files */ remote->can_update_info_refs = 0; diff --git a/imap-send.c b/imap-send.c index c3fa0df855..f91293c23f 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,6 +23,7 @@ */ #include "cache.h" +#include "exec_cmd.h" #ifdef NO_OPENSSL typedef void *SSL; #endif @@ -1389,6 +1390,8 @@ int main(int argc, char **argv) int total, n = 0; int nongit_ok; + git_extract_argv0_path(argv[0]); + /* init the random number generator */ arc4_init(); diff --git a/index-pack.c b/index-pack.c index b46a6d6597..f7a38079e1 100644 --- a/index-pack.c +++ b/index-pack.c @@ -8,6 +8,7 @@ #include "tree.h" #include "progress.h" #include "fsck.h" +#include "exec_cmd.h" static const char index_pack_usage[] = "git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }"; @@ -880,6 +881,8 @@ int main(int argc, char **argv) struct pack_idx_entry **idx_objects; unsigned char pack_sha1[20]; + git_extract_argv0_path(argv[0]); + /* * We wish to read the repository's config file if any, and * for that it is necessary to call setup_git_directory_gently(). diff --git a/lockfile.c b/lockfile.c index 8589155532..021c3375c1 100644 --- a/lockfile.c +++ b/lockfile.c @@ -2,6 +2,7 @@ * Copyright (c) 2005, Junio C Hamano */ #include "cache.h" +#include "sigchain.h" static struct lock_file *lock_file_list; static const char *alternate_index_output; @@ -24,7 +25,7 @@ static void remove_lock_file(void) static void remove_lock_file_on_signal(int signo) { remove_lock_file(); - signal(signo, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -136,11 +137,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { if (!lock_file_list) { - signal(SIGINT, remove_lock_file_on_signal); - signal(SIGHUP, remove_lock_file_on_signal); - signal(SIGTERM, remove_lock_file_on_signal); - signal(SIGQUIT, remove_lock_file_on_signal); - signal(SIGPIPE, remove_lock_file_on_signal); + sigchain_push_common(remove_lock_file_on_signal); atexit(remove_lock_file); } lk->owner = getpid(); diff --git a/merge-index.c b/merge-index.c index 7827e87a92..c00a2b385a 100644 --- a/merge-index.c +++ b/merge-index.c @@ -1,5 +1,6 @@ #include "cache.h" #include "run-command.h" +#include "exec_cmd.h" static const char *pgm; static const char *arguments[9]; @@ -93,6 +94,8 @@ int main(int argc, char **argv) if (argc < 3) usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)"); + git_extract_argv0_path(argv[0]); + setup_git_directory(); read_cache(); diff --git a/merge-tree.c b/merge-tree.c index 2d1413efbb..f18201acdb 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -2,6 +2,7 @@ #include "tree-walk.h" #include "xdiff-interface.h" #include "blob.h" +#include "exec_cmd.h" static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>"; static int resolve_directories = 1; @@ -344,6 +345,8 @@ int main(int argc, char **argv) if (argc != 4) usage(merge_tree_usage); + git_extract_argv0_path(argv[0]); + setup_git_directory(); buf1 = get_tree_descriptor(t+0, argv[1]); @@ -1,5 +1,6 @@ #include "cache.h" #include "tag.h" +#include "exec_cmd.h" /* * A signature file has a very simple fixed format: four lines @@ -159,6 +160,8 @@ int main(int argc, char **argv) if (argc != 1) usage("git-mktag < signaturefile"); + git_extract_argv0_path(argv[0]); + setup_git_directory(); if (strbuf_read(&buf, 0, 4096) < 0) { @@ -6,6 +6,7 @@ #include "cache.h" #include "quote.h" #include "tree.h" +#include "exec_cmd.h" static struct treeent { unsigned mode; @@ -70,6 +71,8 @@ int main(int ac, char **av) unsigned char sha1[20]; int line_termination = '\n'; + git_extract_argv0_path(av[0]); + setup_git_directory(); while ((1 < ac) && av[1][0] == '-') { @@ -268,3 +268,22 @@ void add_object_array_with_mode(struct object *obj, const char *name, struct obj objects[nr].mode = mode; array->nr = ++nr; } + +void object_array_remove_duplicates(struct object_array *array) +{ + int ref, src, dst; + struct object_array_entry *objects = array->objects; + + for (ref = 0; ref < array->nr - 1; ref++) { + for (src = ref + 1, dst = src; + src < array->nr; + src++) { + if (!strcmp(objects[ref].name, objects[src].name)) + continue; + if (src != dst) + objects[dst] = objects[src]; + dst++; + } + array->nr = dst; + } +} @@ -82,5 +82,6 @@ int object_list_contains(struct object_list *list, struct object *obj); /* Object array handling .. */ void add_object_array(struct object *obj, const char *name, struct object_array *array); void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode); +void object_array_remove_duplicates(struct object_array *); #endif /* OBJECT_H */ diff --git a/pack-redundant.c b/pack-redundant.c index e93eb966e2..48a12bc135 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -7,6 +7,7 @@ */ #include "cache.h" +#include "exec_cmd.h" #define BLKSIZE 512 @@ -601,6 +602,8 @@ int main(int argc, char **argv) unsigned char *sha1; char buf[42]; /* 40 byte sha1 + \n + \0 */ + git_extract_argv0_path(argv[0]); + setup_git_directory(); for (i = 1; i < argc; i++) { @@ -1,5 +1,6 @@ #include "cache.h" #include "run-command.h" +#include "sigchain.h" /* * This is split up from the rest of git so that we can do @@ -38,6 +39,13 @@ static void wait_for_pager(void) finish_command(&pager_process); } +static void wait_for_pager_signal(int signo) +{ + wait_for_pager(); + sigchain_pop(signo); + raise(signo); +} + void setup_pager(void) { const char *pager = getenv("GIT_PAGER"); @@ -75,6 +83,7 @@ void setup_pager(void) close(pager_process.in); /* this makes sure that the parent terminates after the pager */ + sigchain_push_common(wait_for_pager_signal); atexit(wait_for_pager); } diff --git a/parse-options.c b/parse-options.c index 9eb55cc8b5..4c5d09dd25 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1,6 +1,7 @@ #include "git-compat-util.h" #include "parse-options.h" #include "cache.h" +#include "commit.h" #define OPT_SHORT 1 #define OPT_UNSET 2 @@ -506,6 +507,22 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg, return 0; } +int parse_opt_with_commit(const struct option *opt, const char *arg, int unset) +{ + unsigned char sha1[20]; + struct commit *commit; + + if (!arg) + return -1; + if (get_sha1(arg, sha1)) + return error("malformed object name %s", arg); + commit = lookup_commit_reference(sha1); + if (!commit) + return error("no such commit %s", arg); + commit_list_insert(commit, opt->value); + return 0; +} + /* * This should really be OPTION_FILENAME type as a part of * parse_options that take prefix to do this while parsing. diff --git a/parse-options.h b/parse-options.h index 034162ec69..912290549b 100644 --- a/parse-options.h +++ b/parse-options.h @@ -151,6 +151,7 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx); extern int parse_opt_abbrev_cb(const struct option *, const char *, int); extern int parse_opt_approxidate_cb(const struct option *, const char *, int); extern int parse_opt_verbosity_cb(const struct option *, const char *, int); +extern int parse_opt_with_commit(const struct option *, const char *, int); #define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") #define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") diff --git a/patch-id.c b/patch-id.c index 871f1d20c0..3660ad461d 100644 --- a/patch-id.c +++ b/patch-id.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "exec_cmd.h" static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c) { @@ -79,6 +80,8 @@ int main(int argc, char **argv) if (argc != 1) usage(patch_id_usage); + git_extract_argv0_path(argv[0]); + generate_id_list(); return 0; } diff --git a/read-cache.c b/read-cache.c index b1475ffa09..940ec76fdf 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1574,6 +1574,26 @@ static void update_callback(struct diff_queue_struct *q, default: die("unexpected diff status %c", p->status); case DIFF_STATUS_UNMERGED: + /* + * ADD_CACHE_IGNORE_REMOVAL is unset if "git + * add -u" is calling us, In such a case, a + * missing work tree file needs to be removed + * if there is an unmerged entry at stage #2, + * but such a diff record is followed by + * another with DIFF_STATUS_DELETED (and if + * there is no stage #2, we won't see DELETED + * nor MODIFIED). We can simply continue + * either way. + */ + if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL)) + continue; + /* + * Otherwise, it is "git add path" is asking + * to explicitly add it; we fall through. A + * missing work tree file is an error and is + * caught by add_file_to_index() in such a + * case. + */ case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: if (add_file_to_index(&the_index, path, data->flags)) { @@ -1453,7 +1453,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * return 1; } -int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) +int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data) { const char *logfile; FILE *logfp; @@ -1464,6 +1464,16 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) logfp = fopen(logfile, "r"); if (!logfp) return -1; + + if (ofs) { + struct stat statbuf; + if (fstat(fileno(logfp), &statbuf) || + statbuf.st_size < ofs || + fseek(logfp, -ofs, SEEK_END) || + fgets(buf, sizeof(buf), logfp)) + return -1; + } + while (fgets(buf, sizeof(buf), logfp)) { unsigned char osha1[20], nsha1[20]; char *email_end, *message; @@ -1497,6 +1507,11 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) return ret; } +int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) +{ + return for_each_recent_reflog_ent(ref, fn, 0, cb_data); +} + static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data) { DIR *dir = opendir(git_path("logs/%s", base)); @@ -60,6 +60,7 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned /* iterate over reflog entries */ typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data); +int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data); /* * Calls the specified function for each reflog file until it returns nonzero, diff --git a/revision.c b/revision.c index db60f06c98..8603c14581 100644 --- a/revision.c +++ b/revision.c @@ -183,8 +183,11 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object if (!tag->tagged) die("bad tag"); object = parse_object(tag->tagged->sha1); - if (!object) + if (!object) { + if (flags & UNINTERESTING) + return NULL; die("bad object %s", sha1_to_hex(tag->tagged->sha1)); + } } /* @@ -479,9 +482,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, while (parent) { struct commit *p = parent->item; parent = parent->next; + if (p) + p->object.flags |= UNINTERESTING; if (parse_commit(p) < 0) - return -1; - p->object.flags |= UNINTERESTING; + continue; if (p->parents) mark_parents_uninteresting(p); if (p->object.flags & SEEN) @@ -1263,6 +1267,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (!strcmp(arg, "--all")) { handle_refs(revs, flags, for_each_ref); + handle_refs(revs, flags, head_ref); continue; } if (!strcmp(arg, "--branches")) { diff --git a/run-command.c b/run-command.c index db9ce59204..b05c734d05 100644 --- a/run-command.c +++ b/run-command.c @@ -118,7 +118,9 @@ int start_command(struct child_process *cmd) } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } - die("exec %s failed.", cmd->argv[0]); + trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0], + strerror(errno)); + exit(127); } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ @@ -187,6 +189,7 @@ int start_command(struct child_process *cmd) #endif if (cmd->pid < 0) { + int err = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@ -197,7 +200,9 @@ int start_command(struct child_process *cmd) close(cmd->out); if (need_err) close_pair(fderr); - return -ERR_RUN_COMMAND_FORK; + return err == ENOENT ? + -ERR_RUN_COMMAND_EXEC : + -ERR_RUN_COMMAND_FORK; } if (need_in) @@ -236,9 +241,14 @@ static int wait_or_whine(pid_t pid) if (!WIFEXITED(status)) return -ERR_RUN_COMMAND_WAITPID_NOEXIT; code = WEXITSTATUS(status); - if (code) + switch (code) { + case 127: + return -ERR_RUN_COMMAND_EXEC; + case 0: + return 0; + default: return -code; - return 0; + } } } diff --git a/run-command.h b/run-command.h index 0211e1d471..15e870a65e 100644 --- a/run-command.h +++ b/run-command.h @@ -10,6 +10,7 @@ enum { ERR_RUN_COMMAND_WAITPID_SIGNAL, ERR_RUN_COMMAND_WAITPID_NOEXIT, }; +#define IS_RUN_COMMAND_ERR(x) ((x) <= -ERR_RUN_COMMAND_FORK) struct child_process { const char **argv; diff --git a/sha1_file.c b/sha1_file.c index 360f7e5a02..8868b800cb 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2340,7 +2340,8 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, void *buf, unsigned long len, time_t mtime) { - int fd, size, ret; + int fd, ret; + size_t size; unsigned char *compressed; z_stream stream; char *filename; diff --git a/sha1_name.c b/sha1_name.c index 159c2ab84f..5d0ac0263d 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -238,8 +238,28 @@ static int ambiguous_path(const char *path, int len) return slash; } +/* + * *string and *len will only be substituted, and *string returned (for + * later free()ing) if the string passed in is of the form @{-<n>}. + */ +static char *substitute_nth_last_branch(const char **string, int *len) +{ + struct strbuf buf = STRBUF_INIT; + int ret = interpret_nth_last_branch(*string, &buf); + + if (ret == *len) { + size_t size; + *string = strbuf_detach(&buf, &size); + *len = size; + return (char *)*string; + } + + return NULL; +} + int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { + char *last_branch = substitute_nth_last_branch(&str, &len); const char **p, *r; int refs_found = 0; @@ -259,11 +279,13 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) break; } } + free(last_branch); return refs_found; } int dwim_log(const char *str, int len, unsigned char *sha1, char **log) { + char *last_branch = substitute_nth_last_branch(&str, &len); const char **p; int logs_found = 0; @@ -294,9 +316,12 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) if (!warn_ambiguous_refs) break; } + free(last_branch); return logs_found; } +static int get_sha1_1(const char *name, int len, unsigned char *sha1); + static int get_sha1_basic(const char *str, int len, unsigned char *sha1) { static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; @@ -307,10 +332,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (len == 40 && !get_sha1_hex(str, sha1)) return 0; - /* basic@{time or number} format to query ref-log */ + /* basic@{time or number or -number} format to query ref-log */ reflog_len = at = 0; - if (str[len-1] == '}') { - for (at = 0; at < len - 1; at++) { + if (len && str[len-1] == '}') { + for (at = len-2; at >= 0; at--) { if (str[at] == '@' && str[at+1] == '{') { reflog_len = (len-1) - (at+2); len = at; @@ -324,6 +349,16 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) return -1; if (!len && reflog_len) { + struct strbuf buf = STRBUF_INIT; + int ret; + /* try the @{-N} syntax for n-th checkout */ + ret = interpret_nth_last_branch(str+at, &buf); + if (ret > 0) { + /* substitute this branch name and restart */ + return get_sha1_1(buf.buf, buf.len, sha1); + } else if (ret == 0) { + return -1; + } /* allow "@{...}" to mean the current branch reflog */ refs_found = dwim_ref("HEAD", 4, sha1, &real_ref); } else if (reflog_len) @@ -379,8 +414,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) return 0; } -static int get_sha1_1(const char *name, int len, unsigned char *sha1); - static int get_parent(const char *name, int len, unsigned char *result, int idx) { @@ -674,6 +707,92 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) return retval; } +struct grab_nth_branch_switch_cbdata { + long cnt, alloc; + struct strbuf *buf; +}; + +static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) +{ + struct grab_nth_branch_switch_cbdata *cb = cb_data; + const char *match = NULL, *target = NULL; + size_t len; + int nth; + + if (!prefixcmp(message, "checkout: moving from ")) { + match = message + strlen("checkout: moving from "); + target = strstr(match, " to "); + } + + if (!match || !target) + return 0; + + len = target - match; + nth = cb->cnt++ % cb->alloc; + strbuf_reset(&cb->buf[nth]); + strbuf_add(&cb->buf[nth], match, len); + return 0; +} + +/* + * This reads "@{-N}" syntax, finds the name of the Nth previous + * branch we were on, and places the name of the branch in the given + * buf and returns the number of characters parsed if successful. + * + * If the input is not of the accepted format, it returns a negative + * number to signal an error. + * + * If the input was ok but there are not N branch switches in the + * reflog, it returns 0. + */ +int interpret_nth_last_branch(const char *name, struct strbuf *buf) +{ + long nth; + int i, retval; + struct grab_nth_branch_switch_cbdata cb; + const char *brace; + char *num_end; + + if (name[0] != '@' || name[1] != '{' || name[2] != '-') + return -1; + brace = strchr(name, '}'); + if (!brace) + return -1; + nth = strtol(name+3, &num_end, 10); + if (num_end != brace) + return -1; + if (nth <= 0) + return -1; + cb.alloc = nth; + cb.buf = xmalloc(nth * sizeof(struct strbuf)); + for (i = 0; i < nth; i++) + strbuf_init(&cb.buf[i], 20); + cb.cnt = 0; + retval = 0; + for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb); + if (cb.cnt < nth) { + cb.cnt = 0; + for (i = 0; i < nth; i++) + strbuf_release(&cb.buf[i]); + for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); + } + if (cb.cnt < nth) + goto release_return; + i = cb.cnt % nth; + strbuf_reset(buf); + strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len); + retval = brace-name+1; + +release_return: + for (i = 0; i < nth; i++) + strbuf_release(&cb.buf[i]); + free(cb.buf); + + return retval; +} + /* * This is like "get_sha1_basic()", except it allows "sha1 expressions", * notably "xyz^" for "parent of xyz" diff --git a/sigchain.c b/sigchain.c new file mode 100644 index 0000000000..1118b99e57 --- /dev/null +++ b/sigchain.c @@ -0,0 +1,52 @@ +#include "sigchain.h" +#include "cache.h" + +#define SIGCHAIN_MAX_SIGNALS 32 + +struct sigchain_signal { + sigchain_fun *old; + int n; + int alloc; +}; +static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; + +static void check_signum(int sig) +{ + if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) + die("BUG: signal out of range: %d", sig); +} + +int sigchain_push(int sig, sigchain_fun f) +{ + struct sigchain_signal *s = signals + sig; + check_signum(sig); + + ALLOC_GROW(s->old, s->n + 1, s->alloc); + s->old[s->n] = signal(sig, f); + if (s->old[s->n] == SIG_ERR) + return -1; + s->n++; + return 0; +} + +int sigchain_pop(int sig) +{ + struct sigchain_signal *s = signals + sig; + check_signum(sig); + if (s->n < 1) + return 0; + + if (signal(sig, s->old[s->n - 1]) == SIG_ERR) + return -1; + s->n--; + return 0; +} + +void sigchain_push_common(sigchain_fun f) +{ + sigchain_push(SIGINT, f); + sigchain_push(SIGHUP, f); + sigchain_push(SIGTERM, f); + sigchain_push(SIGQUIT, f); + sigchain_push(SIGPIPE, f); +} diff --git a/sigchain.h b/sigchain.h new file mode 100644 index 0000000000..618083bce0 --- /dev/null +++ b/sigchain.h @@ -0,0 +1,11 @@ +#ifndef SIGCHAIN_H +#define SIGCHAIN_H + +typedef void (*sigchain_fun)(int); + +int sigchain_push(int sig, sigchain_fun f); +int sigchain_pop(int sig); + +void sigchain_push_common(sigchain_fun f); + +#endif /* SIGCHAIN_H */ diff --git a/symlinks.c b/symlinks.c index 5a5e781a15..f262b7c44b 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,64 +1,241 @@ #include "cache.h" -struct pathname { +static struct cache_def { + char path[PATH_MAX + 1]; int len; - char path[PATH_MAX]; -}; + int flags; + int track_flags; + int prefix_len_stat_func; +} cache; -/* Return matching pathname prefix length, or zero if not matching */ -static inline int match_pathname(int len, const char *name, struct pathname *match) +/* + * Returns the length (on a path component basis) of the longest + * common prefix match of 'name' and the cached path string. + */ +static inline int longest_match_lstat_cache(int len, const char *name, + int *previous_slash) { - int match_len = match->len; - return (len > match_len && - name[match_len] == '/' && - !memcmp(name, match->path, match_len)) ? match_len : 0; + int max_len, match_len = 0, match_len_prev = 0, i = 0; + + max_len = len < cache.len ? len : cache.len; + while (i < max_len && name[i] == cache.path[i]) { + if (name[i] == '/') { + match_len_prev = match_len; + match_len = i; + } + i++; + } + /* Is the cached path string a substring of 'name'? */ + if (i == cache.len && cache.len < len && name[cache.len] == '/') { + match_len_prev = match_len; + match_len = cache.len; + /* Is 'name' a substring of the cached path string? */ + } else if ((i == len && len < cache.len && cache.path[len] == '/') || + (i == len && len == cache.len)) { + match_len_prev = match_len; + match_len = len; + } + *previous_slash = match_len_prev; + return match_len; } -static inline void set_pathname(int len, const char *name, struct pathname *match) +static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func) { - if (len < PATH_MAX) { - match->len = len; - memcpy(match->path, name, len); - match->path[len] = 0; - } + cache.path[0] = '\0'; + cache.len = 0; + cache.flags = 0; + cache.track_flags = track_flags; + cache.prefix_len_stat_func = prefix_len_stat_func; } -int has_symlink_leading_path(int len, const char *name) +#define FL_DIR (1 << 0) +#define FL_NOENT (1 << 1) +#define FL_SYMLINK (1 << 2) +#define FL_LSTATERR (1 << 3) +#define FL_ERR (1 << 4) +#define FL_FULLPATH (1 << 5) + +/* + * Check if name 'name' of length 'len' has a symlink leading + * component, or if the directory exists and is real, or not. + * + * To speed up the check, some information is allowed to be cached. + * This can be indicated by the 'track_flags' argument, which also can + * be used to indicate that we should check the full path. + * + * The 'prefix_len_stat_func' parameter can be used to set the length + * of the prefix, where the cache should use the stat() function + * instead of the lstat() function to test each path component. + */ +static int lstat_cache(int len, const char *name, + int track_flags, int prefix_len_stat_func) { - static struct pathname link, nonlink; - char path[PATH_MAX]; + int match_len, last_slash, last_slash_dir, previous_slash; + int match_flags, ret_flags, save_flags, max_len, ret; struct stat st; - char *sp; - int known_dir; - /* - * See if the last known symlink cache matches. - */ - if (match_pathname(len, name, &link)) - return 1; + if (cache.track_flags != track_flags || + cache.prefix_len_stat_func != prefix_len_stat_func) { + /* + * As a safeguard we clear the cache if the values of + * track_flags and/or prefix_len_stat_func does not + * match with the last supplied values. + */ + reset_lstat_cache(track_flags, prefix_len_stat_func); + match_len = last_slash = 0; + } else { + /* + * Check to see if we have a match from the cache for + * the 2 "excluding" path types. + */ + match_len = last_slash = + longest_match_lstat_cache(len, name, &previous_slash); + match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); + if (match_flags && match_len == cache.len) + return match_flags; + /* + * If we now have match_len > 0, we would know that + * the matched part will always be a directory. + * + * Also, if we are tracking directories and 'name' is + * a substring of the cache on a path component basis, + * we can return immediately. + */ + match_flags = track_flags & FL_DIR; + if (match_flags && len == match_len) + return match_flags; + } /* - * Get rid of the last known directory part + * Okay, no match from the cache so far, so now we have to + * check the rest of the path components. */ - known_dir = match_pathname(len, name, &nonlink); - - while ((sp = strchr(name + known_dir + 1, '/')) != NULL) { - int thislen = sp - name ; - memcpy(path, name, thislen); - path[thislen] = 0; - - if (lstat(path, &st)) - return 0; - if (S_ISDIR(st.st_mode)) { - set_pathname(thislen, path, &nonlink); - known_dir = thislen; + ret_flags = FL_DIR; + last_slash_dir = last_slash; + max_len = len < PATH_MAX ? len : PATH_MAX; + while (match_len < max_len) { + do { + cache.path[match_len] = name[match_len]; + match_len++; + } while (match_len < max_len && name[match_len] != '/'); + if (match_len >= max_len && !(track_flags & FL_FULLPATH)) + break; + last_slash = match_len; + cache.path[last_slash] = '\0'; + + if (last_slash <= prefix_len_stat_func) + ret = stat(cache.path, &st); + else + ret = lstat(cache.path, &st); + + if (ret) { + ret_flags = FL_LSTATERR; + if (errno == ENOENT) + ret_flags |= FL_NOENT; + } else if (S_ISDIR(st.st_mode)) { + last_slash_dir = last_slash; continue; - } - if (S_ISLNK(st.st_mode)) { - set_pathname(thislen, path, &link); - return 1; + } else if (S_ISLNK(st.st_mode)) { + ret_flags = FL_SYMLINK; + } else { + ret_flags = FL_ERR; } break; } - return 0; + + /* + * At the end update the cache. Note that max 3 different + * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached + * for the moment! + */ + save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK); + if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) { + cache.path[last_slash] = '\0'; + cache.len = last_slash; + cache.flags = save_flags; + } else if (track_flags & FL_DIR && + last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { + /* + * We have a separate test for the directory case, + * since it could be that we have found a symlink or a + * non-existing directory and the track_flags says + * that we cannot cache this fact, so the cache would + * then have been left empty in this case. + * + * But if we are allowed to track real directories, we + * can still cache the path components before the last + * one (the found symlink or non-existing component). + */ + cache.path[last_slash_dir] = '\0'; + cache.len = last_slash_dir; + cache.flags = FL_DIR; + } else { + reset_lstat_cache(track_flags, prefix_len_stat_func); + } + return ret_flags; +} + +/* + * Invalidate the given 'name' from the cache, if 'name' matches + * completely with the cache. + */ +void invalidate_lstat_cache(int len, const char *name) +{ + int match_len, previous_slash; + + match_len = longest_match_lstat_cache(len, name, &previous_slash); + if (len == match_len) { + if ((cache.track_flags & FL_DIR) && previous_slash > 0) { + cache.path[previous_slash] = '\0'; + cache.len = previous_slash; + cache.flags = FL_DIR; + } else + reset_lstat_cache(cache.track_flags, + cache.prefix_len_stat_func); + } +} + +/* + * Completely clear the contents of the cache + */ +void clear_lstat_cache(void) +{ + reset_lstat_cache(0, 0); +} + +#define USE_ONLY_LSTAT 0 + +/* + * Return non-zero if path 'name' has a leading symlink component + */ +int has_symlink_leading_path(int len, const char *name) +{ + return lstat_cache(len, name, + FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & + FL_SYMLINK; +} + +/* + * Return non-zero if path 'name' has a leading symlink component or + * if some leading path component does not exists. + */ +int has_symlink_or_noent_leading_path(int len, const char *name) +{ + return lstat_cache(len, name, + FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & + (FL_SYMLINK|FL_NOENT); +} + +/* + * Return non-zero if all path components of 'name' exists as a + * directory. If prefix_len > 0, we will test with the stat() + * function instead of the lstat() function for a prefix length of + * 'prefix_len', thus we then allow for symlinks in the prefix part as + * long as those points to real existing directories. + */ +int has_dirs_only_path(int len, const char *name, int prefix_len) +{ + return lstat_cache(len, name, + FL_DIR|FL_FULLPATH, prefix_len) & + FL_DIR; } @@ -212,6 +212,24 @@ library for your script to use. is to summarize successes and failures in the test script and exit with an appropriate error code. + - test_tick + + Make commit and tag names consistent by setting the author and + committer times to defined stated. Subsequent calls will + advance the times by a fixed amount. + + - test_commit <message> [<filename> [<contents>]] + + Creates a commit with the given message, committing the given + file with the given contents (default for both is to reuse the + message string), and adds a tag (again reusing the message + string as name). Calls test_tick to make the SHA-1s + reproducible. + + - test_merge <message> <commit-or-tag> + + Merges the given rev using the given message. Like test_commit, + creates a tag and calls test_tick before committing. Tips for Writing Tests ---------------------- diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh new file mode 100644 index 0000000000..260a231933 --- /dev/null +++ b/t/lib-rebase.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# After setting the fake editor with this function, you can +# +# - override the commit message with $FAKE_COMMIT_MESSAGE, +# - amend the commit message with $FAKE_COMMIT_AMEND +# - check that non-commit messages have a certain line count with $EXPECT_COUNT +# - rewrite a rebase -i script with $FAKE_LINES in the form +# +# "[<lineno1>] [<lineno2>]..." +# +# If a line number is prefixed with "squash" or "edit", the respective line's +# command will be replaced with the specified one. + +set_fake_editor () { + echo "#!$SHELL_PATH" >fake-editor.sh + cat >> fake-editor.sh <<\EOF +case "$1" in +*/COMMIT_EDITMSG) + test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" + test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" + exit + ;; +esac +test -z "$EXPECT_COUNT" || + test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || + exit +test -z "$FAKE_LINES" && exit +grep -v '^#' < "$1" > "$1".tmp +rm -f "$1" +cat "$1".tmp +action=pick +for line in $FAKE_LINES; do + case $line in + squash|edit) + action="$line";; + *) + echo sed -n "${line}s/^pick/$action/p" + sed -n "${line}p" < "$1".tmp + sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" + action=pick;; + esac +done +EOF + + test_set_editor "$(pwd)/fake-editor.sh" + chmod a+x fake-editor.sh +} diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh new file mode 100755 index 0000000000..09f855af3e --- /dev/null +++ b/t/t0005-signals.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +test_description='signals work as we expect' +. ./test-lib.sh + +cat >expect <<EOF +three +two +one +EOF + +test_expect_success 'sigchain works' ' + test-sigchain >actual + case "$?" in + 143) true ;; # POSIX w/ SIGTERM=15 + 3) true ;; # Windows + *) false ;; + esac && + test_cmp expect actual +' + +test_done diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh new file mode 100755 index 0000000000..d709ecf8df --- /dev/null +++ b/t/t1505-rev-parse-last.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +test_description='test @{-N} syntax' + +. ./test-lib.sh + + +make_commit () { + echo "$1" > "$1" && + git add "$1" && + git commit -m "$1" +} + + +test_expect_success 'setup' ' + + make_commit 1 && + git branch side && + make_commit 2 && + make_commit 3 && + git checkout side && + make_commit 4 && + git merge master && + git checkout master + +' + +# 1 -- 2 -- 3 master +# \ \ +# \ \ +# --- 4 --- 5 side +# +# and 'side' should be the last branch + +test_rev_equivalent () { + + git rev-parse "$1" > expect && + git rev-parse "$2" > output && + test_cmp expect output + +} + +test_expect_success '@{-1} works' ' + test_rev_equivalent side @{-1} +' + +test_expect_success '@{-1}~2 works' ' + test_rev_equivalent side~2 @{-1}~2 +' + +test_expect_success '@{-1}^2 works' ' + test_rev_equivalent side^2 @{-1}^2 +' + +test_expect_success '@{-1}@{1} works' ' + test_rev_equivalent side@{1} @{-1}@{1} +' + +test_expect_success '@{-2} works' ' + test_rev_equivalent master @{-2} +' + +test_expect_success '@{-3} fails' ' + test_must_fail git rev-parse @{-3} +' + +test_done + + diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh new file mode 100755 index 0000000000..87b30a268c --- /dev/null +++ b/t/t2012-checkout-last.sh @@ -0,0 +1,94 @@ +#!/bin/sh + +test_description='checkout can switch to last branch' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo hello >world && + git add world && + git commit -m initial && + git branch other && + echo "hello again" >>world && + git add world && + git commit -m second +' + +test_expect_success '"checkout -" does not work initially' ' + test_must_fail git checkout - +' + +test_expect_success 'first branch switch' ' + git checkout other +' + +test_expect_success '"checkout -" switches back' ' + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master" +' + +test_expect_success '"checkout -" switches forth' ' + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other" +' + +test_expect_success 'detach HEAD' ' + git checkout $(git rev-parse HEAD) +' + +test_expect_success '"checkout -" attaches again' ' + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other" +' + +test_expect_success '"checkout -" detaches again' ' + git checkout - && + test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" && + test_must_fail git symbolic-ref HEAD +' + +test_expect_success 'more switches' ' + for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 + do + git checkout -b branch$i + done +' + +more_switches () { + for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 + do + git checkout branch$i + done +} + +test_expect_success 'switch to the last' ' + more_switches && + git checkout @{-1} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2" +' + +test_expect_success 'switch to second from the last' ' + more_switches && + git checkout @{-2} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3" +' + +test_expect_success 'switch to third from the last' ' + more_switches && + git checkout @{-3} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4" +' + +test_expect_success 'switch to fourth from the last' ' + more_switches && + git checkout @{-4} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5" +' + +test_expect_success 'switch to twelfth from the last' ' + more_switches && + git checkout @{-12} && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13" +' + +test_done diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index cd9231cf61..b2ddf5ace3 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -12,7 +12,7 @@ and issues a git add -u with path limiting on "dir" to add only the updates to dir/sub. Also tested are "git add -u" without limiting, and "git add -u" -without contents changes.' +without contents changes, and other conditions' . ./test-lib.sh @@ -128,4 +128,52 @@ test_expect_success 'add -n -u should not add but just report' ' ' +test_expect_success 'add -u resolves unmerged paths' ' + git reset --hard && + one=$(echo 1 | git hash-object -w --stdin) && + two=$(echo 2 | git hash-object -w --stdin) && + three=$(echo 3 | git hash-object -w --stdin) && + { + for path in path1 path2 + do + echo "100644 $one 1 $path" + echo "100644 $two 2 $path" + echo "100644 $three 3 $path" + done + echo "100644 $one 1 path3" + echo "100644 $one 1 path4" + echo "100644 $one 3 path5" + echo "100644 $one 3 path6" + } | + git update-index --index-info && + echo 3 >path1 && + echo 2 >path3 && + echo 2 >path5 && + git add -u && + git ls-files -s "path?" >actual && + { + echo "100644 $three 0 path1" + echo "100644 $one 1 path3" + echo "100644 $one 1 path4" + echo "100644 $one 3 path5" + echo "100644 $one 3 path6" + } >expect && + test_cmp expect actual && + + # Bonus tests. Explicit resolving + git add path3 path5 && + test_must_fail git add path4 && + test_must_fail git add path6 && + git rm path4 && + git rm path6 && + + git ls-files -s "path?" >actual && + { + echo "100644 $three 0 path1" + echo "100644 $two 0 path3" + echo "100644 $two 0 path5" + } >expect + +' + test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 2cc8e7abe1..603b003edf 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -10,6 +10,10 @@ that the result still makes sense. ' . ./test-lib.sh +. ../lib-rebase.sh + +set_fake_editor + # set up two branches like this: # # A - B - C - D - E @@ -61,39 +65,6 @@ test_expect_success 'setup' ' git tag I ' -echo "#!$SHELL_PATH" >fake-editor.sh -cat >> fake-editor.sh <<\EOF -case "$1" in -*/COMMIT_EDITMSG) - test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" - test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" - exit - ;; -esac -test -z "$EXPECT_COUNT" || - test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || - exit -test -z "$FAKE_LINES" && exit -grep -v '^#' < "$1" > "$1".tmp -rm -f "$1" -cat "$1".tmp -action=pick -for line in $FAKE_LINES; do - case $line in - squash|edit) - action="$line";; - *) - echo sed -n "${line}s/^pick/$action/p" - sed -n "${line}p" < "$1".tmp - sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" - action=pick;; - esac -done -EOF - -test_set_editor "$(pwd)/fake-editor.sh" -chmod a+x fake-editor.sh - test_expect_success 'no changes are a nop' ' git rebase -i F && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" && @@ -462,4 +433,30 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' ' ' +test_expect_success 'submodule rebase setup' ' + git checkout A && + mkdir sub && + ( + cd sub && git init && >elif && + git add elif && git commit -m "submodule initial" + ) && + echo 1 >file1 && + git add file1 sub + test_tick && + git commit -m "One" && + echo 2 >file1 && + test_tick && + git commit -a -m "Two" && + ( + cd sub && echo 3 >elif && + git commit -a -m "submodule second" + ) && + test_tick && + git commit -a -m "Three changes submodule" +' + +test_expect_success 'submodule rebase -i' ' + FAKE_LINES="1 squash 2 3" git rebase -i A +' + test_done diff --git a/t/t3409-rebase-hook.sh b/t/t3409-rebase-hook.sh index 1f1b850677..098b75507b 100755 --- a/t/t3409-rebase-hook.sh +++ b/t/t3409-rebase-hook.sh @@ -118,7 +118,11 @@ test_expect_success 'pre-rebase hook stops rebase (1)' ' test_expect_success 'pre-rebase hook stops rebase (2)' ' git checkout test && git reset --hard side && - EDITOR=true test_must_fail git rebase -i master && + ( + EDITOR=: + export EDITOR + test_must_fail git rebase -i master + ) && test "z$(git symbolic-ref HEAD)" = zrefs/heads/test && test 0 = $(git rev-list HEAD...side | wc -l) ' diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh index 5816415aaf..c49143a1a4 100755 --- a/t/t3410-rebase-preserve-dropped-merges.sh +++ b/t/t3410-rebase-preserve-dropped-merges.sh @@ -22,47 +22,17 @@ rewritten. # where B, D and G touch the same file. test_expect_success 'setup' ' - : > file1 && - git add file1 && - test_tick && - git commit -m A && - git tag A && - echo 1 > file1 && - test_tick && - git commit -m B file1 && - : > file2 && - git add file2 && - test_tick && - git commit -m C && - echo 2 > file1 && - test_tick && - git commit -m D file1 && - : > file3 && - git add file3 && - test_tick && - git commit -m E && - git tag E && - git checkout -b branch1 A && - : > file4 && - git add file4 && - test_tick && - git commit -m F && - git tag F && - echo 3 > file1 && - test_tick && - git commit -m G file1 && - git tag G && - : > file5 && - git add file5 && - test_tick && - git commit -m H && - git tag H && - git checkout -b branch2 F && - : > file6 && - git add file6 && - test_tick && - git commit -m I && - git tag I + test_commit A file1 && + test_commit B file1 1 && + test_commit C file2 && + test_commit D file1 2 && + test_commit E file3 && + git checkout A && + test_commit F file4 && + test_commit G file1 3 && + test_commit H file5 && + git checkout F && + test_commit I file6 ' # A - B - C - D - E @@ -72,68 +42,44 @@ test_expect_success 'setup' ' # I -- G2 -- J -- K I -- K # G2 = same changes as G test_expect_success 'skip same-resolution merges with -p' ' - git checkout branch1 && + git checkout H && ! git merge E && - echo 23 > file1 && - git add file1 && - git commit -m L && - git checkout branch2 && - echo 3 > file1 && - git commit -a -m G2 && + test_commit L file1 23 && + git checkout I && + test_commit G2 file1 3 && ! git merge E && - echo 23 > file1 && - git add file1 && - git commit -m J && - echo file7 > file7 && - git add file7 && - git commit -m K && - GIT_EDITOR=: git rebase -i -p branch1 && - test $(git rev-parse branch2^^) = $(git rev-parse branch1) && + test_commit J file1 23 && + test_commit K file7 file7 && + git rebase -i -p L && + test $(git rev-parse HEAD^^) = $(git rev-parse L) && test "23" = "$(cat file1)" && - test "" = "$(cat file6)" && - test "file7" = "$(cat file7)" && - - git checkout branch1 && - git reset --hard H && - git checkout branch2 && - git reset --hard I + test "I" = "$(cat file6)" && + test "file7" = "$(cat file7)" ' # A - B - C - D - E # \ \ \ -# F - G - H -- L \ --> L -# \ | \ -# I -- G2 -- J -- K I -- G2 -- K +# F - G - H -- L2 \ --> L2 +# \ | \ +# I -- G3 --- J2 -- K2 I -- G3 -- K2 # G2 = different changes as G test_expect_success 'keep different-resolution merges with -p' ' - git checkout branch1 && + git checkout H && ! git merge E && - echo 23 > file1 && - git add file1 && - git commit -m L && - git checkout branch2 && - echo 4 > file1 && - git commit -a -m G2 && + test_commit L2 file1 23 && + git checkout I && + test_commit G3 file1 4 && ! git merge E && - echo 24 > file1 && - git add file1 && - git commit -m J && - echo file7 > file7 && - git add file7 && - git commit -m K && - ! GIT_EDITOR=: git rebase -i -p branch1 && + test_commit J2 file1 24 && + test_commit K2 file7 file7 && + test_must_fail git rebase -i -p L2 && echo 234 > file1 && git add file1 && - GIT_EDITOR=: git rebase --continue && - test $(git rev-parse branch2^^^) = $(git rev-parse branch1) && + git rebase --continue && + test $(git rev-parse HEAD^^^) = $(git rev-parse L2) && test "234" = "$(cat file1)" && - test "" = "$(cat file6)" && - test "file7" = "$(cat file7)" && - - git checkout branch1 && - git reset --hard H && - git checkout branch2 && - git reset --hard I + test "I" = "$(cat file6)" && + test "file7" = "$(cat file7)" ' test_done diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index aacfaae843..6533505218 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -5,44 +5,14 @@ test_description='git rebase preserve merges -This test runs git rebase with and tries to squash a commit from after a merge -to before the merge. +This test runs git rebase with -p and tries to squash a commit from after +a merge to before the merge. ' . ./test-lib.sh -# Copy/paste from t3404-rebase-interactive.sh -echo "#!$SHELL_PATH" >fake-editor.sh -cat >> fake-editor.sh <<\EOF -case "$1" in -*/COMMIT_EDITMSG) - test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" - test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" - exit - ;; -esac -test -z "$EXPECT_COUNT" || - test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || - exit -test -z "$FAKE_LINES" && exit -grep -v '^#' < "$1" > "$1".tmp -rm -f "$1" -cat "$1".tmp -action=pick -for line in $FAKE_LINES; do - case $line in - squash|edit) - action="$line";; - *) - echo sed -n "${line}s/^pick/$action/p" - sed -n "${line}p" < "$1".tmp - sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" - action=pick;; - esac -done -EOF +. ../lib-rebase.sh -test_set_editor "$(pwd)/fake-editor.sh" -chmod a+x fake-editor.sh +set_fake_editor # set up two branches like this: # @@ -51,27 +21,13 @@ chmod a+x fake-editor.sh # -- C1 -- test_expect_success 'setup' ' - touch a && - touch b && - git add a && - git commit -m A1 && - git tag A1 - git add b && - git commit -m B1 && - git tag B1 && - git checkout -b branch && - touch c && - git add c && - git commit -m C1 && - git checkout master && - touch d && - git add d && - git commit -m D1 && - git merge branch && - touch f && - git add f && - git commit -m F1 && - git tag F1 + test_commit A1 && + test_commit B1 && + test_commit C1 && + git reset --hard B1 && + test_commit D1 && + test_merge E1 C1 && + test_commit F1 ' # Should result in: @@ -82,7 +38,7 @@ test_expect_success 'setup' ' # test_expect_success 'squash F1 into D1' ' FAKE_LINES="1 squash 3 2" git rebase -i -p B1 && - test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" && + test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" && git tag E2 ' @@ -100,32 +56,15 @@ test_expect_success 'squash F1 into D1' ' # And rebase G1..M1 onto E2 test_expect_success 'rebase two levels of merge' ' - git checkout -b branch2 A1 && - touch g && - git add g && - git commit -m G1 && - git checkout -b branch3 && - touch h - git add h && - git commit -m H1 && - git checkout -b branch4 && - touch i && - git add i && - git commit -m I1 && - git tag I1 && - git checkout branch3 && - touch j && - git add j && - git commit -m J1 && - git merge I1 --no-commit && - git commit -m K1 && - git tag K1 && - git checkout branch2 && - touch l && - git add l && - git commit -m L1 && - git merge K1 --no-commit && - git commit -m M1 && + test_commit G1 && + test_commit H1 && + test_commit I1 && + git checkout -b branch3 H1 && + test_commit J1 && + test_merge K1 I1 && + git checkout -b branch2 G1 && + test_commit L1 && + test_merge M1 K1 && GIT_EDITOR=: git rebase -i -p E2 && test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" && test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" && diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 6359580262..5869061c5b 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -6,24 +6,20 @@ Tests if git rebase --root --onto <newparent> can rebase the root commit. ' . ./test-lib.sh +log_with_names () { + git rev-list --topo-order --parents --pretty="tformat:%s" HEAD | + git name-rev --stdin --name-only --refs=refs/heads/$1 +} + + test_expect_success 'prepare repository' ' - echo 1 > A && - git add A && - git commit -m 1 && - echo 2 > A && - git add A && - git commit -m 2 && + test_commit 1 A && + test_commit 2 A && git symbolic-ref HEAD refs/heads/other && rm .git/index && - echo 3 > B && - git add B && - git commit -m 3 && - echo 1 > A && - git add A && - git commit -m 1b && - echo 4 > B && - git add B && - git commit -m 4 + test_commit 3 B && + test_commit 1b A 1 && + test_commit 4 B ' test_expect_success 'rebase --root expects --onto' ' @@ -69,7 +65,7 @@ test_expect_success 'pre-rebase got correct input (2)' ' test_expect_success 'rebase -i --root --onto <newbase>' ' git checkout -b work3 other && - GIT_EDITOR=: git rebase -i --root --onto master && + git rebase -i --root --onto master && git log --pretty=tformat:"%s" > rebased3 && test_cmp expect rebased3 ' @@ -80,7 +76,7 @@ test_expect_success 'pre-rebase got correct input (3)' ' test_expect_success 'rebase -i --root --onto <newbase> <branch>' ' git branch work4 other && - GIT_EDITOR=: git rebase -i --root --onto master work4 && + git rebase -i --root --onto master work4 && git log --pretty=tformat:"%s" > rebased4 && test_cmp expect rebased4 ' @@ -91,7 +87,7 @@ test_expect_success 'pre-rebase got correct input (4)' ' test_expect_success 'rebase -i -p with linear history' ' git checkout -b work5 other && - GIT_EDITOR=: git rebase -i -p --root --onto master && + git rebase -i -p --root --onto master && git log --pretty=tformat:"%s" > rebased5 && test_cmp expect rebased5 ' @@ -103,28 +99,30 @@ test_expect_success 'pre-rebase got correct input (5)' ' test_expect_success 'set up merge history' ' git checkout other^ && git checkout -b side && - echo 5 > C && - git add C && - git commit -m 5 && + test_commit 5 C && git checkout other && git merge side ' -sed 's/#/ /g' > expect-side <<'EOF' -* Merge branch 'side' into other -|\## -| * 5 -* | 4 -|/## -* 3 -* 2 -* 1 +cat > expect-side <<'EOF' +commit work6 work6~1 work6^2 +Merge branch 'side' into other +commit work6^2 work6~2 +5 +commit work6~1 work6~2 +4 +commit work6~2 work6~3 +3 +commit work6~3 work6~4 +2 +commit work6~4 +1 EOF test_expect_success 'rebase -i -p with merge' ' git checkout -b work6 other && - GIT_EDITOR=: git rebase -i -p --root --onto master && - git log --graph --topo-order --pretty=tformat:"%s" > rebased6 && + git rebase -i -p --root --onto master && + log_with_names work6 > rebased6 && test_cmp expect-side rebased6 ' @@ -132,32 +130,34 @@ test_expect_success 'set up second root and merge' ' git symbolic-ref HEAD refs/heads/third && rm .git/index && rm A B C && - echo 6 > D && - git add D && - git commit -m 6 && + test_commit 6 D && git checkout other && git merge third ' -sed 's/#/ /g' > expect-third <<'EOF' -* Merge branch 'third' into other -|\## -| * 6 -* | Merge branch 'side' into other -|\ \## -| * | 5 -* | | 4 -|/ /## -* | 3 -|/## -* 2 -* 1 +cat > expect-third <<'EOF' +commit work7 work7~1 work7^2 +Merge branch 'third' into other +commit work7^2 work7~4 +6 +commit work7~1 work7~2 work7~1^2 +Merge branch 'side' into other +commit work7~1^2 work7~3 +5 +commit work7~2 work7~3 +4 +commit work7~3 work7~4 +3 +commit work7~4 work7~5 +2 +commit work7~5 +1 EOF test_expect_success 'rebase -i -p with two roots' ' git checkout -b work7 other && - GIT_EDITOR=: git rebase -i -p --root --onto master && - git log --graph --topo-order --pretty=tformat:"%s" > rebased7 && + git rebase -i -p --root --onto master && + log_with_names work7 > rebased7 && test_cmp expect-third rebased7 ' @@ -172,16 +172,109 @@ EOF test_expect_success 'pre-rebase hook stops rebase' ' git checkout -b stops1 other && - GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test_must_fail git rebase --root --onto master && test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 test 0 = $(git rev-list other...stops1 | wc -l) ' test_expect_success 'pre-rebase hook stops rebase -i' ' git checkout -b stops2 other && - GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test_must_fail git rebase --root --onto master && test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 test 0 = $(git rev-list other...stops2 | wc -l) ' +test_expect_success 'remove pre-rebase hook' ' + rm -f .git/hooks/pre-rebase +' + +test_expect_success 'set up a conflict' ' + git checkout master && + echo conflict > B && + git add B && + git commit -m conflict +' + +test_expect_success 'rebase --root with conflict (first part)' ' + git checkout -b conflict1 other && + test_must_fail git rebase --root --onto master && + git ls-files -u | grep "B$" +' + +test_expect_success 'fix the conflict' ' + echo 3 > B && + git add B +' + +cat > expect-conflict <<EOF +6 +5 +4 +3 +conflict +2 +1 +EOF + +test_expect_success 'rebase --root with conflict (second part)' ' + git rebase --continue && + git log --pretty=tformat:"%s" > conflict1 && + test_cmp expect-conflict conflict1 +' + +test_expect_success 'rebase -i --root with conflict (first part)' ' + git checkout -b conflict2 other && + test_must_fail git rebase -i --root --onto master && + git ls-files -u | grep "B$" +' + +test_expect_success 'fix the conflict' ' + echo 3 > B && + git add B +' + +test_expect_success 'rebase -i --root with conflict (second part)' ' + git rebase --continue && + git log --pretty=tformat:"%s" > conflict2 && + test_cmp expect-conflict conflict2 +' + +cat >expect-conflict-p <<\EOF +commit conflict3 conflict3~1 conflict3^2 +Merge branch 'third' into other +commit conflict3^2 conflict3~4 +6 +commit conflict3~1 conflict3~2 conflict3~1^2 +Merge branch 'side' into other +commit conflict3~1^2 conflict3~3 +5 +commit conflict3~2 conflict3~3 +4 +commit conflict3~3 conflict3~4 +3 +commit conflict3~4 conflict3~5 +conflict +commit conflict3~5 conflict3~6 +2 +commit conflict3~6 +1 +EOF + +test_expect_success 'rebase -i -p --root with conflict (first part)' ' + git checkout -b conflict3 other && + test_must_fail git rebase -i -p --root --onto master && + git ls-files -u | grep "B$" +' + +test_expect_success 'fix the conflict' ' + echo 3 > B && + git add B +' + +test_expect_success 'rebase -i -p --root with conflict (second part)' ' + git rebase --continue && + log_with_names conflict3 >out && + test_cmp expect-conflict-p out +' + test_done diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh index 02efecae3a..9055c8b318 100755 --- a/t/t4011-diff-symlink.sh +++ b/t/t4011-diff-symlink.sh @@ -82,4 +82,11 @@ test_expect_success \ git diff-index -M -p $tree > current && compare_diff_patch current expected' +test_expect_success \ + 'diff symlinks with non-existing targets' \ + 'ln -s narf pinky && + ln -s take\ over brain && + test_must_fail git diff --no-index pinky brain > output 2> output.err && + grep narf output && + ! grep error output.err' test_done diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index 2f27a0ba9e..a3f0897a52 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -104,7 +104,7 @@ cat >expect.typechange <<'EOF' -1 diff --git a/file b/file new file mode 120000 -index ad8b3d2..67be421 +index 0000000..67be421 --- /dev/null +++ b/file @@ -0,0 +1 @@ diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh new file mode 100755 index 0000000000..4508effcaa --- /dev/null +++ b/t/t4034-diff-words.sh @@ -0,0 +1,200 @@ +#!/bin/sh + +test_description='word diff colors' + +. ./test-lib.sh + +test_expect_success setup ' + + git config diff.color.old red + git config diff.color.new green + +' + +decrypt_color () { + sed \ + -e 's/.\[1m/<WHITE>/g' \ + -e 's/.\[31m/<RED>/g' \ + -e 's/.\[32m/<GREEN>/g' \ + -e 's/.\[36m/<BROWN>/g' \ + -e 's/.\[m/<RESET>/g' +} + +word_diff () { + test_must_fail git diff --no-index "$@" pre post > output && + decrypt_color < output > output.decrypted && + test_cmp expect output.decrypted +} + +cat > pre <<\EOF +h(4) + +a = b + c +EOF + +cat > post <<\EOF +h(4),hh[44] + +a = b + c + +aa = a + +aeff = aeff * ( aaa ) +EOF + +cat > expect <<\EOF +<WHITE>diff --git a/pre b/post<RESET> +<WHITE>index 330b04f..5ed8eff 100644<RESET> +<WHITE>--- a/pre<RESET> +<WHITE>+++ b/post<RESET> +<BROWN>@@ -1,3 +1,7 @@<RESET> +<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET> +<RESET> +a = b + c<RESET> + +<GREEN>aa = a<RESET> + +<GREEN>aeff = aeff * ( aaa )<RESET> +EOF + +test_expect_success 'word diff with runs of whitespace' ' + + word_diff --color-words + +' + +cat > expect <<\EOF +<WHITE>diff --git a/pre b/post<RESET> +<WHITE>index 330b04f..5ed8eff 100644<RESET> +<WHITE>--- a/pre<RESET> +<WHITE>+++ b/post<RESET> +<BROWN>@@ -1,3 +1,7 @@<RESET> +h(4),<GREEN>hh<RESET>[44] +<RESET> +a = b + c<RESET> + +<GREEN>aa = a<RESET> + +<GREEN>aeff = aeff * ( aaa<RESET> ) +EOF +cp expect expect.letter-runs-are-words + +test_expect_success 'word diff with a regular expression' ' + + word_diff --color-words="[a-z]+" + +' + +test_expect_success 'set a diff driver' ' + git config diff.testdriver.wordRegex "[^[:space:]]" && + cat <<EOF > .gitattributes +pre diff=testdriver +post diff=testdriver +EOF +' + +test_expect_success 'option overrides .gitattributes' ' + + word_diff --color-words="[a-z]+" + +' + +cat > expect <<\EOF +<WHITE>diff --git a/pre b/post<RESET> +<WHITE>index 330b04f..5ed8eff 100644<RESET> +<WHITE>--- a/pre<RESET> +<WHITE>+++ b/post<RESET> +<BROWN>@@ -1,3 +1,7 @@<RESET> +h(4)<GREEN>,hh[44]<RESET> +<RESET> +a = b + c<RESET> + +<GREEN>aa = a<RESET> + +<GREEN>aeff = aeff * ( aaa )<RESET> +EOF +cp expect expect.non-whitespace-is-word + +test_expect_success 'use regex supplied by driver' ' + + word_diff --color-words + +' + +test_expect_success 'set diff.wordRegex option' ' + git config diff.wordRegex "[[:alnum:]]+" +' + +cp expect.letter-runs-are-words expect + +test_expect_success 'command-line overrides config' ' + word_diff --color-words="[a-z]+" +' + +cp expect.non-whitespace-is-word expect + +test_expect_success '.gitattributes override config' ' + word_diff --color-words +' + +test_expect_success 'remove diff driver regex' ' + git config --unset diff.testdriver.wordRegex +' + +cat > expect <<\EOF +<WHITE>diff --git a/pre b/post<RESET> +<WHITE>index 330b04f..5ed8eff 100644<RESET> +<WHITE>--- a/pre<RESET> +<WHITE>+++ b/post<RESET> +<BROWN>@@ -1,3 +1,7 @@<RESET> +h(4),<GREEN>hh[44<RESET>] +<RESET> +a = b + c<RESET> + +<GREEN>aa = a<RESET> + +<GREEN>aeff = aeff * ( aaa<RESET> ) +EOF + +test_expect_success 'use configured regex' ' + word_diff --color-words +' + +echo 'aaa (aaa)' > pre +echo 'aaa (aaa) aaa' > post + +cat > expect <<\EOF +<WHITE>diff --git a/pre b/post<RESET> +<WHITE>index c29453b..be22f37 100644<RESET> +<WHITE>--- a/pre<RESET> +<WHITE>+++ b/post<RESET> +<BROWN>@@ -1 +1 @@<RESET> +aaa (aaa) <GREEN>aaa<RESET> +EOF + +test_expect_success 'test parsing words for newline' ' + + word_diff --color-words="a+" + + +' + +echo '(:' > pre +echo '(' > post + +cat > expect <<\EOF +<WHITE>diff --git a/pre b/post<RESET> +<WHITE>index 289cb9d..2d06f37 100644<RESET> +<WHITE>--- a/pre<RESET> +<WHITE>+++ b/post<RESET> +<BROWN>@@ -1 +1 @@<RESET> +(<RED>:<RESET> +EOF + +test_expect_success 'test when words are only removed at the end' ' + + word_diff --color-words=. + +' + +test_done diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh index 55334927ab..0f185caa44 100755 --- a/t/t4114-apply-typechange.sh +++ b/t/t4114-apply-typechange.sh @@ -25,6 +25,10 @@ test_expect_success 'setup repository and commits' ' git update-index foo && git commit -m "foo back to file" && git branch foo-back-to-file && + printf "\0" > foo && + git update-index foo && + git commit -m "foo becomes binary" && + git branch foo-becomes-binary && rm -f foo && git update-index --remove foo && mkdir foo && @@ -85,6 +89,20 @@ test_expect_success 'symlink becomes file' ' ' test_debug 'cat patch' +test_expect_success 'binary file becomes symlink' ' + git checkout -f foo-becomes-binary && + git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch && + git apply --index < patch + ' +test_debug 'cat patch' + +test_expect_success 'symlink becomes binary file' ' + git checkout -f foo-symlinked-to-bar && + git diff-tree -p --binary HEAD foo-becomes-binary > patch && + git apply --index < patch + ' +test_debug 'cat patch' + test_expect_success 'symlink becomes directory' ' git checkout -f foo-symlinked-to-bar && diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index fe14589427..e70ea94a13 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -11,7 +11,7 @@ test_expect_success 'split sample box' \ 'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last && last=`cat last` && echo total is $last && - test `cat last` = 11' + test `cat last` = 13' for mail in `echo 00*` do @@ -26,6 +26,28 @@ do ' done + +test_expect_success 'split box with rfc2047 samples' \ + 'mkdir rfc2047 && + git mailsplit -orfc2047 "$TEST_DIRECTORY"/t5100/rfc2047-samples.mbox \ + >rfc2047/last && + last=`cat rfc2047/last` && + echo total is $last && + test `cat rfc2047/last` = 11' + +for mail in `echo rfc2047/00*` +do + test_expect_success "mailinfo $mail" ' + git mailinfo -u $mail-msg $mail-patch <$mail >$mail-info && + echo msg && + test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-msg && + echo patch && + test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-patch && + echo info && + test_cmp "$TEST_DIRECTORY"/t5100/rfc2047-info-$(basename $mail) $mail-info + ' +done + test_expect_success 'respect NULs' ' git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain && diff --git a/t/t5100/empty b/t/t5100/empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/t/t5100/empty diff --git a/t/t5100/info0001 b/t/t5100/info0001 index 8c052777e0..f951538acc 100644 --- a/t/t5100/info0001 +++ b/t/t5100/info0001 @@ -1,4 +1,4 @@ -Author: A U Thor +Author: A (zzz) U Thor (Comment) Email: a.u.thor@example.com Subject: a commit. Date: Fri, 9 Jun 2006 00:44:16 -0700 diff --git a/t/t5100/info0012 b/t/t5100/info0012 new file mode 100644 index 0000000000..ac1216ff75 --- /dev/null +++ b/t/t5100/info0012 @@ -0,0 +1,5 @@ +Author: Dmitriy Blinov +Email: bda@mnsspb.ru +Subject: Изменён список пакетов необходимых для сборки +Date: Wed, 12 Nov 2008 17:54:41 +0300 + diff --git a/t/t5100/info0013 b/t/t5100/info0013 new file mode 100644 index 0000000000..bbe049e20e --- /dev/null +++ b/t/t5100/info0013 @@ -0,0 +1,5 @@ +Author: A U Thor +Email: a.u.thor@example.com +Subject: a patch +Date: Fri, 9 Jun 2006 00:44:16 -0700 + diff --git a/t/t5100/msg0012 b/t/t5100/msg0012 new file mode 100644 index 0000000000..1dc2bf7f7f --- /dev/null +++ b/t/t5100/msg0012 @@ -0,0 +1,7 @@ +textlive-* исправлены на texlive-* +docutils заменён на python-docutils + +Действительно, оказалось, что rest2web вытягивает за собой +python-docutils. В то время как сам rest2web не нужен. + +Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru> diff --git a/t/t5100/msg0013 b/t/t5100/msg0013 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/t/t5100/msg0013 diff --git a/t/t5100/patch0012 b/t/t5100/patch0012 new file mode 100644 index 0000000000..36a0b68161 --- /dev/null +++ b/t/t5100/patch0012 @@ -0,0 +1,30 @@ +--- + howto/build_navy.txt | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/howto/build_navy.txt b/howto/build_navy.txt +index 3fd3afb..0ee807e 100644 +--- a/howto/build_navy.txt ++++ b/howto/build_navy.txt +@@ -119,8 +119,8 @@ + - libxv-dev + - libusplash-dev + - latex-make +- - textlive-lang-cyrillic +- - textlive-latex-extra ++ - texlive-lang-cyrillic ++ - texlive-latex-extra + - dia + - python-pyrex + - libtool +@@ -128,7 +128,7 @@ + - sox + - cython + - imagemagick +- - docutils ++ - python-docutils + + #. на машине dinar: добавить свой открытый ssh-ключ в authorized_keys2 пользователя ddev + #. на своей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно следующим образом:: +-- +1.5.6.5 diff --git a/t/t5100/patch0013 b/t/t5100/patch0013 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/t/t5100/patch0013 diff --git a/t/t5100/rfc2047-info-0001 b/t/t5100/rfc2047-info-0001 new file mode 100644 index 0000000000..0a383b07e3 --- /dev/null +++ b/t/t5100/rfc2047-info-0001 @@ -0,0 +1,4 @@ +Author: Keith Moore +Email: moore@cs.utk.edu +Subject: If you can read this you understand the example. + diff --git a/t/t5100/rfc2047-info-0002 b/t/t5100/rfc2047-info-0002 new file mode 100644 index 0000000000..881be75d6f --- /dev/null +++ b/t/t5100/rfc2047-info-0002 @@ -0,0 +1,4 @@ +Author: Olle Järnefors +Email: ojarnef@admin.kth.se +Subject: Time for ISO 10646? + diff --git a/t/t5100/rfc2047-info-0003 b/t/t5100/rfc2047-info-0003 new file mode 100644 index 0000000000..d0f789177c --- /dev/null +++ b/t/t5100/rfc2047-info-0003 @@ -0,0 +1,4 @@ +Author: Patrik Fältström +Email: paf@nada.kth.se +Subject: RFC-HDR care and feeding + diff --git a/t/t5100/rfc2047-info-0004 b/t/t5100/rfc2047-info-0004 new file mode 100644 index 0000000000..f67a90a974 --- /dev/null +++ b/t/t5100/rfc2047-info-0004 @@ -0,0 +1,4 @@ +Author: Nathaniel Borenstein (םולש ןב ילטפנ) +Email: nsb@thumper.bellcore.com +Subject: Test of new header generator + diff --git a/t/t5100/rfc2047-info-0005 b/t/t5100/rfc2047-info-0005 new file mode 100644 index 0000000000..c27be3bf24 --- /dev/null +++ b/t/t5100/rfc2047-info-0005 @@ -0,0 +1,2 @@ +Subject: (a) + diff --git a/t/t5100/rfc2047-info-0006 b/t/t5100/rfc2047-info-0006 new file mode 100644 index 0000000000..9dad474456 --- /dev/null +++ b/t/t5100/rfc2047-info-0006 @@ -0,0 +1,2 @@ +Subject: (a b) + diff --git a/t/t5100/rfc2047-info-0007 b/t/t5100/rfc2047-info-0007 new file mode 100644 index 0000000000..294f195a57 --- /dev/null +++ b/t/t5100/rfc2047-info-0007 @@ -0,0 +1,2 @@ +Subject: (ab) + diff --git a/t/t5100/rfc2047-info-0008 b/t/t5100/rfc2047-info-0008 new file mode 100644 index 0000000000..294f195a57 --- /dev/null +++ b/t/t5100/rfc2047-info-0008 @@ -0,0 +1,2 @@ +Subject: (ab) + diff --git a/t/t5100/rfc2047-info-0009 b/t/t5100/rfc2047-info-0009 new file mode 100644 index 0000000000..294f195a57 --- /dev/null +++ b/t/t5100/rfc2047-info-0009 @@ -0,0 +1,2 @@ +Subject: (ab) + diff --git a/t/t5100/rfc2047-info-0010 b/t/t5100/rfc2047-info-0010 new file mode 100644 index 0000000000..9dad474456 --- /dev/null +++ b/t/t5100/rfc2047-info-0010 @@ -0,0 +1,2 @@ +Subject: (a b) + diff --git a/t/t5100/rfc2047-info-0011 b/t/t5100/rfc2047-info-0011 new file mode 100644 index 0000000000..9dad474456 --- /dev/null +++ b/t/t5100/rfc2047-info-0011 @@ -0,0 +1,2 @@ +Subject: (a b) + diff --git a/t/t5100/rfc2047-samples.mbox b/t/t5100/rfc2047-samples.mbox new file mode 100644 index 0000000000..3ca2470da2 --- /dev/null +++ b/t/t5100/rfc2047-samples.mbox @@ -0,0 +1,48 @@ +From nobody Mon Sep 17 00:00:00 2001 +From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu> +To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk> +CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be> +Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= + =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?= + +From nobody Mon Sep 17 00:00:00 2001 +From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se> +To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se +Subject: Time for ISO 10646? + +From nobody Mon Sep 17 00:00:00 2001 +To: Dave Crocker <dcrocker@mordor.stanford.edu> +Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se +From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se> +Subject: Re: RFC-HDR care and feeding + +From nobody Mon Sep 17 00:00:00 2001 +From: Nathaniel Borenstein <nsb@thumper.bellcore.com> + (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=) +To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed + <ned@innosoft.com>, Keith Moore <moore@cs.utk.edu> +Subject: Test of new header generator +MIME-Version: 1.0 +Content-type: text/plain; charset=ISO-8859-1 + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a?=) + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a?= b) + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a?= + =?ISO-8859-1?Q?b?=) + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a_b?=) + +From nobody Mon Sep 17 00:00:00 2001 +Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=) diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index 4bf7947b41..c5ad206b40 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -2,7 +2,10 @@ From nobody Mon Sep 17 00:00:00 2001 -From: A U Thor <a.u.thor@example.com> +From: A (zzz) + U + Thor + <a.u.thor@example.com> (Comment) Date: Fri, 9 Jun 2006 00:44:16 -0700 Subject: [PATCH] a commit. @@ -501,3 +504,60 @@ index 3e5fe51..aabfe5c 100644 --=-=-=-- +From bda@mnsspb.ru Wed Nov 12 17:54:41 2008 +From: Dmitriy Blinov <bda@mnsspb.ru> +To: navy-patches@dinar.mns.mnsspb.ru +Date: Wed, 12 Nov 2008 17:54:41 +0300 +Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru> +X-Mailer: git-send-email 1.5.6.5 +MIME-Version: 1.0 +Content-Type: text/plain; + charset=utf-8 +Content-Transfer-Encoding: 8bit +Subject: [Navy-patches] [PATCH] + =?utf-8?b?0JjQt9C80LXQvdGR0L0g0YHQv9C40YHQvtC6INC/0LA=?= + =?utf-8?b?0LrQtdGC0L7QsiDQvdC10L7QsdGF0L7QtNC40LzRi9GFINC00LvRjyA=?= + =?utf-8?b?0YHQsdC+0YDQutC4?= + +textlive-* исправлены на texlive-* +docutils заменён на python-docutils + +Действительно, оказалось, что rest2web вытягивает за собой +python-docutils. В то время как сам rest2web не нужен. + +Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru> +--- + howto/build_navy.txt | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/howto/build_navy.txt b/howto/build_navy.txt +index 3fd3afb..0ee807e 100644 +--- a/howto/build_navy.txt ++++ b/howto/build_navy.txt +@@ -119,8 +119,8 @@ + - libxv-dev + - libusplash-dev + - latex-make +- - textlive-lang-cyrillic +- - textlive-latex-extra ++ - texlive-lang-cyrillic ++ - texlive-latex-extra + - dia + - python-pyrex + - libtool +@@ -128,7 +128,7 @@ + - sox + - cython + - imagemagick +- - docutils ++ - python-docutils + + #. на машине dinar: добавить свой открытый ssh-ключ в authorized_keys2 пользователя ddev + #. на своей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно следующим образом:: +-- +1.5.6.5 +From nobody Mon Sep 17 00:00:00 2001 +From: <a.u.thor@example.com> (A U Thor) +Date: Fri, 9 Jun 2006 00:44:16 -0700 +Subject: [PATCH] a patch + diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh new file mode 100755 index 0000000000..96be5236a2 --- /dev/null +++ b/t/t5519-push-alternates.sh @@ -0,0 +1,143 @@ +#!/bin/sh + +test_description='push to a repository that borrows from elsewhere' + +. ./test-lib.sh + +test_expect_success setup ' + mkdir alice-pub && + ( + cd alice-pub && + GIT_DIR=. git init + ) && + mkdir alice-work && + ( + cd alice-work && + git init && + >file && + git add . && + git commit -m initial && + git push ../alice-pub master + ) && + + # Project Bob is a fork of project Alice + mkdir bob-pub && + ( + cd bob-pub && + GIT_DIR=. git init && + mkdir -p objects/info && + echo ../../alice-pub/objects >objects/info/alternates + ) && + git clone alice-pub bob-work && + ( + cd bob-work && + git push ../bob-pub master + ) +' + +test_expect_success 'alice works and pushes' ' + ( + cd alice-work && + echo more >file && + git commit -a -m second && + git push ../alice-pub + ) +' + +test_expect_success 'bob fetches from alice, works and pushes' ' + ( + # Bob acquires what Alice did in his work tree first. + # Even though these objects are not directly in + # the public repository of Bob, this push does not + # need to send the commit Bob received from Alice + # to his public repository, as all the object Alice + # has at her public repository are available to it + # via its alternates. + cd bob-work && + git pull ../alice-pub master && + echo more bob >file && + git commit -a -m third && + git push ../bob-pub + ) && + + # Check that the second commit by Alice is not sent + # to ../bob-pub + ( + cd bob-pub && + second=$(git rev-parse HEAD^) && + rm -f objects/info/alternates && + test_must_fail git cat-file -t $second && + echo ../../alice-pub/objects >objects/info/alternates + ) +' + +test_expect_success 'clean-up in case the previous failed' ' + ( + cd bob-pub && + echo ../../alice-pub/objects >objects/info/alternates + ) +' + +test_expect_success 'alice works and pushes again' ' + ( + # Alice does not care what Bob does. She does not + # even have to be aware of his existence. She just + # keeps working and pushing + cd alice-work && + echo more alice >file && + git commit -a -m fourth && + git push ../alice-pub + ) +' + +test_expect_success 'bob works and pushes' ' + ( + # This time Bob does not pull from Alice, and + # the master branch at her public repository points + # at a commit Bob does not know about. This should + # not prevent the push by Bob from succeeding. + cd bob-work && + echo yet more bob >file && + git commit -a -m fifth && + git push ../bob-pub + ) +' + +test_expect_success 'alice works and pushes yet again' ' + ( + # Alice does not care what Bob does. She does not + # even have to be aware of his existence. She just + # keeps working and pushing + cd alice-work && + echo more and more alice >file && + git commit -a -m sixth.1 && + echo more and more alice >>file && + git commit -a -m sixth.2 && + echo more and more alice >>file && + git commit -a -m sixth.3 && + git push ../alice-pub + ) +' + +test_expect_success 'bob works and pushes again' ' + ( + cd alice-pub && + git cat-file commit master >../bob-work/commit + ) + ( + # This time Bob does not pull from Alice, and + # the master branch at her public repository points + # at a commit Bob does not fully know about, but + # he happens to have the commit object (but not the + # necessary tree) in his repository from Alice. + # This should not prevent the push by Bob from + # succeeding. + cd bob-work && + git hash-object -t commit -w commit && + echo even more bob >file && + git commit -a -m seventh && + git push ../bob-pub + ) +' + +test_done diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index 8dfaaa456e..3559d17964 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -11,8 +11,8 @@ test_expect_success 'preparing origin repository' ' git clone --bare . x && test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true && test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true - git bundle create b1.bundle --all HEAD && - git bundle create b2.bundle --all && + git bundle create b1.bundle --all && + git bundle create b2.bundle master && mkdir dir && cp b1.bundle dir/b3 cp b1.bundle b4 @@ -116,4 +116,20 @@ test_expect_success 'bundle clone with nonexistent HEAD' ' test ! -e .git/refs/heads/master ' +test_expect_success 'clone empty repository' ' + cd "$D" && + mkdir empty && + (cd empty && git init) && + git clone empty empty-clone && + test_tick && + (cd empty-clone + echo "content" >> foo && + git add foo && + git commit -m "Initial commit" && + git push origin master && + expected=$(git rev-parse master) && + actual=$(git --git-dir=../empty/.git rev-parse master) && + test $actual = $expected) +' + test_done diff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh new file mode 100755 index 0000000000..991ab4a65b --- /dev/null +++ b/t/t6014-rev-list-all.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +test_description='--all includes detached HEADs' + +. ./test-lib.sh + + +commit () { + test_tick && + echo $1 > foo && + git add foo && + git commit -m "$1" +} + +test_expect_success 'setup' ' + + commit one && + commit two && + git checkout HEAD^ && + commit detached + +' + +test_expect_success 'rev-list --all lists detached HEAD' ' + + test 3 = $(git rev-list --all | wc -l) + +' + +test_expect_success 'repack does not lose detached HEAD' ' + + git gc && + git prune --expire=now && + git show HEAD + +' + +test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index f377fea4bb..69501e2711 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1090,6 +1090,121 @@ test_expect_success 'filename for the message is relative to cwd' ' git cat-file tag tag-from-subdir-2 | grep "in sub directory" ' +# create a few more commits to test --contains + +hash1=$(git rev-parse HEAD) + +test_expect_success 'creating second commit and tag' ' + echo foo-2.0 >foo && + git add foo && + git commit -m second + git tag v2.0 +' + +hash2=$(git rev-parse HEAD) + +test_expect_success 'creating third commit without tag' ' + echo foo-dev >foo && + git add foo && + git commit -m third +' + +hash3=$(git rev-parse HEAD) + +# simple linear checks of --continue + +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +EOF + +test_expect_success 'checking that first commit is in all tags (hash)' " + git tag -l --contains $hash1 v* >actual + test_cmp expected actual +" + +# other ways of specifying the commit +test_expect_success 'checking that first commit is in all tags (tag)' " + git tag -l --contains v1.0 v* >actual + test_cmp expected actual +" + +test_expect_success 'checking that first commit is in all tags (relative)' " + git tag -l --contains HEAD~2 v* >actual + test_cmp expected actual +" + +cat > expected <<EOF +v2.0 +EOF + +test_expect_success 'checking that second commit only has one tag' " + git tag -l --contains $hash2 v* >actual + test_cmp expected actual +" + + +cat > expected <<EOF +EOF + +test_expect_success 'checking that third commit has no tags' " + git tag -l --contains $hash3 v* >actual + test_cmp expected actual +" + +# how about a simple merge? + +test_expect_success 'creating simple branch' ' + git branch stable v2.0 && + git checkout stable && + echo foo-3.0 > foo && + git commit foo -m fourth + git tag v3.0 +' + +hash4=$(git rev-parse HEAD) + +cat > expected <<EOF +v3.0 +EOF + +test_expect_success 'checking that branch head only has one tag' " + git tag -l --contains $hash4 v* >actual + test_cmp expected actual +" + +test_expect_success 'merging original branch into this branch' ' + git merge --strategy=ours master && + git tag v4.0 +' + +cat > expected <<EOF +v4.0 +EOF + +test_expect_success 'checking that original branch head has one tag now' " + git tag -l --contains $hash3 v* >actual + test_cmp expected actual +" + +cat > expected <<EOF +v0.2.1 +v1.0 +v1.0.1 +v1.1.3 +v2.0 +v3.0 +v4.0 +EOF + +test_expect_success 'checking that initial commit is in all tags' " + git tag -l --contains $hash1 v* >actual + test_cmp expected actual +" + # mixing modes and options: test_expect_success 'mixing incompatibles modes and options is forbidden' ' diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh index 63a8225ae5..5babdf26e6 100755 --- a/t/t7701-repack-unpack-unreachable.sh +++ b/t/t7701-repack-unpack-unreachable.sh @@ -50,12 +50,10 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' ' compare_mtimes () { - perl -e 'my $reference = shift; - foreach my $file (@ARGV) { - exit(1) unless(-f $file && -M $file == -M $reference); - } - exit(0); - ' -- "$@" + read tref rest && + while read t rest; do + test "$tref" = "$t" || break + done } test_expect_success '-A without -d option leaves unreachable objects packed' ' @@ -87,7 +85,9 @@ test_expect_success 'unpacked objects receive timestamp of pack file' ' tmppack=".git/objects/pack/tmp_pack" && ln "$packfile" "$tmppack" && git repack -A -l -d && - compare_mtimes "$tmppack" "$fsha1path" "$csha1path" "$tsha1path" + test-chmtime -v +0 "$tmppack" "$fsha1path" "$csha1path" "$tsha1path" \ + > mtimes && + compare_mtimes < mtimes ' test_done diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh new file mode 100755 index 0000000000..c4b5b8bcf7 --- /dev/null +++ b/t/t9134-git-svn-ignore-paths.sh @@ -0,0 +1,98 @@ +#!/bin/sh +# +# Copyright (c) 2009 Vitaly Shukela +# Copyright (c) 2009 Eric Wong +# + +test_description='git svn property tests' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + svn co "$svnrepo" s && + ( + cd s && + mkdir qqq www && + echo test_qqq > qqq/test_qqq.txt && + echo test_www > www/test_www.txt && + svn add qqq && + svn add www && + svn commit -m "create some files" && + svn up && + echo hi >> www/test_www.txt && + svn commit -m "modify www/test_www.txt" && + svn up + ) +' + +test_expect_success 'clone an SVN repository with ignored www directory' ' + git svn clone --ignore-paths="^www" "$svnrepo" g && + echo test_qqq > expect && + for i in g/*/*.txt; do cat $i >> expect2; done && + test_cmp expect expect2 +' + +test_expect_success 'SVN-side change outside of www' ' + ( + cd s && + echo b >> qqq/test_qqq.txt && + svn commit -m "SVN-side change outside of www" && + svn up && + svn log -v | fgrep "SVN-side change outside of www" + ) +' + +test_expect_success 'update git svn-cloned repo' ' + ( + cd g && + git svn rebase --ignore-paths="^www" && + printf "test_qqq\nb\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'SVN-side change inside of ignored www' ' + ( + cd s && + echo zaq >> www/test_www.txt + svn commit -m "SVN-side change inside of www/test_www.txt" && + svn up && + svn log -v | fgrep "SVN-side change inside of www/test_www.txt" + ) +' + +test_expect_success 'update git svn-cloned repo' ' + ( + cd g && + git svn rebase --ignore-paths="^www" && + printf "test_qqq\nb\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'SVN-side change in and out of ignored www' ' + ( + cd s && + echo cvf >> www/test_www.txt + echo ygg >> qqq/test_qqq.txt + svn commit -m "SVN-side change in and out of ignored www" && + svn up && + svn log -v | fgrep "SVN-side change in and out of ignored www" + ) +' + +test_expect_success 'update git svn-cloned repo again' ' + ( + cd g && + git svn rebase --ignore-paths="^www" && + printf "test_qqq\nb\nygg\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 41d5a5996e..6f6244ab7e 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -193,6 +193,31 @@ test_tick () { export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } +# Call test_commit with the arguments "<message> [<file> [<contents>]]" +# +# This will commit a file with the given contents and the given commit +# message. It will also add a tag with <message> as name. +# +# Both <file> and <contents> default to <message>. + +test_commit () { + file=${2:-"$1.t"} + echo "${3-$1}" > "$file" && + git add "$file" && + test_tick && + git commit -m "$1" && + git tag "$1" +} + +# Call test_merge with the arguments "<message> <commit>", where <commit> +# can be a tag pointing to the commit-to-merge. + +test_merge () { + test_tick && + git merge -m "$1" "$2" && + git tag "$1" +} + # You are not expected to call test_ok_ and test_failure_ directly, use # the text_expect_* functions instead. diff --git a/dump-cache-tree.c b/test-dump-cache-tree.c index 1f73f1ea7d..1f73f1ea7d 100644 --- a/dump-cache-tree.c +++ b/test-dump-cache-tree.c diff --git a/test-path-utils.c b/test-path-utils.c index a0bcb0e210..2c0f5a37e8 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -3,7 +3,7 @@ int main(int argc, char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_absolute_path")) { - char *buf = xmalloc(strlen(argv[2])+1); + char *buf = xmalloc(PATH_MAX + 1); int rv = normalize_absolute_path(buf, argv[2]); assert(strlen(buf) == rv); puts(buf); diff --git a/test-sigchain.c b/test-sigchain.c new file mode 100644 index 0000000000..42db234e87 --- /dev/null +++ b/test-sigchain.c @@ -0,0 +1,22 @@ +#include "sigchain.h" +#include "cache.h" + +#define X(f) \ +static void f(int sig) { \ + puts(#f); \ + fflush(stdout); \ + sigchain_pop(sig); \ + raise(sig); \ +} +X(one) +X(two) +X(three) +#undef X + +int main(int argc, char **argv) { + sigchain_push(SIGTERM, one); + sigchain_push(SIGTERM, two); + sigchain_push(SIGTERM, three); + raise(SIGTERM); + return 0; +} diff --git a/unpack-file.c b/unpack-file.c index bcdc8bbb3b..6dd8ad02fb 100644 --- a/unpack-file.c +++ b/unpack-file.c @@ -1,5 +1,6 @@ #include "cache.h" #include "blob.h" +#include "exec_cmd.h" static char *create_temp_file(unsigned char *sha1) { @@ -25,6 +26,8 @@ int main(int argc, char **argv) { unsigned char sha1[20]; + git_extract_argv0_path(argv[0]); + if (argc != 2) usage("git-unpack-file <sha1>"); if (get_sha1(argv[1], sha1)) diff --git a/unpack-trees.c b/unpack-trees.c index 15c9ef592b..e547282ed5 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -61,7 +61,7 @@ static void unlink_entry(struct cache_entry *ce) char *cp, *prev; char *name = ce->name; - if (has_symlink_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) return; if (unlink(name)) return; @@ -240,8 +240,11 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, con return ce; } -static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5], - const struct name_entry *names, const struct traverse_info *info) +static int unpack_nondirectories(int n, unsigned long mask, + unsigned long dirmask, + struct cache_entry **src, + const struct name_entry *names, + const struct traverse_info *info) { int i; struct unpack_trees_options *o = info->data; @@ -291,7 +294,7 @@ static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmas static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info) { - struct cache_entry *src[5] = { NULL, }; + struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, }; struct unpack_trees_options *o = info->data; const struct name_entry *p = names; @@ -580,7 +583,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) return 0; if (!lstat(ce->name, &st)) { diff --git a/update-server-info.c b/update-server-info.c index 7e8209ea4b..7b38fd867b 100644 --- a/update-server-info.c +++ b/update-server-info.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "exec_cmd.h" static const char update_server_info_usage[] = "git update-server-info [--force]"; @@ -19,6 +20,8 @@ int main(int ac, char **av) if (i != ac) usage(update_server_info_usage); + git_extract_argv0_path(av[0]); + setup_git_directory(); return !!update_server_info(force); diff --git a/upload-pack.c b/upload-pack.c index e5adbc011e..5db6f93955 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -616,6 +616,8 @@ int main(int argc, char **argv) int i; int strict = 0; + git_extract_argv0_path(argv[0]); + for (i = 1; i < argc; i++) { char *arg = argv[i]; diff --git a/userdiff.c b/userdiff.c index 3681062ebf..d556da9751 100644 --- a/userdiff.c +++ b/userdiff.c @@ -6,14 +6,20 @@ static struct userdiff_driver *drivers; static int ndrivers; static int drivers_alloc; -#define FUNCNAME(name, pattern) \ - { name, NULL, -1, { pattern, REG_EXTENDED } } +#define PATTERNS(name, pattern, word_regex) \ + { name, NULL, -1, { pattern, REG_EXTENDED }, word_regex } static struct userdiff_driver builtin_drivers[] = { -FUNCNAME("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$"), -FUNCNAME("java", +PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", + "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"), +PATTERNS("java", "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n" - "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$"), -FUNCNAME("objc", + "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$", + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=" + "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|" + "|[^[:space:]]|[\x80-\xff]+"), +PATTERNS("objc", /* Negate C statements that can look like functions */ "!^[ \t]*(do|for|if|else|return|switch|while)\n" /* Objective-C methods */ @@ -21,20 +27,60 @@ FUNCNAME("objc", /* C functions */ "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n" /* Objective-C class/protocol definitions */ - "^(@(implementation|interface|protocol)[ \t].*)$"), -FUNCNAME("pascal", + "^(@(implementation|interface|protocol)[ \t].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->" + "|[^[:space:]]|[\x80-\xff]+"), +PATTERNS("pascal", "^((procedure|function|constructor|destructor|interface|" "implementation|initialization|finalization)[ \t]*.*)$" "\n" - "^(.*=[ \t]*(class|record).*)$"), -FUNCNAME("php", "^[\t ]*((function|class).*)"), -FUNCNAME("python", "^[ \t]*((class|def)[ \t].*)$"), -FUNCNAME("ruby", "^[ \t]*((class|module|def)[ \t].*)$"), -FUNCNAME("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$"), -FUNCNAME("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$"), + "^(.*=[ \t]*(class|record).*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+" + "|<>|<=|>=|:=|\\.\\." + "|[^[:space:]]|[\x80-\xff]+"), +PATTERNS("php", "^[\t ]*((function|class).*)", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+" + "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->" + "|[^[:space:]]|[\x80-\xff]+"), +PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?" + "|[^[:space:]|[\x80-\xff]+"), + /* -- */ +PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$", + /* -- */ + "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?." + "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~" + "|[^[:space:]|[\x80-\xff]+"), +PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + "[={}\"]|[^={}\" \t]+"), +PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", + "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"), +PATTERNS("cpp", + /* Jump targets or access declarations */ + "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n" + /* C/++ functions/methods at top level */ + "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n" + /* compound type at top level */ + "^((struct|class|enum)[^;]*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->" + "|[^[:space:]]|[\x80-\xff]+"), { "default", NULL, -1, { NULL, 0 } }, }; -#undef FUNCNAME +#undef PATTERNS static struct userdiff_driver driver_true = { "diff=true", @@ -134,6 +180,8 @@ int userdiff_config(const char *k, const char *v) return parse_string(&drv->external, k, v); if ((drv = parse_driver(k, v, "textconv"))) return parse_string(&drv->textconv, k, v); + if ((drv = parse_driver(k, v, "wordregex"))) + return parse_string(&drv->word_regex, k, v); return 0; } diff --git a/userdiff.h b/userdiff.h index ba2945770b..c3151594f5 100644 --- a/userdiff.h +++ b/userdiff.h @@ -11,6 +11,7 @@ struct userdiff_driver { const char *external; int binary; struct userdiff_funcname funcname; + const char *word_regex; const char *textconv; }; @@ -4,6 +4,7 @@ * Copyright (C) Eric Biederman, 2005 */ #include "cache.h" +#include "exec_cmd.h" static const char var_usage[] = "git var [-l | <variable>]"; @@ -56,6 +57,8 @@ int main(int argc, char **argv) usage(var_usage); } + git_extract_argv0_path(argv[0]); + setup_git_directory_gently(&nongit); val = NULL; |