diff options
280 files changed, 7268 insertions, 2853 deletions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 0d7fa9cca9..b8bf618a30 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -129,3 +129,6 @@ For C programs: used in the git core command set (unless your command is clearly separate from it, such as an importer to convert random-scm-X repositories to git). + + - When we pass <string, length> pair to functions, we should try to + pass them in that order. diff --git a/Documentation/Makefile b/Documentation/Makefile index 144ec32f12..dba97dc21d 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -41,7 +41,8 @@ man7dir=$(mandir)/man7 ASCIIDOC=asciidoc ASCIIDOC_EXTRA = -MANPAGE_XSL = callouts.xsl +MANPAGE_XSL = manpage-normal.xsl +XMLTO_EXTRA = INSTALL?=install RM ?= rm -f DOC_REF = origin/man @@ -59,12 +60,47 @@ endif -include ../config.mak.autogen -include ../config.mak +# +# For asciidoc ... +# -7.1.2, no extra settings are needed. +# 8.0-, set ASCIIDOC8. +# + +# +# For docbook-xsl ... +# -1.68.1, set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0) +# 1.69.0, no extra settings are needed? +# 1.69.1-1.71.0, set DOCBOOK_SUPPRESS_SP? +# 1.71.1, no extra settings are needed? +# 1.72.0, set DOCBOOK_XSL_172. +# 1.73.0-, set ASCIIDOC_NO_ROFF +# + +# +# If you had been using DOCBOOK_XSL_172 in an attempt to get rid +# of 'the ".ft C" problem' in your generated manpages, and you +# instead ended up with weird characters around callouts, try +# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8). +# + ifdef ASCIIDOC8 ASCIIDOC_EXTRA += -a asciidoc7compatible endif ifdef DOCBOOK_XSL_172 -ASCIIDOC_EXTRA += -a docbook-xsl-172 +ASCIIDOC_EXTRA += -a git-asciidoc-no-roff MANPAGE_XSL = manpage-1.72.xsl +else + ifdef ASCIIDOC_NO_ROFF + # docbook-xsl after 1.72 needs the regular XSL, but will not + # pass-thru raw roff codes from asciidoc.conf, so turn them off. + ASCIIDOC_EXTRA += -a git-asciidoc-no-roff + endif +endif +ifdef MAN_BOLD_LITERAL +XMLTO_EXTRA += -m manpage-bold-literal.xsl +endif +ifdef DOCBOOK_SUPPRESS_SP +XMLTO_EXTRA += -m manpage-suppress-sp.xsl endif # @@ -76,6 +112,32 @@ endif # yourself - yes, all 6 characters of it! # +QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir +QUIET_SUBDIR1 = + +ifneq ($(findstring $(MAKEFLAGS),w),w) +PRINT_DIR = --no-print-directory +else # "make -w" +NO_SUBDIR = : +endif + +ifneq ($(findstring $(MAKEFLAGS),s),s) +ifndef V + QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@; + QUIET_XMLTO = @echo ' ' XMLTO $@; + QUIET_DB2TEXI = @echo ' ' DB2TEXI $@; + QUIET_MAKEINFO = @echo ' ' MAKEINFO $@; + QUIET_DBLATEX = @echo ' ' DBLATEX $@; + QUIET_XSLTPROC = @echo ' ' XSLTPROC $@; + QUIET_GEN = @echo ' ' GEN $@; + QUIET_STDERR = 2> /dev/null + QUIET_SUBDIR0 = +@subdir= + QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ + $(MAKE) $(PRINT_DIR) -C $$subdir + export V +endif +endif + all: html man html: $(DOC_HTML) @@ -119,7 +181,7 @@ install-html: html sh ./install-webdoc.sh $(DESTDIR)$(htmldir) ../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE - $(MAKE) -C ../ GIT-VERSION-FILE + $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE -include ../GIT-VERSION-FILE @@ -127,8 +189,8 @@ install-html: html # Determine "include::" file references in asciidoc files. # doc.dep : $(wildcard *.txt) build-docdep.perl - $(RM) $@+ $@ - $(PERL_PATH) ./build-docdep.perl >$@+ + $(QUIET_GEN)$(RM) $@+ $@ && \ + $(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \ mv $@+ $@ -include doc.dep @@ -146,91 +208,94 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ $(cmds_txt): cmd-list.made cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) - $(RM) $@ - $(PERL_PATH) ./cmd-list.perl ../command-list.txt + $(QUIET_GEN)$(RM) $@ && \ + $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \ date >$@ clean: $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 - $(RM) *.texi *.texi+ git.info gitman.info + $(RM) *.texi *.texi+ *.texi++ git.info gitman.info $(RM) howto-index.txt howto/*.html doc.dep $(RM) technical/api-*.html technical/api-index.txt $(RM) $(cmds_txt) *.made $(MAN_HTML): %.html : %.txt - $(RM) $@+ $@ + $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \ - $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< + $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \ mv $@+ $@ %.1 %.5 %.7 : %.xml - $(RM) $@ - xmlto -m $(MANPAGE_XSL) man $< + $(QUIET_XMLTO)$(RM) $@ && \ + xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< %.xml : %.txt - $(RM) $@+ $@ + $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ - $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< + $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) -o $@+ $< && \ mv $@+ $@ user-manual.xml: user-manual.txt user-manual.conf - $(ASCIIDOC) -b docbook -d book $< + $(QUIET_ASCIIDOC)$(ASCIIDOC) -b docbook -d book $< technical/api-index.txt: technical/api-index-skel.txt \ technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS)) - cd technical && sh ./api-index.sh + $(QUIET_GEN)cd technical && sh ./api-index.sh $(patsubst %,%.html,$(API_DOCS) technical/api-index): %.html : %.txt - $(ASCIIDOC) -b xhtml11 -f asciidoc.conf \ + $(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 -f asciidoc.conf \ $(ASCIIDOC_EXTRA) -agit_version=$(GIT_VERSION) $*.txt XSLT = docbook.xsl XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css user-manual.html: user-manual.xml - xsltproc $(XSLTOPTS) -o $@ $(XSLT) $< + $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $< git.info: user-manual.texi - $(MAKEINFO) --no-split -o $@ user-manual.texi + $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi user-manual.texi: user-manual.xml - $(RM) $@+ $@ - $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout | \ - $(PERL_PATH) fix-texi.perl >$@+ + $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ + $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \ + $(PERL_PATH) fix-texi.perl <$@++ >$@+ && \ + rm $@++ && \ mv $@+ $@ user-manual.pdf: user-manual.xml - $(RM) $@+ $@ - $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< + $(QUIET_DBLATEX)$(RM) $@+ $@ && \ + $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \ mv $@+ $@ gitman.texi: $(MAN_XML) cat-texi.perl - $(RM) $@+ $@ + $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \ - --to-stdout $(xml);)) | $(PERL_PATH) cat-texi.perl $@ >$@+ + --to-stdout $(xml) &&) true) > $@++ && \ + $(PERL_PATH) cat-texi.perl $@ <$@++ >$@+ && \ + rm $@++ && \ mv $@+ $@ gitman.info: gitman.texi - $(MAKEINFO) --no-split --no-validate $*.texi + $(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml - $(RM) $@+ $@ - $(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ + $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ + $(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+ && \ mv $@+ $@ howto-index.txt: howto-index.sh $(wildcard howto/*.txt) - $(RM) $@+ $@ - sh ./howto-index.sh $(wildcard howto/*.txt) >$@+ + $(QUIET_GEN)$(RM) $@+ $@ && \ + sh ./howto-index.sh $(wildcard howto/*.txt) >$@+ && \ mv $@+ $@ $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt - $(ASCIIDOC) -b xhtml11 $*.txt + $(QUIET_ASCIIDOC)$(ASCIIDOC) -b xhtml11 $*.txt WEBDOC_DEST = /pub/software/scm/git/docs $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt - $(RM) $@+ $@ - sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ + $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ + sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ && \ mv $@+ $@ install-webdoc : html diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt new file mode 100644 index 0000000000..db6956205d --- /dev/null +++ b/Documentation/RelNotes-1.6.3.txt @@ -0,0 +1,151 @@ +GIT v1.6.3 Release Notes +======================== + +With the next major release, "git push" into a branch that is +currently checked out will be refused by default. You can choose +what should happen upon such a push by setting the configuration +variable receive.denyCurrentBranch in the receiving repository. + +To ease the transition plan, the receiving repository of such a +push running this release will issue a big warning when the +configuration variable is missing. Please refer to: + + http://git.or.cz/gitwiki/GitFaq#non-bare + http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007 + +for more details on the reason why this change is needed and the +transition plan. + +For a similar reason, "git push $there :$killed" to delete the branch +$killed in a remote repository $there, if $killed branch is the current +branch pointed at by its HEAD, gets a large warning. You can choose what +should happen upon such a push by setting the configuration variable +receive.denyDeleteCurrent in the receiving repository. + +When the user does not tell "git push" what to push, it has always +pushed matching refs. For some people it is unexpected, and a new +configuration variable push.default has been introduced to allow +changing a different default behaviour. To advertise the new feature, +a big warning is issued if this is not configured and a git push without +arguments is attempted. + + +Updates since v1.6.2 +-------------------- + +(subsystems) + +(performance) + +* many uses of lstat(2) in the codepath for "git checkout" have been + optimized out. + +(usability, bells and whistles) + +* rsync:/path/to/repo can be used to run git over rsync for local + repositories. It may not be useful in practice; meant primarily for + testing. + +* http transport learned to prompt and use password when fetching from or + pushing to http://user@host.xz/ URL. + +* (msysgit) progress output that is sent over the sideband protocol can + be handled appropriately in Windows console. + +* "--pretty=<style>" option to the log family of commands can now be + spelled as "--format=<style>". In addition, --format=%formatstring + is a short-hand for --pretty=tformat:%formatstring. + +* "--oneline" is a synonym for "--pretty=oneline --abbrev=commit". + +* If you realize that you botched the patch when you are editing hunks + with the 'edit' action in git-add -i/-p, you can abort the editor to + tell git not to apply it. + +* The number of commits shown in "you are ahead/behind your upstream" + messages given by "git checkout" and "git status" used to count merge + commits; now it doesn't. + +* git-archive learned --output=<file> option. + +* git-bisect shows not just the number of remaining commits whose goodness + is unknown, but also shows the estimated number of remaining rounds. + +* You can give --date=<format> option to git-blame. + +* git-branch -r shows HEAD symref that points at a remote branch in + interest of each tracked remote repository. + +* git-config learned -e option to open an editor to edit the config file + directly. + +* git-clone runs post-checkout hook when run without --no-checkout. + +* git-fast-export choked when seeing a tag that does not point at commit. + +* git-format-patch can be told to use attachment with a new configuration, + format.attach. + +* git-format-patch can be told to produce deep or shallow message threads. + +* git-format-patch learned format.headers configuration to add extra + header fields to the output. This behaviour is similar to the existing + --add-header=<header> option of the command. + +* git-grep learned to highlight the found substrings in color. + +* git-imap-send learned to work around Thunderbird's inability to easily + disable format=flowed with a new configuration, imap.preformattedHTML. + +* git-rebase can be told to rebase the series even if your branch is a + descendant of the commit you are rebasing onto with --force-rebase + option. + +* git-rebase can be told to report diffstat with the --stat option. + +* Output from git-remote command has been vastly improved. + +* git-repack (invoked from git-gc) did not work as nicely as it should in + a repository that borrows objects from neighbours via alternates + mechanism especially when some packs are marked with the ".keep" flag + to prevent them from being repacked. + +* git-send-email learned --confirm option to review the Cc: list before + sending the messages out. + +(developers) + +* Test scripts can be run under valgrind. + +* Test scripts can be run with installed git. + +* Makefile learned 'coverage' option to run the test suites with + coverage tracking enabled. + +* Building the manpages with docbook-xsl between 1.69.1 and 1.71.1 now + requires setting DOCBOOK_SUPPRESS_SP to work around a docbook-xsl bug. + This workaround used to be enabled by default, but causes problems + with newer versions of docbook-xsl. In addition, there are a few more + knobs you can tweak to work around issues with various versions of the + docbook-xsl package. See comments in Documentation/Makefile for details. + +Fixes since v1.6.2 +------------------ + +All of the fixes in v1.6.2.X maintenance series are included in this +release, unless otherwise noted. + +Here are fixes that this release has, but have not been backported to +v1.6.2.X series. + +* The initial checkout did not read the attributes from the .gitattribute + file that is being checked out. + +* git-gc spent excessive amount of time to decide if an object appears + in a locally existing pack (if needed, backport by merging 69e020a). + +--- +exec >/var/tmp/1 +O=v1.6.2.2-403-g8130949 +echo O=$(git describe master) +git shortlog --no-merges $O..master ^maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 9b559adefc..8d818a2160 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -491,6 +491,12 @@ message, complete the addressing and subject fields, and press send. Gmail ----- +GMail does not appear to have any way to turn off line wrapping in the web +interface, so this will mangle any emails that you send. You can however +use any IMAP email client to connect to the google imap server, and forward +the emails through that. Just make sure to disable line wrapping in that +email client. Alternatively, use "git send-email" instead. + Submitting properly formatted patches via Gmail is simple now that IMAP support is available. First, edit your ~/.gitconfig to specify your account settings: @@ -503,6 +509,9 @@ account settings: port = 993 sslverify = false +You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error +that the "Folder doesn't exist". + Next, ensure that your Gmail settings are correct. In "Settings" the "Use Unicode (UTF-8) encoding for outgoing messages" should be checked. @@ -513,3 +522,4 @@ command to send the patch emails to your Gmail Drafts folder. Go to your Gmail account, open the Drafts folder, find the patch email, fill in the To: and CC: fields and send away! + diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 1e735df3bb..dc76e7f073 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -27,7 +27,7 @@ ifdef::backend-docbook[] endif::backend-docbook[] ifdef::backend-docbook[] -ifndef::docbook-xsl-172[] +ifndef::git-asciidoc-no-roff[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. # v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] @@ -42,16 +42,16 @@ ifdef::doctype-manpage[] endif::doctype-manpage[] </literallayout> {title#}</example> -endif::docbook-xsl-172[] +endif::git-asciidoc-no-roff[] -ifdef::docbook-xsl-172[] +ifdef::git-asciidoc-no-roff[] ifdef::doctype-manpage[] # The following two small workarounds insert a simple paragraph after screen [listingblock] <example><title>{title}</title> -<screen> +<literallayout> | -</screen><simpara></simpara> +</literallayout><simpara></simpara> {title#}</example> [verseblock] @@ -59,10 +59,11 @@ ifdef::doctype-manpage[] {title%}<literallayout{id? id="{id}"}> {title#}<literallayout> | -</literallayout><simpara></simpara> +</literallayout> {title#}</para></formalpara> +{title%}<simpara></simpara> endif::doctype-manpage[] -endif::docbook-xsl-172[] +endif::git-asciidoc-no-roff[] endif::backend-docbook[] ifdef::doctype-manpage[] diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index 26cfb62195..1625ffce6a 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -70,6 +70,14 @@ of lines before or after the line given by <start>. tree copy has the contents of the named file (specify `-` to make the command read from the standard input). +--date <format>:: + The value is one of the following alternatives: + {relative,local,default,iso,rfc,short}. If --date is not + provided, the value of the blame.date config variable is + used. If the blame.date config variable is also not set, the + iso format is used. For more information, See the discussion + of the --date option at linkgit:git-log[1]. + -M|<num>|:: Detect moving lines in the file as well. When a commit moves a block of lines in a file (e.g. the original file diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl deleted file mode 100644 index 6a361a2136..0000000000 --- a/Documentation/callouts.xsl +++ /dev/null @@ -1,30 +0,0 @@ -<!-- callout.xsl: converts asciidoc callouts to man page format --> -<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> -<xsl:template match="co"> - <xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/> -</xsl:template> -<xsl:template match="calloutlist"> - <xsl:text>.sp </xsl:text> - <xsl:apply-templates/> - <xsl:text> </xsl:text> -</xsl:template> -<xsl:template match="callout"> - <xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/> - <xsl:apply-templates/> - <xsl:text>.br </xsl:text> -</xsl:template> - -<!-- sorry, this is not about callouts, but attempts to work around - spurious .sp at the tail of the line docbook stylesheets seem to add --> -<xsl:template match="simpara"> - <xsl:variable name="content"> - <xsl:apply-templates/> - </xsl:variable> - <xsl:value-of select="normalize-space($content)"/> - <xsl:if test="not(ancestor::authorblurb) and - not(ancestor::personblurb)"> - <xsl:text> </xsl:text> - </xsl:if> -</xsl:template> - -</xsl:stylesheet> diff --git a/Documentation/config.txt b/Documentation/config.txt index f5152c5038..ad22cb875e 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -25,7 +25,7 @@ blank lines are ignored. The file consists of sections and variables. A section begins with the name of the section in square brackets and continues until the next section begins. Section names are not case sensitive. Only alphanumeric -characters, '`-`' and '`.`' are allowed in section names. Each variable +characters, `-` and `.` are allowed in section names. Each variable must belong to some section, which means that there must be section header before first setting of a variable. @@ -39,7 +39,7 @@ in the section header, like in example below: -------- Subsection names can contain any characters except newline (doublequote -'`"`' and backslash have to be escaped as '`\"`' and '`\\`', +`"` and backslash have to be escaped as `\"` and `\\`, respectively) and are case sensitive. Section header cannot span multiple lines. Variables may belong directly to a section or to a given subsection. You can have `[section]` if you have `[section "subsection"]`, but you @@ -53,7 +53,7 @@ All the other lines are recognized as setting variables, in the form 'name = value'. If there is no equal sign on the line, the entire line is taken as 'name' and the variable is recognized as boolean "true". The variable names are case-insensitive and only alphanumeric -characters and '`-`' are allowed. There can be more than one value +characters and `-` are allowed. There can be more than one value for a given variable; we say then that variable is multivalued. Leading and trailing whitespace in a variable value is discarded. @@ -69,15 +69,15 @@ String values may be entirely or partially enclosed in double quotes. You need to enclose variable value in double quotes if you want to preserve leading or trailing whitespace, or if variable value contains beginning of comment characters (if it contains '#' or ';'). -Double quote '`"`' and backslash '`\`' characters in variable value must -be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'. +Double quote `"` and backslash `\` characters in variable value must +be escaped: use `\"` for `"` and `\\` for `\`. -The following escape sequences (beside '`\"`' and '`\\`') are recognized: -'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB) -and '`\b`' for backspace (BS). No other char escape sequence, nor octal +The following escape sequences (beside `\"` and `\\`) are recognized: +`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) +and `\b` for backspace (BS). No other char escape sequence, nor octal char sequences are valid. -Variable value ending in a '`\`' is continued on the next line in the +Variable value ending in a `\` is continued on the next line in the customary UNIX fashion. Some variables may require special value format. @@ -221,6 +221,11 @@ core.gitProxy:: Can be overridden by the 'GIT_PROXY_COMMAND' environment variable (which always applies universally, without the special "for" handling). ++ +The special string `none` can be used as the proxy command to +specify that no proxy be used for a given domain pattern. +This is useful for excluding servers inside a firewall from +proxy use, while defaulting to a common proxy for external domains. core.ignoreStat:: If true, commands which modify both the working tree and the index @@ -382,9 +387,9 @@ core.pager:: to override git's default settings this way, you need to be explicit. For example, to disable the S option in a backward compatible manner, set `core.pager` - to "`less -+$LESS -FRX`". This will be passed to the + to `less -+$LESS -FRX`. This will be passed to the shell by git, which will translate the final command to - "`LESS=FRSX less -+FRSX -FRX`". + `LESS=FRSX less -+FRSX -FRX`. core.whitespace:: A comma separated list of common whitespace problems to @@ -468,10 +473,14 @@ branch.autosetuprebase:: This option defaults to never. branch.<name>.remote:: - When in branch <name>, it tells 'git-fetch' which remote to fetch. - If this option is not given, 'git-fetch' defaults to remote "origin". + When in branch <name>, it tells 'git-fetch' and 'git-push' which + remote to fetch from/push to. It defaults to `origin` if no remote is + configured. `origin` is also used if you are not on any branch. branch.<name>.merge:: + Defines, together with branch.<name>.remote, the upstream branch + for the given branch. It tells 'git-fetch'/'git-pull' which + branch to merge from. When in branch <name>, it tells 'git-fetch' the default refspec to be marked for merging in FETCH_HEAD. The value is handled like the remote part of a refspec, and must match a @@ -548,6 +557,25 @@ color.diff.<slot>:: whitespace errors). The values of these variables may be specified as in color.branch.<slot>. +color.grep:: + When set to `always`, always highlight matches. When `false` (or + `never`), never. When set to `true` or `auto`, use color only + when the output is written to the terminal. Defaults to `false`. + +color.grep.external:: + The string value of this variable is passed to an external 'grep' + command as a command line option if match highlighting is turned + on. If set to an empty string, no option is passed at all, + turning off coloring for external 'grep' calls; this is the default. + For GNU grep, set it to `--color=always` to highlight matches even + when a pager is used. + +color.grep.match:: + Use customized color for matches. The value of this variable + may be specified as in color.branch.<slot>. It is passed using + the environment variables 'GREP_COLOR' and 'GREP_COLORS' when + calling an external 'grep'. + color.interactive:: When set to `always`, always use colors for interactive prompts and displays (such as those used by "git-add --interactive"). @@ -677,6 +705,16 @@ format.pretty:: See linkgit:git-log[1], linkgit:git-show[1], linkgit:git-whatchanged[1]. +format.thread:: + The default threading style for 'git-format-patch'. Can be + either a boolean value, `shallow` or `deep`. 'Shallow' + threading makes every mail a reply to the head of the series, + where the head is chosen from the cover letter, the + `\--in-reply-to`, and the first patch mail, in this order. + 'Deep' threading makes every mail a reply to the previous one. + A true boolean value is the same as `shallow`, and a false + value disables threading. + gc.aggressiveWindow:: The window size parameter used in the delta compression algorithm used by 'git-gc --aggressive'. This defaults @@ -1151,7 +1189,7 @@ pager.<cmd>:: particular git subcommand when writing to a tty. If `\--paginate` or `\--no-pager` is specified on the command line, it takes precedence over this option. To disable pagination for - all commands, set `core.pager` or 'GIT_PAGER' to "`cat`". + all commands, set `core.pager` or `GIT_PAGER` to `cat`. pull.octopus:: The default merge strategy to use when pulling multiple branches @@ -1160,6 +1198,23 @@ pull.octopus:: pull.twohead:: The default merge strategy to use when pulling a single branch. +push.default:: + Defines the action git push should take if no refspec is given + on the command line, no refspec is configured in the remote, and + no refspec is implied by any of the options given on the command + line. Possible values are: ++ +* `nothing` do not push anything. +* `matching` push all matching branches. + All branches having the same name in both ends are considered to be + matching. This is the default. +* `tracking` push the current branch to the branch it is tracking. +* `current` push the current branch to a branch of the same name. + +rebase.stat:: + Whether to show a diffstat of what changed upstream since the last + rebase. False by default. + receive.fsckObjects:: If it is set to true, git-receive-pack will check all received objects. It will abort in the case of a malformed object or a diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index ad38f7f39d..c1adf59497 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -10,6 +10,7 @@ SYNOPSIS -------- [verse] 'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>] + [--output=<file>] [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish> [path...] @@ -47,6 +48,9 @@ OPTIONS --prefix=<prefix>/:: Prepend <prefix>/ to each filename in the archive. +--output=<file>:: + Write the archive to <file> instead of stdout. + <extra>:: This can be any options that the archiver backend understands. See next section. diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index e5862b9dbb..ffc02c737c 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -217,7 +217,7 @@ If you have a script that can tell if the current source code is good or bad, you can bisect by issuing the command: ------------ -$ git bisect run my_script +$ git bisect run my_script arguments ------------ Note that the script (`my_script` in the above example) should @@ -257,6 +257,13 @@ $ git bisect start HEAD v1.2 -- # HEAD is bad, v1.2 is good $ git bisect run make # "make" builds the app ------------ +* Automatically bisect a test failure between origin and HEAD: ++ +------------ +$ git bisect start HEAD origin -- # HEAD is bad, origin is good +$ git bisect run make test # "make test" builds and tests +------------ + * Automatically bisect a broken test suite: + ------------ @@ -296,6 +303,15 @@ It is safer if both "test.sh" and "check_test_case.sh" scripts are outside the repository to prevent interactions between the bisect, make and test processes and the scripts. +* Automatically bisect a broken test suite: ++ +------------ +$ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 +$ git bisect run sh -c "make || exit 125; ~/check_test_case.sh" +------------ ++ +Does the same as the previous example, but on a single line. + Author ------ Written by Linus Torvalds <torvalds@osdl.org> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 7f7b781f24..31ba7f2ade 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -76,8 +76,8 @@ OPTIONS based sha1 expressions such as "<branchname>@\{yesterday}". -f:: - Force the creation of a new branch even if it means deleting - a branch that already exists with the same name. + Reset <branchname> to <startpoint> if <branchname> exists + already. Without `-f` 'git-branch' refuses to change an existing branch. -m:: Move/rename a branch and the corresponding reflog. diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 3bccffae62..1a6c19e5c3 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -8,7 +8,7 @@ git-checkout - Checkout a branch or paths to the working tree SYNOPSIS -------- [verse] -'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>] +'git checkout' [-q] [-f] [-t | --track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>] 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... DESCRIPTION @@ -21,15 +21,15 @@ specified, <new_branch>. Using -b will cause <new_branch> to be created; in this case you can use the --track or --no-track options, which will be passed to `git branch`. -As a convenience, --track will default to create a branch whose +As a convenience, --track will default to creating a branch whose name is constructed from the specified branch name by stripping the first namespace level. When <paths> are given, this command does *not* switch branches. It updates the named paths in the working tree from the index file, or from a named <tree-ish> (most often a commit). In -this case, the `-b` options is meaningless and giving -either of them results in an error. <tree-ish> argument can be +this case, the `-b` and `--track` options are meaningless and giving +either of them results in an error. The <tree-ish> argument can be used to specify a specific tree-ish (i.e. commit, tag or tree) to update the index for the given paths before updating the working tree. @@ -75,14 +75,13 @@ entries; instead, unmerged entries are ignored. <repository> <refspec>" explicitly. This behavior is the default when the start point is a remote branch. Set the branch.autosetupmerge configuration variable to `false` if you want - 'git-checkout' and 'git-branch' to always behave as if '--no-track' were + 'git checkout' and 'git branch' to always behave as if '--no-track' were given. Set it to `always` if you want this behavior when the - start-point is either a local or remote branch. + start point is either a local or remote branch. + -If no '-b' option was given, the name of the new branch will be -derived from the remote branch, by attempting to guess the name -of the branch on remote system. If "remotes/" or "refs/remotes/" -are prefixed, it is stripped away, and then the part up to the +If no '-b' option is given, the name of the new branch will be +derived from the remote branch. If "remotes/" or "refs/remotes/" +is prefixed it is stripped away, and then the part up to the next slash (which would be the nickname of the remote) is removed. This would tell us to use "hack" as the local branch when branching off of "origin/hack" (or "remotes/origin/hack", or even @@ -134,9 +133,9 @@ 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 +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\}`". +`-` which is synonymous with `"@\{-1\}"`. Detached HEAD @@ -152,12 +151,12 @@ $ git checkout v2.6.18 ------------ Earlier versions of git did not allow this and asked you to -create a temporary branch using `-b` option, but starting from +create a temporary branch using the `-b` option, but starting from version 1.5.0, the above command 'detaches' your HEAD from the -current branch and directly point at the commit named by the tag -(`v2.6.18` in the above example). +current branch and directly points at the commit named by the tag +(`v2.6.18` in the example above). -You can use usual git commands while in this state. You can use +You can use all git commands while in this state. You can use `git reset --hard $othercommit` to further move around, for example. You can make changes and create a new commit on top of a detached HEAD. You can even create a merge by using `git @@ -191,7 +190,7 @@ $ git checkout hello.c <3> ------------ + <1> switch branch -<2> take out a file out of other commit +<2> take a file out of another commit <3> restore hello.c from HEAD of current branch + If you have an unfortunate branch that is named `hello.c`, this @@ -202,7 +201,7 @@ You should instead write: $ git checkout -- hello.c ------------ -. After working in a wrong branch, switching to the correct +. After working in the wrong branch, switching to the correct branch would be done using: + ------------ @@ -210,7 +209,7 @@ $ git checkout mytopic ------------ + However, your "wrong" branch and correct "mytopic" branch may -differ in files that you have locally modified, in which case, +differ in files that you have modified locally, in which case the above checkout would fail like this: + ------------ diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 6ab2af4b61..7131ee3c66 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]] 'git config' [<file-option>] [type] --add name value -'git config' [<file-option>] [type] --replace-all name [value [value_regex]] +'git config' [<file-option>] [type] --replace-all name value [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex] @@ -22,6 +22,7 @@ SYNOPSIS 'git config' [<file-option>] [-z|--null] -l | --list 'git config' [<file-option>] --get-color name [default] 'git config' [<file-option>] --get-colorbool name [stdout-is-tty] +'git config' [<file-option>] -e | --edit DESCRIPTION ----------- @@ -161,6 +162,11 @@ See also <<FILES>>. output. The optional `default` parameter is used instead, if there is no color configured for `name`. +-e:: +--edit:: + Opens an editor to modify the specified config file; either + '--system', '--global', or repository (default). + [[FILES]] FILES ----- diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 8f9ba74c8b..d7bab13f6c 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -24,6 +24,9 @@ repository, or incrementally import into an existing one. Splitting the CVS log into patch sets is done by 'cvsps'. At least version 2.1 is required. +*WARNING:* for certain situations the import leads to incorrect results. +Please see the section <<issues,ISSUES>> for further reference. + You should *never* do any work of your own on the branches that are created by 'git-cvsimport'. By default initial import will create and populate a "master" branch from the CVS repository's main branch which you're free @@ -164,6 +167,39 @@ If '-v' is specified, the script reports what it is doing. Otherwise, success is indicated the Unix way, i.e. by simply exiting with a zero exit status. +[[issues]] +ISSUES +------ +Problems related to timestamps: + + * If timestamps of commits in the cvs repository are not stable enough + to be used for ordering commits changes may show up in the wrong + order. + * If any files were ever "cvs import"ed more than once (e.g., import of + more than one vendor release) the HEAD contains the wrong content. + * If the timestamp order of different files cross the revision order + within the commit matching time window the order of commits may be + wrong. + +Problems related to branches: + + * Branches on which no commits have been made are not imported. + * All files from the branching point are added to a branch even if + never added in cvs. + * This applies to files added to the source branch *after* a daughter + branch was created: if previously no commit was made on the daughter + branch they will erroneously be added to the daughter branch in git. + +Problems related to tags: + +* Multiple tags on the same revision are not imported. + +If you suspect that any of these issues may apply to the repository you +want to import consider using these alternative tools which proved to be +more stable in practise: + +* cvs2git (part of cvs2svn), `http://cvs2svn.tigris.org` +* parsecvs, `http://cgit.freedesktop.org/~keithp/parsecvs` Author ------ diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 7ffe03f427..237f85e767 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -91,7 +91,9 @@ OPTIONS --index-filter <command>:: This is the filter for rewriting the index. It is similar to the tree filter but does not check out the tree, which makes it much - faster. For hairy cases, see linkgit:git-update-index[1]. + faster. Frequently used with `git rm \--cached + \--ignore-unmatch ...`, see EXAMPLES below. For hairy + cases, see linkgit:git-update-index[1]. --parent-filter <command>:: This is the filter for rewriting the commit's parent list. @@ -204,19 +206,18 @@ However, if the file is absent from the tree of some commit, a simple `rm filename` will fail for that tree and commit. Thus you may instead want to use `rm -f filename` as the script. -A significantly faster version: +Using `\--index-filter` with 'git-rm' yields a significantly faster +version. Like with using `rm filename`, `git rm --cached filename` +will fail if the file is absent from the tree of a commit. If you +want to "completely forget" a file, it does not matter when it entered +history, so we also add `\--ignore-unmatch`: -------------------------------------------------------------------------- -git filter-branch --index-filter 'git rm --cached filename' HEAD +git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD -------------------------------------------------------------------------- Now, you will get the rewritten history saved in HEAD. -As with using `rm filename`, `git rm --cached filename` will fail -if the file is absent from the tree of a commit. If it is not important -whether the file is already absent from the tree, you can use -`git rm --cached --ignore-unmatch filename` instead. - To rewrite the repository to look as if `foodir/` had been its project root, and discard all other history: diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 3c29655d75..db3bdb5657 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -10,7 +10,8 @@ SYNOPSIS -------- [verse] 'git format-patch' [-k] [-o <dir> | --stdout] [--thread] - [--attach[=<boundary>] | --inline[=<boundary>]] + [--attach[=<boundary>] | --inline[=<boundary>] | + [--no-attach]] [-s | --signoff] [<common diff options>] [-n | --numbered | -N | --no-numbered] [--start-number <n>] [--numbered-files] @@ -112,15 +113,27 @@ include::diff-options.txt[] which is the commit message and the patch itself in the second part, with "Content-Disposition: attachment". +--no-attach:: + Disable the creation of an attachment, overriding the + configuration setting. + --inline[=<boundary>]:: Create multipart/mixed attachment, the first part of which is the commit message and the patch itself in the second part, with "Content-Disposition: inline". ---thread:: +--thread[=<style>]:: Add In-Reply-To and References headers to make the second and subsequent mails appear as replies to the first. Also generates the Message-Id header to reference. ++ +The optional <style> argument can be either `shallow` or `deep`. +'Shallow' threading makes every mail a reply to the head of the +series, where the head is chosen from the cover letter, the +`\--in-reply-to`, and the first patch mail, in this order. 'Deep' +threading makes every mail a reply to the previous one. If not +specified, defaults to the 'format.thread' configuration, or `shallow` +if that is not set. --in-reply-to=Message-Id:: Make the first mail (or all the mails with --no-thread) appear as a @@ -144,6 +157,11 @@ include::diff-options.txt[] Add a "Cc:" header to the email headers. This is in addition to any configured headers, and may be used multiple times. +--add-header=<header>:: + Add an arbitrary header to the email headers. This is in addition + to any configured headers, and may be used multiple times. + For example, --add-header="Organization: git-foo" + --cover-letter:: In addition to the patches, generate a cover letter file containing the shortlog and the overall diffstat. You can @@ -176,7 +194,8 @@ CONFIGURATION ------------- You can specify extra mail header lines to be added to each message in the repository configuration, new defaults for the subject prefix -and file suffix, and number patches when outputting more than one. +and file suffix, control attachements, and number patches when outputting +more than one. ------------ [format] @@ -185,6 +204,7 @@ and file suffix, and number patches when outputting more than one. suffix = .txt numbered = auto cc = <email> + attach [ = mime-boundary-string ] ------------ diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 553da6cbb1..fccb82deb4 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -17,6 +17,7 @@ SYNOPSIS [-l | --files-with-matches] [-L | --files-without-match] [-z | --null] [-c | --count] [--all-match] + [--color | --no-color] [-A <post-context>] [-B <pre-context>] [-C <context>] [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...] [<tree>...] @@ -105,6 +106,13 @@ OPTIONS Instead of showing every matched line, show the number of lines that match. +--color:: + Show colored matches. + +--no-color:: + Turn off match highlighting, even when the configuration file + gives the default to color output. + -[ABC] <context>:: Show `context` trailing (`A` -- after), or leading (`B` -- before), or both (`C` -- context) lines, and place a diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index 1685f04efe..024084b8b7 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -64,6 +64,13 @@ imap.sslverify:: used by the SSL/TLS connection. Default is `true`. Ignored when imap.tunnel is set. +imap.preformattedHTML:: + A boolean to enable/disable the use of html encoding when sending + a patch. An html encoded patch will be bracketed with <pre> + and have a content type of text/html. Ironically, enabling this + option causes Thunderbird to send the patch as a plain/text, + format=fixed email. Default is `false`. + Examples ~~~~~~~~ diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index f7be5846a6..c04ae739ed 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -40,8 +40,8 @@ include::merge-options.txt[] include::merge-strategies.txt[] -If you tried a merge which resulted in a complex conflicts and -would want to start over, you can recover with 'git-reset'. +If you tried a merge which resulted in complex conflicts and +want to start over, you can recover with 'git-reset'. CONFIGURATION ------------- @@ -146,7 +146,7 @@ And here is another line that is cleanly resolved or unmodified. ------------ The area where a pair of conflicting changes happened is marked with markers -"`<<<<<<<`", "`=======`", and "`>>>>>>>`". The part before the "`=======`" +`<<<<<<<`, `=======`, and `>>>>>>>`. The part before the `=======` is typically your side, and the part afterwards is typically their side. The default format does not show what the original said in the conflicting @@ -173,8 +173,8 @@ Git makes conflict resolution easy. And here is another line that is cleanly resolved or unmodified. ------------ -In addition to the "`<<<<<<<`", "`=======`", and "`>>>>>>>`" markers, it uses -another "`|||||||`" marker that is followed by the original text. You can +In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses +another `|||||||` marker that is followed by the original text. You can tell that the original just stated a fact, and your side simply gave in to that statement and gave up, while the other side tried to have a more positive attitude. You can sometimes come up with a better resolution by diff --git a/Documentation/git-patch-id.txt b/Documentation/git-patch-id.txt index 477785e134..253fc0fc25 100644 --- a/Documentation/git-patch-id.txt +++ b/Documentation/git-patch-id.txt @@ -20,7 +20,7 @@ IOW, you can use this thing to look for likely duplicate commits. When dealing with 'git-diff-tree' output, it takes advantage of the fact that the patch is prefixed with the object name of the -commit, and outputs two 40-byte hexadecimal string. The first +commit, and outputs two 40-byte hexadecimal strings. The first string is the patch ID, and the second string is the commit ID. This can be used to make a mapping from patch ID to commit ID. diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 4e7e5a719a..fd53c49fb8 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -24,8 +24,8 @@ every time you push into it, by setting up 'hooks' there. See documentation for linkgit:git-receive-pack[1]. -OPTIONS -------- +OPTIONS[[OPTIONS]] +------------------ <repository>:: The "remote" repository that is destination of a push operation. This parameter can be either a URL @@ -187,6 +187,28 @@ reason:: Examples -------- +git push:: + Works like `git push <remote>`, where <remote> is the + current branch's remote (or `origin`, if no remote is + configured for the current branch). + +git push origin:: + Without additional configuration, works like + `git push origin :`. ++ +The default behavior of this command when no <refspec> is given can be +configured by setting the `push` option of the remote. ++ +For example, to default to pushing only the current branch to `origin` +use `git config remote.origin.push HEAD`. Any valid <refspec> (like +the ones in the examples below) can be configured as the default for +`git push origin`. + +git push origin ::: + Push "matching" branches to `origin`. See + <refspec> in the <<OPTIONS,OPTIONS>> section above for a + description of "matching" branches. + git push origin master:: Find a ref that matches `master` in the source repository (most likely, it would find `refs/heads/master`), and update diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index da3c38cd60..3d5a066c31 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -192,6 +192,13 @@ Alternatively, you can undo the 'git-rebase' with git rebase --abort +CONFIGURATION +------------- + +rebase.stat:: + Whether to show a diffstat of what changed upstream since the last + rebase. False by default. + OPTIONS ------- <newbase>:: @@ -232,7 +239,15 @@ OPTIONS -v:: --verbose:: - Display a diffstat of what changed upstream since the last rebase. + Be verbose. Implies --stat. + +--stat:: + Show a diffstat of what changed upstream since the last rebase. The + diffstat is also controlled by the configuration option rebase.stat. + +-n:: +--no-stat:: + Do not show a diffstat as part of the rebase process. --no-verify:: This option bypasses the pre-rebase hook. See also linkgit:githooks[5]. @@ -243,11 +258,23 @@ OPTIONS context exist they all must match. By default no context is ever ignored. +-f:: +--force-rebase:: + Force the rebase even if the current branch is a descendant + of the commit you are rebasing onto. Normally the command will + exit with the message "Current branch is up to date" in such a + situation. + --whitespace=<option>:: This flag is passed to the 'git-apply' program (see linkgit:git-apply[1]) that applies the patch. Incompatible with the --interactive option. +--committer-date-is-author-date:: +--ignore-date:: + These flags are passed to 'git-am' to easily change the dates + of the rebased commits (see linkgit:git-am[1]). + -i:: --interactive:: Make a list of the commits which are about to be rebased. Let the diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index fad983e297..c9c0e6f932 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -13,6 +13,7 @@ SYNOPSIS 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url> 'git remote rename' <old> <new> 'git remote rm' <name> +'git remote set-head' <name> [-a | -d | <branch>] 'git remote show' [-n] <name> 'git remote prune' [-n | --dry-run] <name> 'git remote update' [group] @@ -53,8 +54,7 @@ is created. You can give more than one `-t <branch>` to track multiple branches without grabbing all branches. + With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set -up to point at remote's `<master>` branch instead of whatever -branch the `HEAD` at the remote repository actually points at. +up to point at remote's `<master>` branch. See also the set-head command. + In mirror mode, enabled with `\--mirror`, the refs will not be stored in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option @@ -76,6 +76,30 @@ the configuration file format. Remove the remote named <name>. All remote tracking branches and configuration settings for the remote are removed. +'set-head':: + +Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for +the named remote. Having a default branch for a remote is not required, +but allows the name of the remote to be specified in lieu of a specific +branch. For example, if the default branch for `origin` is set to +`master`, then `origin` may be specified wherever you would normally +specify `origin/master`. ++ +With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted. ++ +With `-a`, the remote is queried to determine its `HEAD`, then +`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote +`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set +`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will +only work if `refs/remotes/origin/next` already exists; if not it must be +fetched first. ++ +Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git +remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to +`refs/remotes/origin/master`. This will only work if +`refs/remotes/origin/master` already exists; if not it must be fetched first. ++ + 'show':: Gives some information about the remote <name>. diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 3ccef2f2b3..5ed2bc840f 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -299,18 +299,18 @@ previous section means the set of commits reachable from that commit, following the commit ancestry chain. To exclude commits reachable from a commit, a prefix `{caret}` -notation is used. E.g. "`{caret}r1 r2`" means commits reachable +notation is used. E.g. `{caret}r1 r2` means commits reachable from `r2` but exclude the ones reachable from `r1`. This set operation appears so often that there is a shorthand for it. When you have two commits `r1` and `r2` (named according to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those that are reachable -from r1 by "`{caret}r1 r2`" and it can be written as "`r1..r2`". +from r1 by `{caret}r1 r2` and it can be written as `r1..r2`. -A similar notation "`r1\...r2`" is called symmetric difference +A similar notation `r1\...r2` is called symmetric difference of `r1` and `r2` and is defined as -"`r1 r2 --not $(git merge-base --all r1 r2)`". +`r1 r2 --not $(git merge-base --all r1 r2)`. It is the set of commits that are reachable from either one of `r1` or `r2` but not from both. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index fc0a4ab441..10dfd667b2 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -60,14 +60,13 @@ The --cc option must be repeated for each user you want on the cc list. Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an introductory message for the patch series. + -When '--compose' is used, git send-email gets less interactive will use the -values of the headers you set there. If the body of the email (what you type -after the headers and a blank line) only contains blank (or GIT: prefixed) -lines, the summary won't be sent, but git-send-email will still use the -Headers values if you don't removed them. +When '--compose' is used, git send-email will use the From, Subject, and +In-Reply-To headers specified in the message. If the body of the message +(what you type after the headers and a blank line) only contains blank +(or GIT: prefixed) lines the summary won't be sent, but From, Subject, +and In-Reply-To headers will be used unless they are removed. + -If it wasn't able to see a header in the summary it will ask you about it -interactively after quitting your editor. +Missing From or In-Reply-To headers will be prompted for. --from:: Specify the sender of the emails. This will default to @@ -212,6 +211,22 @@ specified, as well as 'body' if --no-signed-off-cc is specified. Administering ~~~~~~~~~~~~~ +--confirm:: + Confirm just before sending: ++ +-- +- 'always' will always confirm before sending +- 'never' will never confirm before sending +- 'cc' will confirm before sending when send-email has automatically + added addresses from the patch to the Cc list +- 'compose' will confirm before sending the first message when using --compose. +- 'auto' is equivalent to 'cc' + 'compose' +-- ++ +Default is the value of 'sendemail.confirm' configuration value; if that +is unspecified, default to 'auto' unless any of the suppress options +have been specified, in which case default to 'compose'. + --dry-run:: Do everything except actually send the emails. @@ -255,6 +270,11 @@ sendemail.multiedit:: summary when '--compose' is used). If false, files will be edited one after the other, spawning a new editor each time. +sendemail.confirm:: + Sets the default for whether to confirm before sending. Must be + one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm' + in the previous section for the meaning of these values. + Author ------ diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index cda3389331..b7b1af813d 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -385,7 +385,8 @@ config key: svn.authorsfile -q:: --quiet:: - Make 'git-svn' less verbose. + Make 'git-svn' less verbose. Specify a second time to make it + even less verbose. --repack[=<n>]:: --repack-flags=<flags>:: @@ -672,9 +673,9 @@ listed below are allowed: ------------------------------------------------------------------------ [svn-remote "project-a"] url = http://server.org/svn + fetch = trunk/project-a:refs/remotes/project-a/trunk branches = branches/*/project-a:refs/remotes/project-a/branches/* tags = tags/*/project-a:refs/remotes/project-a/tags/* - trunk = trunk/project-a:refs/remotes/project-a/trunk ------------------------------------------------------------------------ Keep in mind that the '*' (asterisk) wildcard of the local ref diff --git a/Documentation/git.txt b/Documentation/git.txt index 9a26bde73e..7513c57c6a 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.2/git.html[documentation for release 1.6.2] +* link:v1.6.2.1/git.html[documentation for release 1.6.2.1] * release notes for + link:RelNotes-1.6.2.1.txt[1.6.2.1], link:RelNotes-1.6.2.txt[1.6.2]. * link:v1.6.1.3/git.html[documentation for release 1.6.1.3] diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 29e5929db2..be39ed7c15 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -46,20 +46,20 @@ Here are the rules regarding the "flags" that you should follow when you are scripting git: * it's preferred to use the non dashed form of git commands, which means that - you should prefer `"git foo"` to `"git-foo"`. + you should prefer `git foo` to `git-foo`. - * splitting short options to separate words (prefer `"git foo -a -b"` - to `"git foo -ab"`, the latter may not even work). + * splitting short options to separate words (prefer `git foo -a -b` + to `git foo -ab`, the latter may not even work). * when a command line option takes an argument, use the 'sticked' form. In - other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short - options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"` + other words, write `git foo -oArg` instead of `git foo -o Arg` for short + options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg` for long options. An option that takes optional option-argument must be written in the 'sticked' form. * when you give a revision parameter to a command, make sure the parameter is not ambiguous with a name of a file in the work tree. E.g. do not write - `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work + `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work if you happen to have a file called `HEAD` in the work tree. @@ -99,17 +99,17 @@ usage: git-describe [options] <committish>* Negating options ~~~~~~~~~~~~~~~~ -Options with long option names can be negated by prefixing `"--no-"`. For -example, `"git branch"` has the option `"--track"` which is 'on' by default. You -can use `"--no-track"` to override that behaviour. The same goes for `"--color"` -and `"--no-color"`. +Options with long option names can be negated by prefixing `--no-`. For +example, `git branch` has the option `--track` which is 'on' by default. You +can use `--no-track` to override that behaviour. The same goes for `--color` +and `--no-color`. Aggregating short options ~~~~~~~~~~~~~~~~~~~~~~~~~ Commands that support the enhanced option parser allow you to aggregate short -options. This means that you can for example use `"git rm -rf"` or -`"git clean -fdx"`. +options. This means that you can for example use `git rm -rf` or +`git clean -fdx`. Separating argument from the option diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 1fd512bca2..1c736738cc 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -151,6 +151,10 @@ indicating whether the checkout was a branch checkout (changing branches, flag=1) or a file checkout (retrieving a file from the index, flag=0). This hook cannot affect the outcome of 'git-checkout'. +It is also run after 'git-clone', unless the --no-checkout (-n) option is +used. The first parameter given to the hook is the null-ref, the second the +ref of the new HEAD and the flag is always 1. + This hook can be used to perform repository validity checks, auto-display differences from the previous HEAD if different, or set working dir metadata properties. diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 9afca755ed..4fc1cf1184 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -262,7 +262,7 @@ This commit is referred to as a "merge commit", or sometimes just a 'origin' is used for that purpose. New upstream updates will be fetched into remote <<def_tracking_branch,tracking branches>> named origin/name-of-upstream-branch, which you can see using - "`git branch -r`". + `git branch -r`. [[def_pack]]pack:: A set of objects which have been compressed into one file (to save space diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl index 4065a3a27a..b4d315cb8c 100644 --- a/Documentation/manpage-1.72.xsl +++ b/Documentation/manpage-1.72.xsl @@ -1,21 +1,14 @@ -<!-- Based on callouts.xsl. Fixes man page callouts for DocBook 1.72 XSL --> -<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> +<!-- manpage-1.72.xsl: + special settings for manpages rendered from asciidoc+docbook + handles peculiarities in docbook-xsl 1.72.0 --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> -<xsl:param name="man.output.quietly" select="1"/> -<xsl:param name="refentry.meta.get.quietly" select="1"/> +<xsl:import href="manpage-base.xsl"/> -<xsl:template match="co"> - <xsl:value-of select="concat('▓fB(',substring-after(@id,'-'),')▓fR')"/> -</xsl:template> -<xsl:template match="calloutlist"> - <xsl:text>⌂sp </xsl:text> - <xsl:apply-templates/> - <xsl:text> </xsl:text> -</xsl:template> -<xsl:template match="callout"> - <xsl:value-of select="concat('▓fB',substring-after(@arearefs,'-'),'. ▓fR')"/> - <xsl:apply-templates/> - <xsl:text>⌂br </xsl:text> -</xsl:template> +<!-- these are the special values for the roff control characters + needed for docbook-xsl 1.72.0 --> +<xsl:param name="git.docbook.backslash">▓</xsl:param> +<xsl:param name="git.docbook.dot" >⌂</xsl:param> </xsl:stylesheet> diff --git a/Documentation/manpage-base.xsl b/Documentation/manpage-base.xsl new file mode 100644 index 0000000000..a264fa6160 --- /dev/null +++ b/Documentation/manpage-base.xsl @@ -0,0 +1,35 @@ +<!-- manpage-base.xsl: + special formatting for manpages rendered from asciidoc+docbook --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + +<!-- these params silence some output from xmlto --> +<xsl:param name="man.output.quietly" select="1"/> +<xsl:param name="refentry.meta.get.quietly" select="1"/> + +<!-- convert asciidoc callouts to man page format; + git.docbook.backslash and git.docbook.dot params + must be supplied by another XSL file or other means --> +<xsl:template match="co"> + <xsl:value-of select="concat( + $git.docbook.backslash,'fB(', + substring-after(@id,'-'),')', + $git.docbook.backslash,'fR')"/> +</xsl:template> +<xsl:template match="calloutlist"> + <xsl:value-of select="$git.docbook.dot"/> + <xsl:text>sp </xsl:text> + <xsl:apply-templates/> + <xsl:text> </xsl:text> +</xsl:template> +<xsl:template match="callout"> + <xsl:value-of select="concat( + $git.docbook.backslash,'fB', + substring-after(@arearefs,'-'), + '. ',$git.docbook.backslash,'fR')"/> + <xsl:apply-templates/> + <xsl:value-of select="$git.docbook.dot"/> + <xsl:text>br </xsl:text> +</xsl:template> + +</xsl:stylesheet> diff --git a/Documentation/manpage-bold-literal.xsl b/Documentation/manpage-bold-literal.xsl new file mode 100644 index 0000000000..608eb5df62 --- /dev/null +++ b/Documentation/manpage-bold-literal.xsl @@ -0,0 +1,17 @@ +<!-- manpage-bold-literal.xsl: + special formatting for manpages rendered from asciidoc+docbook --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + +<!-- render literal text as bold (instead of plain or monospace); + this makes literal text easier to distinguish in manpages + viewed on a tty --> +<xsl:template match="literal"> + <xsl:value-of select="$git.docbook.backslash"/> + <xsl:text>fB</xsl:text> + <xsl:apply-templates/> + <xsl:value-of select="$git.docbook.backslash"/> + <xsl:text>fR</xsl:text> +</xsl:template> + +</xsl:stylesheet> diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl new file mode 100644 index 0000000000..a48f5b11f3 --- /dev/null +++ b/Documentation/manpage-normal.xsl @@ -0,0 +1,13 @@ +<!-- manpage-normal.xsl: + special settings for manpages rendered from asciidoc+docbook + handles anything we want to keep away from docbook-xsl 1.72.0 --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + +<xsl:import href="manpage-base.xsl"/> + +<!-- these are the normal values for the roff control characters --> +<xsl:param name="git.docbook.backslash">\</xsl:param> +<xsl:param name="git.docbook.dot" >.</xsl:param> + +</xsl:stylesheet> diff --git a/Documentation/manpage-suppress-sp.xsl b/Documentation/manpage-suppress-sp.xsl new file mode 100644 index 0000000000..a63c7632a8 --- /dev/null +++ b/Documentation/manpage-suppress-sp.xsl @@ -0,0 +1,21 @@ +<!-- manpage-suppress-sp.xsl: + special settings for manpages rendered from asciidoc+docbook + handles erroneous, inline .sp in manpage output of some + versions of docbook-xsl --> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + +<!-- attempt to work around spurious .sp at the tail of the line + that some versions of docbook stylesheets seem to add --> +<xsl:template match="simpara"> + <xsl:variable name="content"> + <xsl:apply-templates/> + </xsl:variable> + <xsl:value-of select="normalize-space($content)"/> + <xsl:if test="not(ancestor::authorblurb) and + not(ancestor::personblurb)"> + <xsl:text> </xsl:text> + </xsl:if> +</xsl:template> + +</xsl:stylesheet> diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt index 1276f858ad..4365b7e842 100644 --- a/Documentation/merge-strategies.txt +++ b/Documentation/merge-strategies.txt @@ -3,15 +3,15 @@ MERGE STRATEGIES resolve:: This can only resolve two heads (i.e. the current branch - and another branch you pulled from) using 3-way merge + and another branch you pulled from) using a 3-way merge algorithm. It tries to carefully detect criss-cross merge ambiguities and is considered generally safe and fast. recursive:: - This can only resolve two heads using 3-way merge - algorithm. When there are more than one common - ancestors that can be used for 3-way merge, it creates a + This can only resolve two heads using a 3-way merge + algorithm. When there is more than one common + ancestor that can be used for 3-way merge, it creates a merged tree of the common ancestors and uses that as the reference tree for the 3-way merge. This has been reported to result in fewer merge conflicts without @@ -22,11 +22,11 @@ recursive:: pulling or merging one branch. octopus:: - This resolves more than two-head case, but refuses to do - complex merge that needs manual resolution. It is + This resolves cases with more than two heads, but refuses to do + a complex merge that needs manual resolution. It is primarily meant to be used for bundling topic branch heads together. This is the default merge strategy when - pulling or merging more than one branches. + pulling or merging more than one branch. ours:: This resolves any number of heads, but the result of the diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 159390c35a..5c6e678aa3 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -152,3 +152,12 @@ $ git log -2 --pretty=tformat:%h 4da45bef \ 4da45be 7134973 --------------------- ++ +In addition, any unrecognized string that has a `%` in it is interpreted +as if it has `tformat:` in front of it. For example, these two are +equivalent: ++ +--------------------- +$ git log -2 --pretty=tformat:%h 4da45bef +$ git log -2 --pretty=%h 4da45bef +--------------------- diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 5f21efe407..bff94991b6 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -1,4 +1,5 @@ --pretty[='<format>']:: +--format[='<format>']:: Pretty-print the contents of the commit logs in a given format, where '<format>' can be one of 'oneline', 'short', 'medium', @@ -17,6 +18,10 @@ configuration (see linkgit:git-config[1]). This should make "--pretty=oneline" a whole lot more readable for people using 80-column terminals. +--oneline:: + This is a shorthand for "--pretty=oneline --abbrev-commit" + used together. + --encoding[=<encoding>]:: The commit objects record the encoding used for the log message in their encoding header; this option can be used to tell the diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 539863b1f9..e66ca9f70c 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -66,6 +66,12 @@ Steps to parse options non-option arguments in `argv[]`. `argc` is updated appropriately because of the assignment. + +You can also pass NULL instead of a usage array as fourth parameter of +parse_options(), to avoid displaying a help screen with usage info and +option list. This should only be done if necessary, e.g. to implement +a limited parser for only a subset of the options that needs to be run +before the full parser, which in turn shows the full help message. ++ Flags are the bitwise-or of: `PARSE_OPT_KEEP_DASHDASH`:: @@ -77,6 +83,28 @@ Flags are the bitwise-or of: Using this flag, processing is stopped at the first non-option argument. +`PARSE_OPT_KEEP_ARGV0`:: + Keep the first argument, which contains the program name. It's + removed from argv[] by default. + +`PARSE_OPT_KEEP_UNKNOWN`:: + Keep unknown arguments instead of erroring out. This doesn't + work for all combinations of arguments as users might expect + it to do. E.g. if the first argument in `--unknown --known` + takes a value (which we can't know), the second one is + mistakenly interpreted as a known option. Similarly, if + `PARSE_OPT_STOP_AT_NON_OPTION` is set, the second argument in + `--unknown value` will be mistakenly interpreted as a + non-option, not as a value belonging to the unknown option, + the parser early. That's why parse_options() errors out if + both options are set. + +`PARSE_OPT_NO_INTERNAL_HELP`:: + By default, parse_options() handles `-h`, `--help` and + `--help-all` internally, by showing a help screen. This option + turns it off and allows one to add custom handlers for these + options, or to just leave them unknown. + Data Structure -------------- diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 96af8977f6..e33b29b1dd 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1136,10 +1136,10 @@ Ignoring files A project will often generate files that you do 'not' want to track with git. This typically includes files generated by a build process or temporary backup files made by your editor. Of course, 'not' tracking files with git -is just a matter of 'not' calling "`git-add`" on them. But it quickly becomes +is just a matter of 'not' calling `git-add` on them. But it quickly becomes annoying to have these untracked files lying around; e.g. they make -"`git add .`" practically useless, and they keep showing up in the output of -"`git status`". +`git add .` practically useless, and they keep showing up in the output of +`git status`. You can tell git to ignore certain files by creating a file called .gitignore in the top level of your working directory, with contents such as: diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index ac2f24795c..97fc1e0519 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.2.2 +DEF_VER=v1.6.2.GIT LF=' ' @@ -126,6 +126,12 @@ all:: # randomly break unless your underlying filesystem supports those sub-second # times (my ext3 doesn't). # +# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of +# "st_ctim" +# +# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec" +# available. This automatically turns USE_NSEC off. +# # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-index perspective. # @@ -257,6 +263,18 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ BASIC_CFLAGS = BASIC_LDFLAGS = +# Guard against environment variables +BUILTIN_OBJS = +BUILT_INS = +COMPAT_CFLAGS = +COMPAT_OBJS = +LIB_H = +LIB_OBJS = +PROGRAMS = +SCRIPT_PERL = +SCRIPT_SH = +TEST_PROGRAMS = + SCRIPT_SH += git-am.sh SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-filter-branch.sh @@ -657,6 +675,7 @@ ifeq ($(uname_S),Darwin) endif NO_MEMMEM = YesPlease THREADED_DELTA_SEARCH = YesPlease + USE_ST_TIMESPEC = YesPlease endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -706,6 +725,7 @@ ifeq ($(uname_S),FreeBSD) BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease + USE_ST_TIMESPEC = YesPlease THREADED_DELTA_SEARCH = YesPlease ifeq ($(shell expr "$(uname_R)" : '4\.'),2) PTHREAD_LIBS = -pthread @@ -734,6 +754,7 @@ ifeq ($(uname_S),AIX) NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease NO_STRLCPY = YesPlease + NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes INTERNAL_QSORT = UnfortunatelyYes NEEDS_LIBICONV=YesPlease @@ -776,7 +797,6 @@ ifneq (,$(findstring CYGWIN,$(uname_S))) COMPAT_OBJS += compat/cygwin.o endif ifneq (,$(findstring MINGW,$(uname_S))) - NO_MMAP = YesPlease NO_PREAD = YesPlease NO_OPENSSL = YesPlease NO_CURL = YesPlease @@ -799,6 +819,8 @@ ifneq (,$(findstring MINGW,$(uname_S))) RUNTIME_PREFIX = YesPlease NO_POSIX_ONLY_PROGRAMS = YesPlease NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease + USE_WIN32_MMAP = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" @@ -920,6 +942,15 @@ endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif +ifdef USE_NSEC + BASIC_CFLAGS += -DUSE_NSEC +endif +ifdef USE_ST_TIMESPEC + BASIC_CFLAGS += -DUSE_ST_TIMESPEC +endif +ifdef NO_NSEC + BASIC_CFLAGS += -DNO_NSEC +endif ifdef NO_C99_FORMAT BASIC_CFLAGS += -DNO_C99_FORMAT endif @@ -967,6 +998,11 @@ endif ifdef NO_MMAP COMPAT_CFLAGS += -DNO_MMAP COMPAT_OBJS += compat/mmap.o +else + ifdef USE_WIN32_MMAP + COMPAT_CFLAGS += -DUSE_WIN32_MMAP + COMPAT_OBJS += compat/win32mmap.o + endif endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD @@ -1363,6 +1399,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ + @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK @@ -1640,3 +1677,27 @@ check-docs:: check-builtins:: ./check-builtins.sh +### Test suite coverage testing +# +.PHONY: coverage coverage-clean coverage-build coverage-report + +coverage: + $(MAKE) coverage-build + $(MAKE) coverage-report + +coverage-clean: + rm -f *.gcda *.gcno + +COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs +COVERAGE_LDFLAGS = $(CFLAGS) -O0 -lgcov + +coverage-build: coverage-clean + $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all + $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \ + -j1 test + +coverage-report: + gcov -b *.c + grep '^function.*called 0 ' *.c.gcov \ + | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \ + | tee coverage-untested-functions @@ -1 +1 @@ -Documentation/RelNotes-1.6.2.2.txt
\ No newline at end of file +Documentation/RelNotes-1.6.3.txt
\ No newline at end of file @@ -253,6 +253,7 @@ static int parse_archive_args(int argc, const char **argv, const char *base = NULL; const char *remote = NULL; const char *exec = NULL; + const char *output = NULL; int compression_level = -1; int verbose = 0; int i; @@ -262,6 +263,8 @@ static int parse_archive_args(int argc, const char **argv, OPT_STRING(0, "format", &format, "fmt", "archive format"), OPT_STRING(0, "prefix", &base, "prefix", "prepend prefix to each pathname in the archive"), + OPT_STRING(0, "output", &output, "file", + "write the archive to this file"), OPT__VERBOSE(&verbose), OPT__COMPR('0', &compression_level, "store only", 0), OPT__COMPR('1', &compression_level, "compress faster", 1), @@ -290,6 +293,8 @@ static int parse_archive_args(int argc, const char **argv, die("Unexpected option --remote"); if (exec) die("Option --exec can only be used together with --remote"); + if (output) + die("Unexpected option --output"); if (!base) base = ""; @@ -1,3 +1,4 @@ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "attr.h" @@ -318,6 +319,9 @@ static struct attr_stack *read_attr_from_array(const char **list) return res; } +static enum git_attr_direction direction; +static struct index_state *use_index; + static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) { FILE *fp = fopen(path, "r"); @@ -340,9 +344,10 @@ static void *read_index_data(const char *path) unsigned long sz; enum object_type type; void *data; + struct index_state *istate = use_index ? use_index : &the_index; len = strlen(path); - pos = cache_name_pos(path, len); + pos = index_name_pos(istate, path, len); if (pos < 0) { /* * We might be in the middle of a merge, in which @@ -350,15 +355,15 @@ static void *read_index_data(const char *path) */ int i; for (i = -pos - 1; - (pos < 0 && i < active_nr && - !strcmp(active_cache[i]->name, path)); + (pos < 0 && i < istate->cache_nr && + !strcmp(istate->cache[i]->name, path)); i++) - if (ce_stage(active_cache[i]) == 2) + if (ce_stage(istate->cache[i]) == 2) pos = i; } if (pos < 0) return NULL; - data = read_sha1_file(active_cache[pos]->sha1, &type, &sz); + data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -366,27 +371,17 @@ static void *read_index_data(const char *path) return data; } -static struct attr_stack *read_attr(const char *path, int macro_ok) +static struct attr_stack *read_attr_from_index(const char *path, int macro_ok) { struct attr_stack *res; char *buf, *sp; int lineno = 0; - res = read_attr_from_file(path, macro_ok); - if (res) - return res; - - res = xcalloc(1, sizeof(*res)); - - /* - * There is no checked out .gitattributes file there, but - * we might have it in the index. We allow operation in a - * sparsely checked out work tree, so read from it. - */ buf = read_index_data(path); if (!buf) - return res; + return NULL; + res = xcalloc(1, sizeof(*res)); for (sp = buf; *sp; ) { char *ep; int more; @@ -401,6 +396,30 @@ static struct attr_stack *read_attr(const char *path, int macro_ok) return res; } +static struct attr_stack *read_attr(const char *path, int macro_ok) +{ + struct attr_stack *res; + + if (direction == GIT_ATTR_CHECKOUT) { + res = read_attr_from_index(path, macro_ok); + if (!res) + res = read_attr_from_file(path, macro_ok); + } + else { + res = read_attr_from_file(path, macro_ok); + if (!res) + /* + * There is no checked out .gitattributes file there, but + * we might have it in the index. We allow operation in a + * sparsely checked out work tree, so read from it. + */ + res = read_attr_from_index(path, macro_ok); + } + if (!res) + res = xcalloc(1, sizeof(*res)); + return res; +} + #if DEBUG_ATTR static void debug_info(const char *what, struct attr_stack *elem) { @@ -428,6 +447,15 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr #define debug_set(a,b,c,d) do { ; } while (0) #endif +static void drop_attr_stack(void) +{ + while (attr_stack) { + struct attr_stack *elem = attr_stack; + attr_stack = elem->prev; + free_attr_elem(elem); + } +} + static void bootstrap_attr_stack(void) { if (!attr_stack) { @@ -642,3 +670,12 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check) return 0; } + +void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate) +{ + enum git_attr_direction old = direction; + direction = new; + if (new != old) + drop_attr_stack(); + use_index = istate; +} @@ -31,4 +31,10 @@ struct git_attr_check { int git_checkattr(const char *path, int, struct git_attr_check *); +enum git_attr_direction { + GIT_ATTR_CHECKIN, + GIT_ATTR_CHECKOUT +}; +void git_attr_set_direction(enum git_attr_direction, struct index_state *); + #endif /* ATTR_H */ @@ -32,21 +32,59 @@ static int find_tracked_branch(struct remote *remote, void *priv) return 0; } -static int should_setup_rebase(const struct tracking *tracking) +static int should_setup_rebase(const char *origin) { switch (autorebase) { case AUTOREBASE_NEVER: return 0; case AUTOREBASE_LOCAL: - return tracking->remote == NULL; + return origin == NULL; case AUTOREBASE_REMOTE: - return tracking->remote != NULL; + return origin != NULL; case AUTOREBASE_ALWAYS: return 1; } return 0; } +void install_branch_config(int flag, const char *local, const char *origin, const char *remote) +{ + struct strbuf key = STRBUF_INIT; + int rebasing = should_setup_rebase(origin); + + strbuf_addf(&key, "branch.%s.remote", local); + git_config_set(key.buf, origin ? origin : "."); + + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.merge", local); + git_config_set(key.buf, remote); + + if (rebasing) { + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.rebase", local); + git_config_set(key.buf, "true"); + } + + if (flag & BRANCH_CONFIG_VERBOSE) { + strbuf_reset(&key); + + strbuf_addstr(&key, origin ? "remote" : "local"); + + /* Are we tracking a proper "branch"? */ + if (!prefixcmp(remote, "refs/heads/")) { + strbuf_addf(&key, " branch %s", remote + 11); + if (origin) + strbuf_addf(&key, " from %s", origin); + } + else + strbuf_addf(&key, " ref %s", remote); + printf("Branch %s set up to track %s%s.\n", + local, key.buf, + rebasing ? " by rebasing" : ""); + } + strbuf_release(&key); +} + /* * This is called when new_ref is branched off of orig_ref, and tries * to infer the settings for branch.<new_ref>.{remote,merge} from the @@ -55,7 +93,6 @@ static int should_setup_rebase(const struct tracking *tracking) static int setup_tracking(const char *new_ref, const char *orig_ref, enum branch_track track) { - char key[1024]; struct tracking tracking; if (strlen(new_ref) > 1024 - 7 - 7 - 1) @@ -80,19 +117,10 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, return error("Not tracking: ambiguous information for ref %s", orig_ref); - sprintf(key, "branch.%s.remote", new_ref); - git_config_set(key, tracking.remote ? tracking.remote : "."); - sprintf(key, "branch.%s.merge", new_ref); - git_config_set(key, tracking.src ? tracking.src : orig_ref); - printf("Branch %s set up to track %s branch %s.\n", new_ref, - tracking.remote ? "remote" : "local", orig_ref); - if (should_setup_rebase(&tracking)) { - sprintf(key, "branch.%s.rebase", new_ref); - git_config_set(key, "true"); - printf("This branch will rebase on pull.\n"); - } - free(tracking.src); + install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote, + tracking.src ? tracking.src : orig_ref); + free(tracking.src); return 0; } @@ -21,4 +21,11 @@ void create_branch(const char *head, const char *name, const char *start_name, */ void remove_branch_state(void); +/* + * Configure local branch "local" to merge remote branch "remote" + * taken from origin "origin". + */ +#define BRANCH_CONFIG_VERBOSE 01 +extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote); + #endif diff --git a/builtin-add.c b/builtin-add.c index 08443f2f1e..cb67d2c17e 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -104,7 +104,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, /* Set up the default git porcelain excludes */ memset(dir, 0, sizeof(*dir)); if (!ignored_too) { - dir->collect_ignored = 1; + dir->flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(dir); } @@ -148,7 +148,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p if (pathspec) { const char **p; for (p = pathspec; *p; p++) { - if (has_symlink_leading_path(strlen(*p), *p)) { + if (has_symlink_leading_path(*p, strlen(*p))) { int len = prefix ? strlen(prefix) : 0; die("'%s' is beyond a symbolic link", *p + len); } diff --git a/builtin-apply.c b/builtin-apply.c index f312798af3..1926cd8055 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2360,7 +2360,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists) * In such a case, path "new_name" does not exist as * far as git is concerned. */ - if (has_symlink_leading_path(strlen(new_name), new_name)) + if (has_symlink_leading_path(new_name, strlen(new_name))) return 0; return error("%s: already exists in working directory", new_name); @@ -2451,7 +2451,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s if ((st_mode ^ patch->old_mode) & S_IFMT) return error("%s: wrong type", old_name); if (st_mode != patch->old_mode) - fprintf(stderr, "warning: %s has type %o, expected %o\n", + warning("%s has type %o, expected %o", old_name, st_mode, patch->old_mode); if (!patch->new_mode && !patch->is_delete) patch->new_mode = st_mode; @@ -2932,8 +2932,7 @@ static int write_out_one_reject(struct patch *patch) cnt = strlen(patch->new_name); if (ARRAY_SIZE(namebuf) <= cnt + 5) { cnt = ARRAY_SIZE(namebuf) - 5; - fprintf(stderr, - "warning: truncating .rej filename to %.*s.rej", + warning("truncating .rej filename to %.*s.rej", cnt - 1, patch->new_name); } memcpy(namebuf, patch->new_name, cnt); @@ -3212,7 +3211,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) struct option builtin_apply_options[] = { { OPTION_CALLBACK, 0, "exclude", NULL, "path", - "don´t apply changes matching the given path", + "don't apply changes matching the given path", 0, option_parse_exclude }, { OPTION_CALLBACK, 0, "include", NULL, "path", "apply changes matching the given path", @@ -3224,10 +3223,10 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) "ignore additions made by the patch"), OPT_BOOLEAN(0, "stat", &diffstat, "instead of applying the patch, output diffstat for the input"), - OPT_BOOLEAN(0, "allow-binary-replacement", &binary, - "now no-op"), - OPT_BOOLEAN(0, "binary", &binary, - "now no-op"), + { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary, + NULL, "old option, now no-op", PARSE_OPT_HIDDEN }, + { OPTION_BOOLEAN, 0, "binary", &binary, + NULL, "old option, now no-op", PARSE_OPT_HIDDEN }, OPT_BOOLEAN(0, "numstat", &numstat, "shows number of added and deleted lines in decimal notation"), OPT_BOOLEAN(0, "summary", &summary, @@ -3315,8 +3314,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) squelch_whitespace_errors < whitespace_error) { int squelched = whitespace_error - squelch_whitespace_errors; - fprintf(stderr, "warning: squelched %d " - "whitespace error%s\n", + warning("squelched %d " + "whitespace error%s", squelched, squelched == 1 ? "" : "s"); } @@ -3326,12 +3325,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) whitespace_error == 1 ? "" : "s", whitespace_error == 1 ? "s" : ""); if (applied_after_fixing_ws && apply) - fprintf(stderr, "warning: %d line%s applied after" - " fixing whitespace errors.\n", + warning("%d line%s applied after" + " fixing whitespace errors.", applied_after_fixing_ws, applied_after_fixing_ws == 1 ? "" : "s"); else if (whitespace_error) - fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n", + warning("%d line%s add%s whitespace errors.", whitespace_error, whitespace_error == 1 ? "" : "s", whitespace_error == 1 ? "s" : ""); diff --git a/builtin-archive.c b/builtin-archive.c index 5ceec433fd..ab50cebba0 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -5,44 +5,35 @@ #include "cache.h" #include "builtin.h" #include "archive.h" +#include "parse-options.h" #include "pkt-line.h" #include "sideband.h" -static int run_remote_archiver(const char *remote, int argc, - const char **argv) +static void create_output_file(const char *output_file) +{ + int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (output_fd < 0) + die("could not create archive file: %s ", output_file); + if (output_fd != 1) { + if (dup2(output_fd, 1) < 0) + die("could not redirect output"); + else + close(output_fd); + } +} + +static int run_remote_archiver(int argc, const char **argv, + const char *remote, const char *exec) { char *url, buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; struct child_process *conn; - const char *exec = "git-upload-archive"; - int exec_at = 0, exec_value_at = 0; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!prefixcmp(arg, "--exec=")) { - if (exec_at) - die("multiple --exec specified"); - exec = arg + 7; - exec_at = i; - } else if (!strcmp(arg, "--exec")) { - if (exec_at) - die("multiple --exec specified"); - if (i + 1 >= argc) - die("option --exec requires a value"); - exec = argv[i + 1]; - exec_at = i; - exec_value_at = ++i; - } - } url = xstrdup(remote); conn = git_connect(fd, url, exec, 0); - for (i = 1; i < argc; i++) { - if (i == exec_at || i == exec_value_at) - continue; + for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); - } packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); @@ -61,7 +52,7 @@ static int run_remote_archiver(const char *remote, int argc, die("git archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ - rv = recv_sideband("archive", fd[0], 1, 2); + rv = recv_sideband("archive", fd[0], 1); close(fd[0]); close(fd[1]); rv |= finish_connect(conn); @@ -69,51 +60,33 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static const char *extract_remote_arg(int *ac, const char **av) -{ - int ix, iy, cnt = *ac; - int no_more_options = 0; - const char *remote = NULL; - - for (ix = iy = 1; ix < cnt; ix++) { - const char *arg = av[ix]; - if (!strcmp(arg, "--")) - no_more_options = 1; - if (!no_more_options) { - if (!prefixcmp(arg, "--remote=")) { - if (remote) - die("Multiple --remote specified"); - remote = arg + 9; - continue; - } else if (!strcmp(arg, "--remote")) { - if (remote) - die("Multiple --remote specified"); - if (++ix >= cnt) - die("option --remote requires a value"); - remote = av[ix]; - continue; - } - if (arg[0] != '-') - no_more_options = 1; - } - if (ix != iy) - av[iy] = arg; - iy++; - } - if (remote) { - av[--cnt] = NULL; - *ac = cnt; - } - return remote; -} +#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \ + PARSE_OPT_KEEP_ARGV0 | \ + PARSE_OPT_KEEP_UNKNOWN | \ + PARSE_OPT_NO_INTERNAL_HELP ) int cmd_archive(int argc, const char **argv, const char *prefix) { + const char *exec = "git-upload-archive"; + const char *output = NULL; const char *remote = NULL; + struct option local_opts[] = { + OPT_STRING(0, "output", &output, "file", + "write the archive to this file"), + OPT_STRING(0, "remote", &remote, "repo", + "retrieve the archive from remote repository <repo>"), + OPT_STRING(0, "exec", &exec, "cmd", + "path to the remote git-upload-archive command"), + OPT_END() + }; + + argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL); + + if (output) + create_output_file(output); - remote = extract_remote_arg(&argc, argv); if (remote) - return run_remote_archiver(remote, argc, argv); + return run_remote_archiver(argc, argv, remote, exec); setvbuf(stderr, NULL, _IOLBF, BUFSIZ); diff --git a/builtin-blame.c b/builtin-blame.c index 1ead9b4830..83141fc84e 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1,5 +1,5 @@ /* - * Pickaxe + * Blame * * Copyright (c) 2006, Junio C Hamano */ @@ -40,6 +40,10 @@ static int reverse; static int blank_boundary; static int incremental; static int xdl_opts = XDF_NEED_MINIMAL; + +static enum date_mode blame_date_mode = DATE_ISO8601; +static size_t blame_date_width; + static struct string_list mailmap; #ifndef DEBUG @@ -74,6 +78,7 @@ static unsigned blame_copy_score; */ struct origin { int refcnt; + struct origin *previous; struct commit *commit; mmfile_t file; unsigned char blob_sha1[20]; @@ -115,6 +120,8 @@ static inline struct origin *origin_incref(struct origin *o) static void origin_decref(struct origin *o) { if (o && --o->refcnt <= 0) { + if (o->previous) + origin_decref(o->previous); free(o->file.ptr); free(o); } @@ -1198,6 +1205,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) struct origin *porigin = sg_origin[i]; if (!porigin) continue; + if (!origin->previous) { + origin_incref(porigin); + origin->previous = porigin; + } if (pass_blame_to_parent(sb, origin, porigin)) goto finish; } @@ -1415,6 +1426,39 @@ static void write_filename_info(const char *path) } /* + * Porcelain/Incremental format wants to show a lot of details per + * commit. Instead of repeating this every line, emit it only once, + * the first time each commit appears in the output. + */ +static int emit_one_suspect_detail(struct origin *suspect) +{ + struct commit_info ci; + + if (suspect->commit->object.flags & METAINFO_SHOWN) + return 0; + + suspect->commit->object.flags |= METAINFO_SHOWN; + get_commit_info(suspect->commit, &ci, 1); + printf("author %s\n", ci.author); + printf("author-mail %s\n", ci.author_mail); + printf("author-time %lu\n", ci.author_time); + printf("author-tz %s\n", ci.author_tz); + printf("committer %s\n", ci.committer); + printf("committer-mail %s\n", ci.committer_mail); + printf("committer-time %lu\n", ci.committer_time); + printf("committer-tz %s\n", ci.committer_tz); + printf("summary %s\n", ci.summary); + if (suspect->commit->object.flags & UNINTERESTING) + printf("boundary\n"); + if (suspect->previous) { + struct origin *prev = suspect->previous; + printf("previous %s ", sha1_to_hex(prev->commit->object.sha1)); + write_name_quoted(prev->path, stdout, '\n'); + } + return 1; +} + +/* * The blame_entry is found to be guilty for the range. Mark it * as such, and show it in incremental output. */ @@ -1429,22 +1473,7 @@ static void found_guilty_entry(struct blame_entry *ent) printf("%s %d %d %d\n", sha1_to_hex(suspect->commit->object.sha1), ent->s_lno + 1, ent->lno + 1, ent->num_lines); - if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { - struct commit_info ci; - suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author); - printf("author-mail %s\n", ci.author_mail); - printf("author-time %lu\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz); - printf("committer %s\n", ci.committer); - printf("committer-mail %s\n", ci.committer_mail); - printf("committer-time %lu\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz); - printf("summary %s\n", ci.summary); - if (suspect->commit->object.flags & UNINTERESTING) - printf("boundary\n"); - } + emit_one_suspect_detail(suspect); write_filename_info(suspect->path); maybe_flush_or_die(stdout, "stdout"); } @@ -1507,24 +1536,20 @@ static const char *format_time(unsigned long time, const char *tz_str, int show_raw_time) { static char time_buf[128]; - time_t t = time; - int minutes, tz; - struct tm *tm; + const char *time_str; + int time_len; + int tz; if (show_raw_time) { sprintf(time_buf, "%lu %s", time, tz_str); - return time_buf; } - - tz = atoi(tz_str); - minutes = tz < 0 ? -tz : tz; - minutes = (minutes / 100)*60 + (minutes % 100); - minutes = tz < 0 ? -minutes : minutes; - t = time + minutes * 60; - tm = gmtime(&t); - - strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm); - strcat(time_buf, tz_str); + else { + tz = atoi(tz_str); + time_str = show_date(time, tz, blame_date_mode); + time_len = strlen(time_str); + memcpy(time_buf, time_str, time_len); + memset(time_buf + time_len, ' ', blame_date_width - time_len); + } return time_buf; } @@ -1551,24 +1576,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent) ent->s_lno + 1, ent->lno + 1, ent->num_lines); - if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { - struct commit_info ci; - suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author); - printf("author-mail %s\n", ci.author_mail); - printf("author-time %lu\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz); - printf("committer %s\n", ci.committer); - printf("committer-mail %s\n", ci.committer_mail); - printf("committer-time %lu\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz); - write_filename_info(suspect->path); - printf("summary %s\n", ci.summary); - if (suspect->commit->object.flags & UNINTERESTING) - printf("boundary\n"); - } - else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH) + if (emit_one_suspect_detail(suspect) || + (suspect->commit->object.flags & MORE_THAN_ONE_PATH)) write_filename_info(suspect->path); cp = nth_line(sb, ent->lno); @@ -1806,36 +1815,6 @@ static void sanity_check_refcnt(struct scoreboard *sb) baa = 1; } } - for (ent = sb->ent; ent; ent = ent->next) { - /* Mark the ones that haven't been checked */ - if (0 < ent->suspect->refcnt) - ent->suspect->refcnt = -ent->suspect->refcnt; - } - for (ent = sb->ent; ent; ent = ent->next) { - /* - * ... then pick each and see if they have the the - * correct refcnt. - */ - int found; - struct blame_entry *e; - struct origin *suspect = ent->suspect; - - if (0 < suspect->refcnt) - continue; - suspect->refcnt = -suspect->refcnt; /* Unmark */ - for (found = 0, e = sb->ent; e; e = e->next) { - if (e->suspect != suspect) - continue; - found++; - } - if (suspect->refcnt != found) { - fprintf(stderr, "%s in %s has refcnt %d, not %d\n", - ent->suspect->path, - sha1_to_hex(ent->suspect->commit->object.sha1), - ent->suspect->refcnt, found); - baa = 2; - } - } if (baa) { int opt = 0160; find_alignment(sb, &opt); @@ -1975,6 +1954,12 @@ static int git_blame_config(const char *var, const char *value, void *cb) blank_boundary = git_config_bool(var, value); return 0; } + if (!strcmp(var, "blame.date")) { + if (!value) + return config_error_nonbool(var); + blame_date_mode = parse_date_format(value); + return 0; + } return git_default_config(var, value, cb); } @@ -2239,6 +2224,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) git_config(git_blame_config, NULL); init_revisions(&revs, NULL); + revs.date_mode = blame_date_mode; + save_commit_buffer = 0; dashdash_pos = 0; @@ -2267,8 +2254,35 @@ parse_done: die("reading graft file %s failed: %s", revs_file, strerror(errno)); - if (cmd_is_annotate) + if (cmd_is_annotate) { output_option |= OUTPUT_ANNOTATE_COMPAT; + blame_date_mode = DATE_ISO8601; + } else { + blame_date_mode = revs.date_mode; + } + + /* The maximum width used to show the dates */ + switch (blame_date_mode) { + case DATE_RFC2822: + blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700"); + break; + case DATE_ISO8601: + blame_date_width = sizeof("2006-10-19 16:00:04 -0700"); + break; + case DATE_RAW: + blame_date_width = sizeof("1161298804 -0700"); + break; + case DATE_SHORT: + blame_date_width = sizeof("2006-10-19"); + break; + case DATE_RELATIVE: + /* "normal" is used as the fallback for "relative" */ + case DATE_LOCAL: + case DATE_NORMAL: + blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); + break; + } + blame_date_width -= 1; /* strip the null */ if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER)) opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE | diff --git a/builtin-branch.c b/builtin-branch.c index 07166b8856..07a440eeba 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -32,18 +32,18 @@ static unsigned char head_sha1[20]; static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { - "\033[m", /* reset */ - "", /* PLAIN (normal) */ - "\033[31m", /* REMOTE (red) */ - "", /* LOCAL (normal) */ - "\033[32m", /* CURRENT (green) */ + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_RED, /* REMOTE */ + GIT_COLOR_NORMAL, /* LOCAL */ + GIT_COLOR_GREEN, /* CURRENT */ }; enum color_branch { - COLOR_BRANCH_RESET = 0, - COLOR_BRANCH_PLAIN = 1, - COLOR_BRANCH_REMOTE = 2, - COLOR_BRANCH_LOCAL = 3, - COLOR_BRANCH_CURRENT = 4, + BRANCH_COLOR_RESET = 0, + BRANCH_COLOR_PLAIN = 1, + BRANCH_COLOR_REMOTE = 2, + BRANCH_COLOR_LOCAL = 3, + BRANCH_COLOR_CURRENT = 4, }; static enum merge_filter { @@ -56,15 +56,15 @@ static unsigned char merge_filter_ref[20]; static int parse_branch_color_slot(const char *var, int ofs) { if (!strcasecmp(var+ofs, "plain")) - return COLOR_BRANCH_PLAIN; + return BRANCH_COLOR_PLAIN; if (!strcasecmp(var+ofs, "reset")) - return COLOR_BRANCH_RESET; + return BRANCH_COLOR_RESET; if (!strcasecmp(var+ofs, "remote")) - return COLOR_BRANCH_REMOTE; + return BRANCH_COLOR_REMOTE; if (!strcasecmp(var+ofs, "local")) - return COLOR_BRANCH_LOCAL; + return BRANCH_COLOR_LOCAL; if (!strcasecmp(var+ofs, "current")) - return COLOR_BRANCH_CURRENT; + return BRANCH_COLOR_CURRENT; die("bad config variable '%s'", var); } @@ -188,7 +188,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) struct ref_item { char *name; - unsigned int kind; + char *dest; + unsigned int kind, len; struct commit *commit; }; @@ -200,22 +201,47 @@ struct ref_list { int kinds; }; +static char *resolve_symref(const char *src, const char *prefix) +{ + unsigned char sha1[20]; + int flag; + const char *dst, *cp; + + dst = resolve_ref(src, sha1, 0, &flag); + if (!(dst && (flag & REF_ISSYMREF))) + return NULL; + if (prefix && (cp = skip_prefix(dst, prefix))) + dst = cp; + return xstrdup(dst); +} + 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); struct ref_item *newitem; struct commit *commit; - int kind; - int len; + int kind, i; + const char *prefix, *orig_refname = refname; + + static struct { + int kind; + const char *prefix; + int pfxlen; + } ref_kind[] = { + { REF_LOCAL_BRANCH, "refs/heads/", 11 }, + { REF_REMOTE_BRANCH, "refs/remotes/", 13 }, + }; /* Detect kind */ - if (!prefixcmp(refname, "refs/heads/")) { - kind = REF_LOCAL_BRANCH; - refname += 11; - } else if (!prefixcmp(refname, "refs/remotes/")) { - kind = REF_REMOTE_BRANCH; - refname += 13; - } else + for (i = 0; i < ARRAY_SIZE(ref_kind); i++) { + prefix = ref_kind[i].prefix; + if (strncmp(refname, prefix, ref_kind[i].pfxlen)) + continue; + kind = ref_kind[i].kind; + refname += ref_kind[i].pfxlen; + break; + } + if (ARRAY_SIZE(ref_kind) <= i) return 0; commit = lookup_commit_reference_gently(sha1, 1); @@ -246,9 +272,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, newitem->name = xstrdup(refname); newitem->kind = kind; newitem->commit = commit; - len = strlen(newitem->name); - if (len > ref_list->maxwidth) - ref_list->maxwidth = len; + newitem->len = strlen(refname); + newitem->dest = resolve_symref(orig_refname, prefix); + /* adjust for "remotes/" */ + if (newitem->kind == REF_REMOTE_BRANCH && + ref_list->kinds != REF_REMOTE_BRANCH) + newitem->len += 8; + if (newitem->len > ref_list->maxwidth) + ref_list->maxwidth = newitem->len; return 0; } @@ -257,8 +288,10 @@ static void free_ref_list(struct ref_list *ref_list) { int i; - for (i = 0; i < ref_list->index; i++) + for (i = 0; i < ref_list->index; i++) { free(ref_list->list[i].name); + free(ref_list->list[i].dest); + } free(ref_list->list); } @@ -299,34 +332,46 @@ static int matches_merge_filter(struct commit *commit) } static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, - int abbrev, int current) + int abbrev, int current, char *prefix) { char c; int color; struct commit *commit = item->commit; + struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; if (!matches_merge_filter(commit)) return; switch (item->kind) { case REF_LOCAL_BRANCH: - color = COLOR_BRANCH_LOCAL; + color = BRANCH_COLOR_LOCAL; break; case REF_REMOTE_BRANCH: - color = COLOR_BRANCH_REMOTE; + color = BRANCH_COLOR_REMOTE; break; default: - color = COLOR_BRANCH_PLAIN; + color = BRANCH_COLOR_PLAIN; break; } c = ' '; if (current) { c = '*'; - color = COLOR_BRANCH_CURRENT; + color = BRANCH_COLOR_CURRENT; } - if (verbose) { + strbuf_addf(&name, "%s%s", prefix, item->name); + if (verbose) + strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), + maxwidth, name.buf, + branch_get_color(BRANCH_COLOR_RESET)); + else + strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), + name.buf, branch_get_color(BRANCH_COLOR_RESET)); + + if (item->dest) + strbuf_addf(&out, " -> %s", item->dest); + else if (verbose) { struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; const char *sub = " **** invalid ref ****"; @@ -340,28 +385,25 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, if (item->kind == REF_LOCAL_BRANCH) fill_tracking_info(&stat, item->name); - printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color), - maxwidth, item->name, - branch_get_color(COLOR_BRANCH_RESET), - find_unique_abbrev(item->commit->object.sha1, abbrev), - stat.buf, sub); + strbuf_addf(&out, " %s %s%s", + find_unique_abbrev(item->commit->object.sha1, abbrev), + stat.buf, sub); strbuf_release(&stat); strbuf_release(&subject); - } else { - printf("%c %s%s%s\n", c, branch_get_color(color), item->name, - branch_get_color(COLOR_BRANCH_RESET)); } + printf("%s\n", out.buf); + strbuf_release(&name); + strbuf_release(&out); } static int calc_maxwidth(struct ref_list *refs) { - int i, l, w = 0; + int i, w = 0; for (i = 0; i < refs->index; i++) { if (!matches_merge_filter(refs->list[i].commit)) continue; - l = strlen(refs->list[i].name); - if (l > w) - w = l; + if (refs->list[i].len > w) + w = refs->list[i].len; } return w; } @@ -397,11 +439,13 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str is_descendant_of(head_commit, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); + item.len = strlen(item.name); item.kind = REF_LOCAL_BRANCH; + item.dest = NULL; item.commit = head_commit; - if (strlen(item.name) > ref_list.maxwidth) - ref_list.maxwidth = strlen(item.name); - print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1); + if (item.len > ref_list.maxwidth) + ref_list.maxwidth = item.len; + print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, ""); free(item.name); } @@ -409,8 +453,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) && !strcmp(ref_list.list[i].name, head); + char *prefix = (kinds != REF_REMOTE_BRANCH && + ref_list.list[i].kind == REF_REMOTE_BRANCH) + ? "remotes/" : ""; print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose, - abbrev, current); + abbrev, current, prefix); } free_ref_list(&ref_list); diff --git a/builtin-checkout.c b/builtin-checkout.c index 20b34ce6e1..fc55bbe14d 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -295,6 +295,8 @@ static void show_local_changes(struct object *head) init_revisions(&rev, NULL); rev.abbrev = 0; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; + if (diff_setup_done(&rev.diffopt) < 0) + die("diff_setup_done failed"); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); } @@ -405,7 +407,7 @@ static int merge_working_tree(struct checkout_opts *opts, topts.verbose_update = !opts->quiet; topts.fn = twoway_merge; topts.dir = xcalloc(1, sizeof(*topts.dir)); - topts.dir->show_ignored = 1; + topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->exclude_per_dir = ".gitignore"; tree = parse_tree_indirect(old->commit->object.sha1); init_tree_desc(&trees[0], tree->buffer, tree->size); @@ -556,8 +558,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) if (!old.commit && !opts->force) { if (!opts->quiet) { - fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n"); - fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name); + warning("You appear to be on a branch yet to be born."); + warning("Forcing checkout of %s.", new->name); } opts->force = 1; } diff --git a/builtin-clean.c b/builtin-clean.c index f78c2fb108..c5ad33d3e6 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -60,7 +60,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) memset(&dir, 0, sizeof(dir)); if (ignored_only) - dir.show_ignored = 1; + dir.flags |= DIR_SHOW_IGNORED; if (ignored && ignored_only) die("-x and -X cannot be used together"); @@ -69,7 +69,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) die("clean.requireForce%s set and -n or -f not given; " "refusing to clean", config_set ? "" : " not"); - dir.show_other_directories = 1; + dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; if (!ignored) setup_standard_excludes(&dir); diff --git a/builtin-clone.c b/builtin-clone.c index c338910b1c..880373f279 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -20,6 +20,9 @@ #include "dir.h" #include "pack-refs.h" #include "sigchain.h" +#include "branch.h" +#include "remote.h" +#include "run-command.h" /* * Overall FIXMEs: @@ -267,7 +270,7 @@ static const struct ref *clone_local(const char *src_repo, static const char *junk_work_tree; static const char *junk_git_dir; -pid_t junk_pid; +static pid_t junk_pid; static void remove_junk(void) { @@ -293,43 +296,6 @@ static void remove_junk_on_signal(int signo) raise(signo); } -static const struct ref *locate_head(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref **remote_head_p) -{ - const struct ref *remote_head = NULL; - const struct ref *remote_master = NULL; - const struct ref *r; - for (r = refs; r; r = r->next) - if (!strcmp(r->name, "HEAD")) - remote_head = r; - - for (r = mapped_refs; r; r = r->next) - if (!strcmp(r->name, "refs/heads/master")) - remote_master = r; - - if (remote_head_p) - *remote_head_p = remote_head; - - /* If there's no HEAD value at all, never mind. */ - if (!remote_head) - return NULL; - - /* If refs/heads/master could be right, it is. */ - if (remote_master && !hashcmp(remote_master->old_sha1, - remote_head->old_sha1)) - return remote_master; - - /* Look for another ref that points there */ - for (r = mapped_refs; r; r = r->next) - if (r != remote_head && - !hashcmp(r->old_sha1, remote_head->old_sha1)) - return r; - - /* Nothing is the same */ - return NULL; -} - static struct ref *write_remote_refs(const struct ref *refs, struct refspec *refspec, const char *reflog) { @@ -350,23 +316,8 @@ static struct ref *write_remote_refs(const struct ref *refs, return local_refs; } -static void install_branch_config(const char *local, - const char *origin, - const char *remote) -{ - struct strbuf key = STRBUF_INIT; - strbuf_addf(&key, "branch.%s.remote", local); - git_config_set(key.buf, origin); - strbuf_reset(&key); - strbuf_addf(&key, "branch.%s.merge", local); - git_config_set(key.buf, remote); - strbuf_release(&key); -} - int cmd_clone(int argc, const char **argv, const char *prefix) { - int use_local_hardlinks = 1; - int use_separate_remote = 1; int is_bundle = 0; struct stat buf; const char *repo_name, *repo, *work_tree, *git_dir; @@ -377,8 +328,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; char *src_ref_prefix = "refs/heads/"; + int err = 0; - struct refspec refspec; + struct refspec *refspec; + const char *fetch_pattern; junk_pid = getpid(); @@ -388,9 +341,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (argc == 0) die("You must specify a repository to clone."); - if (option_no_hardlinks) - use_local_hardlinks = 0; - if (option_mirror) option_bare = 1; @@ -399,7 +349,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) die("--bare and --origin %s options are incompatible.", option_origin); option_no_checkout = 1; - use_separate_remote = 0; } if (!option_origin) @@ -457,7 +406,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); - setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); + setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1); if (safe_create_leading_directories_const(git_dir) < 0) die("could not create leading directories of '%s'", git_dir); @@ -487,8 +436,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } + strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); + if (option_mirror || !option_bare) { /* Configure the remote */ + strbuf_addf(&key, "remote.%s.fetch", option_origin); + git_config_set_multivar(key.buf, value.buf, "^$", 0); + strbuf_reset(&key); + if (option_mirror) { strbuf_addf(&key, "remote.%s.mirror", option_origin); git_config_set(key.buf, "true"); @@ -497,19 +452,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_addf(&key, "remote.%s.url", option_origin); git_config_set(key.buf, repo); - strbuf_reset(&key); - - strbuf_addf(&key, "remote.%s.fetch", option_origin); - strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); - git_config_set_multivar(key.buf, value.buf, "^$", 0); strbuf_reset(&key); - strbuf_reset(&value); } - refspec.force = 0; - refspec.pattern = 1; - refspec.src = src_ref_prefix; - refspec.dst = branch_top.buf; + fetch_pattern = value.buf; + refspec = parse_fetch_refspec(1, &fetch_pattern); + + strbuf_reset(&value); if (path && !is_bundle) refs = clone_local(path, git_dir); @@ -543,9 +492,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) 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); + remote_head = find_ref_by_name(refs, "HEAD"); + head_points_at = guess_remote_head(remote_head, mapped_refs, 0); } else { warning("You appear to have cloned an empty repository."); @@ -553,7 +503,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote_head = NULL; option_no_checkout = 1; if (!option_bare) - install_branch_config("master", option_origin, + install_branch_config(0, "master", option_origin, "refs/heads/master"); } @@ -583,7 +533,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->peer_ref->name, reflog_msg.buf); - install_branch_config(head, option_origin, + install_branch_config(0, head, option_origin, head_points_at->name); } } else if (remote_head) { @@ -631,6 +581,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (write_cache(fd, active_cache, active_nr) || commit_locked_index(lock_file)) die("unable to write new index file"); + + err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1), + sha1_to_hex(remote_head->old_sha1), "1", NULL); } strbuf_release(&reflog_msg); @@ -638,5 +591,5 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_release(&key); strbuf_release(&value); junk_pid = 0; - return 0; + return err; } diff --git a/builtin-config.c b/builtin-config.c index f71016204b..d8da72cf20 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "color.h" +#include "parse-options.h" -static const char git_config_set_usage[] = -"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; +static const char *const builtin_config_usage[] = { + "git config [options]", + NULL +}; static char *key; static regex_t *key_regexp; @@ -16,7 +19,67 @@ static int seen; static char delim = '='; static char key_delim = ' '; static char term = '\n'; -static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW; + +static int use_global_config, use_system_config; +static const char *given_config_file; +static int actions, types; +static const char *get_color_slot, *get_colorbool_slot; +static int end_null; + +#define ACTION_GET (1<<0) +#define ACTION_GET_ALL (1<<1) +#define ACTION_GET_REGEXP (1<<2) +#define ACTION_REPLACE_ALL (1<<3) +#define ACTION_ADD (1<<4) +#define ACTION_UNSET (1<<5) +#define ACTION_UNSET_ALL (1<<6) +#define ACTION_RENAME_SECTION (1<<7) +#define ACTION_REMOVE_SECTION (1<<8) +#define ACTION_LIST (1<<9) +#define ACTION_EDIT (1<<10) +#define ACTION_SET (1<<11) +#define ACTION_SET_ALL (1<<12) +#define ACTION_GET_COLOR (1<<13) +#define ACTION_GET_COLORBOOL (1<<14) + +#define TYPE_BOOL (1<<0) +#define TYPE_INT (1<<1) +#define TYPE_BOOL_OR_INT (1<<2) + +static struct option builtin_config_options[] = { + OPT_GROUP("Config file location"), + OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"), + OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"), + OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"), + OPT_GROUP("Action"), + OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET), + OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL), + OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP), + OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL), + OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD), + OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET), + OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL), + OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION), + OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION), + OPT_BIT('l', "list", &actions, "list all", ACTION_LIST), + OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT), + OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"), + OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"), + OPT_GROUP("Type"), + OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL), + OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT), + OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT), + OPT_GROUP("Other"), + OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"), + OPT_END(), +}; + +static void check_argc(int argc, int min, int max) { + if (argc >= min && argc <= max) + return; + error("wrong number of arguments"); + usage_with_options(builtin_config_usage, builtin_config_options); +} static int show_all_config(const char *key_, const char *value_, void *cb) { @@ -27,7 +90,7 @@ static int show_all_config(const char *key_, const char *value_, void *cb) return 0; } -static int show_config(const char* key_, const char* value_, void *cb) +static int show_config(const char *key_, const char *value_, void *cb) { char value[256]; const char *vptr = value; @@ -49,11 +112,11 @@ static int show_config(const char* key_, const char* value_, void *cb) } if (seen && !do_all) dup_error = 1; - if (type == T_INT) + if (types == TYPE_INT) sprintf(value, "%d", git_config_int(key_, value_?value_:"")); - else if (type == T_BOOL) + else if (types == TYPE_BOOL) vptr = git_config_bool(key_, value_) ? "true" : "false"; - else if (type == T_BOOL_OR_INT) { + else if (types == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key_, value_, &is_bool); if (is_bool) @@ -74,7 +137,7 @@ static int show_config(const char* key_, const char* value_, void *cb) return 0; } -static int get_value(const char* key_, const char* regex_) +static int get_value(const char *key_, const char *regex_) { int ret = -1; char *tl; @@ -152,18 +215,18 @@ static char *normalize_value(const char *key, const char *value) if (!value) return NULL; - if (type == T_RAW) + if (types == 0) normalized = xstrdup(value); else { normalized = xmalloc(64); - if (type == T_INT) { + if (types == TYPE_INT) { int v = git_config_int(key, value); sprintf(normalized, "%d", v); } - else if (type == T_BOOL) + else if (types == TYPE_BOOL) sprintf(normalized, "%s", git_config_bool(key, value) ? "true" : "false"); - else if (type == T_BOOL_OR_INT) { + else if (types == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key, value, &is_bool); if (!is_bool) @@ -178,6 +241,7 @@ static char *normalize_value(const char *key, const char *value) static int get_color_found; static const char *get_color_slot; +static const char *get_colorbool_slot; static char parsed_color[COLOR_MAXLEN]; static int git_get_color_config(const char *var, const char *value, void *cb) @@ -191,29 +255,8 @@ static int git_get_color_config(const char *var, const char *value, void *cb) return 0; } -static int get_color(int argc, const char **argv) +static void get_color(const char *def_color) { - /* - * grab the color setting for the given slot from the configuration, - * or parse the default value if missing, and return ANSI color - * escape sequence. - * - * e.g. - * git config --get-color color.diff.whitespace "blue reverse" - */ - const char *def_color = NULL; - - switch (argc) { - default: - usage(git_config_set_usage); - case 2: - def_color = argv[1]; - /* fallthru */ - case 1: - get_color_slot = argv[0]; - break; - } - get_color_found = 0; parsed_color[0] = '\0'; git_config(git_get_color_config, NULL); @@ -222,7 +265,6 @@ static int get_color(int argc, const char **argv) color_parse(def_color, "command line", parsed_color); fputs(parsed_color, stdout); - return 0; } static int stdout_is_tty; @@ -231,7 +273,7 @@ static int get_diff_color_found; static int git_get_colorbool_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, get_color_slot)) { + if (!strcmp(var, get_colorbool_slot)) { get_colorbool_found = git_config_colorbool(var, value, stdout_is_tty); } @@ -246,183 +288,188 @@ static int git_get_colorbool_config(const char *var, const char *value, return 0; } -static int get_colorbool(int argc, const char **argv) +static int get_colorbool(int print) { - /* - * git config --get-colorbool <slot> [<stdout-is-tty>] - * - * returns "true" or "false" depending on how <slot> - * is configured. - */ - - if (argc == 2) - stdout_is_tty = git_config_bool("command line", argv[1]); - else if (argc == 1) - stdout_is_tty = isatty(1); - else - usage(git_config_set_usage); get_colorbool_found = -1; get_diff_color_found = -1; - get_color_slot = argv[0]; git_config(git_get_colorbool_config, NULL); if (get_colorbool_found < 0) { - if (!strcmp(get_color_slot, "color.diff")) + if (!strcmp(get_colorbool_slot, "color.diff")) get_colorbool_found = get_diff_color_found; if (get_colorbool_found < 0) get_colorbool_found = git_use_color_default; } - if (argc == 1) { - return get_colorbool_found ? 0 : 1; - } else { + if (print) { printf("%s\n", get_colorbool_found ? "true" : "false"); return 0; - } + } else + return get_colorbool_found ? 0 : 1; } -int cmd_config(int argc, const char **argv, const char *prefix) +int cmd_config(int argc, const char **argv, const char *unused_prefix) { int nongit; - char* value; - const char *file = setup_git_directory_gently(&nongit); + char *value; + const char *prefix = setup_git_directory_gently(&nongit); config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); - while (1 < argc) { - if (!strcmp(argv[1], "--int")) - type = T_INT; - else if (!strcmp(argv[1], "--bool")) - type = T_BOOL; - else if (!strcmp(argv[1], "--bool-or-int")) - type = T_BOOL_OR_INT; - else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { - if (argc != 2) - usage(git_config_set_usage); - if (git_config(show_all_config, NULL) < 0 && - file && errno) - die("unable to read config file %s: %s", file, - strerror(errno)); - return 0; - } - else if (!strcmp(argv[1], "--global")) { - char *home = getenv("HOME"); - if (home) { - char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - config_exclusive_filename = user_config; - } else { - die("$HOME not set"); - } - } - else if (!strcmp(argv[1], "--system")) - config_exclusive_filename = git_etc_gitconfig(); - else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { - if (argc < 3) - usage(git_config_set_usage); - if (!is_absolute_path(argv[2]) && file) - file = prefix_filename(file, strlen(file), - argv[2]); - else - file = argv[2]; - config_exclusive_filename = file; - argc--; - argv++; - } - else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) { - term = '\0'; - delim = '\n'; - key_delim = '\n'; - } - else if (!strcmp(argv[1], "--rename-section")) { - int ret; - if (argc != 4) - usage(git_config_set_usage); - ret = git_config_rename_section(argv[2], argv[3]); - if (ret < 0) - return ret; - if (ret == 0) { - fprintf(stderr, "No such section!\n"); - return 1; - } - return 0; - } - else if (!strcmp(argv[1], "--remove-section")) { - int ret; - if (argc != 3) - usage(git_config_set_usage); - ret = git_config_rename_section(argv[2], NULL); - if (ret < 0) - return ret; - if (ret == 0) { - fprintf(stderr, "No such section!\n"); - return 1; - } - return 0; - } else if (!strcmp(argv[1], "--get-color")) { - return get_color(argc-2, argv+2); - } else if (!strcmp(argv[1], "--get-colorbool")) { - return get_colorbool(argc-2, argv+2); - } else - break; - argc--; - argv++; - } - - switch (argc) { - case 2: - return get_value(argv[1], NULL); - case 3: - if (!strcmp(argv[1], "--unset")) - return git_config_set(argv[2], NULL); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, NULL, 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], NULL); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], NULL); - } else if (!strcmp(argv[1], "--get-regexp")) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - return get_value(argv[2], NULL); + argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (use_global_config + use_system_config + !!given_config_file > 1) { + error("only one config file at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (use_global_config) { + char *home = getenv("HOME"); + if (home) { + char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); + config_exclusive_filename = user_config; } else { - value = normalize_value(argv[1], argv[2]); - return git_config_set(argv[1], value); + die("$HOME not set"); } - case 4: - if (!strcmp(argv[1], "--unset")) - return git_config_set_multivar(argv[2], NULL, argv[3], 0); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, argv[3], 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], argv[3]); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], argv[3]); - } else if (!strcmp(argv[1], "--get-regexp")) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - return get_value(argv[2], argv[3]); - } else if (!strcmp(argv[1], "--add")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, "^$", 0); - } else if (!strcmp(argv[1], "--replace-all")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, NULL, 1); - } else { - value = normalize_value(argv[1], argv[2]); - return git_config_set_multivar(argv[1], value, argv[3], 0); + } + else if (use_system_config) + config_exclusive_filename = git_etc_gitconfig(); + else if (given_config_file) { + if (!is_absolute_path(given_config_file) && prefix) + config_exclusive_filename = prefix_filename(prefix, + strlen(prefix), + argv[2]); + else + config_exclusive_filename = given_config_file; + } + + if (end_null) { + term = '\0'; + delim = '\n'; + key_delim = '\n'; + } + + if (HAS_MULTI_BITS(types)) { + error("only one type at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (get_color_slot) + actions |= ACTION_GET_COLOR; + if (get_colorbool_slot) + actions |= ACTION_GET_COLORBOOL; + + if ((get_color_slot || get_colorbool_slot) && types) { + error("--get-color and variable type are incoherent"); + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (HAS_MULTI_BITS(actions)) { + error("only one action at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (actions == 0) + switch (argc) { + case 1: actions = ACTION_GET; break; + case 2: actions = ACTION_SET; break; + case 3: actions = ACTION_SET_ALL; break; + default: + usage_with_options(builtin_config_usage, builtin_config_options); } - case 5: - if (!strcmp(argv[1], "--replace-all")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, argv[4], 1); + + if (actions == ACTION_LIST) { + check_argc(argc, 0, 0); + if (git_config(show_all_config, NULL) < 0) { + if (config_exclusive_filename) + die("unable to read config file %s: %s", + config_exclusive_filename, strerror(errno)); + else + die("error processing config file(s)"); } - case 1: - default: - usage(git_config_set_usage); } + else if (actions == ACTION_EDIT) { + check_argc(argc, 0, 0); + git_config(git_default_config, NULL); + launch_editor(config_exclusive_filename ? + config_exclusive_filename : git_path("config"), + NULL, NULL); + } + else if (actions == ACTION_SET) { + check_argc(argc, 2, 2); + value = normalize_value(argv[0], argv[1]); + return git_config_set(argv[0], value); + } + else if (actions == ACTION_SET_ALL) { + check_argc(argc, 2, 3); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, argv[2], 0); + } + else if (actions == ACTION_ADD) { + check_argc(argc, 2, 2); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, "^$", 0); + } + else if (actions == ACTION_REPLACE_ALL) { + check_argc(argc, 2, 3); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, argv[2], 1); + } + else if (actions == ACTION_GET) { + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_GET_ALL) { + do_all = 1; + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_GET_REGEXP) { + show_keys = 1; + use_key_regexp = 1; + do_all = 1; + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_UNSET) { + check_argc(argc, 1, 2); + if (argc == 2) + return git_config_set_multivar(argv[0], NULL, argv[1], 0); + else + return git_config_set(argv[0], NULL); + } + else if (actions == ACTION_UNSET_ALL) { + check_argc(argc, 1, 2); + return git_config_set_multivar(argv[0], NULL, argv[1], 1); + } + else if (actions == ACTION_RENAME_SECTION) { + int ret; + check_argc(argc, 2, 2); + ret = git_config_rename_section(argv[0], argv[1]); + if (ret < 0) + return ret; + if (ret == 0) + die("No such section!"); + } + else if (actions == ACTION_REMOVE_SECTION) { + int ret; + check_argc(argc, 1, 1); + ret = git_config_rename_section(argv[0], NULL); + if (ret < 0) + return ret; + if (ret == 0) + die("No such section!"); + } + else if (actions == ACTION_GET_COLOR) { + get_color(argv[0]); + } + else if (actions == ACTION_GET_COLORBOOL) { + if (argc == 1) + stdout_is_tty = git_config_bool("command line", argv[0]); + else if (argc == 0) + stdout_is_tty = isatty(1); + return get_colorbool(argc != 0); + } + return 0; } diff --git a/builtin-count-objects.c b/builtin-count-objects.c index 62fd1f0961..b814fe5070 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -60,7 +60,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose, hex[40] = 0; if (get_sha1_hex(hex, sha1)) die("internal error"); - if (has_sha1_pack(sha1, NULL)) + if (has_sha1_pack(sha1)) (*packed_loose)++; } } diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 8ecefd4f0f..79cedb72c4 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -102,7 +102,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) init_revisions(opt, prefix); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ - nr_sha1 = 0; opt->abbrev = 0; opt->diff = 1; argc = setup_revisions(argc, argv, opt, NULL); diff --git a/builtin-fast-export.c b/builtin-fast-export.c index fdf4ae9ebd..6731713223 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -221,7 +221,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) if (message) message += 2; - if (commit->parents) { + if (commit->parents && + get_object_mark(&commit->parents->item->object) != 0) { parse_commit(commit->parents->item); diff_tree_sha1(commit->parents->item->tree->object.sha1, commit->tree->object.sha1, "", &rev->diffopt); @@ -362,7 +363,10 @@ static void get_tags_and_duplicates(struct object_array *pending, break; case OBJ_TAG: tag = (struct tag *)e->item; + + /* handle nested tags */ while (tag && tag->object.type == OBJ_TAG) { + parse_object(tag->object.sha1); string_list_append(full_name, extra_refs)->util = tag; tag = (struct tag *)tag->tagged; } @@ -375,11 +379,17 @@ static void get_tags_and_duplicates(struct object_array *pending, case OBJ_BLOB: handle_object(tag->object.sha1); continue; + default: /* OBJ_TAG (nested tags) is already handled */ + warning("Tag points to object of unexpected type %s, skipping.", + typename(tag->object.type)); + continue; } break; default: - die ("Unexpected object of type %s", - typename(e->item->type)); + warning("%s: Unexpected object of type %s, skipping.", + e->name, + typename(e->item->type)); + continue; } if (commit->util) /* more than one name for the same object */ diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 67fb80ec48..5d134be47c 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -216,9 +216,8 @@ static int find_common(int fd[2], unsigned char *result_sha1, if (args.depth > 0) { char line[1024]; unsigned char sha1[20]; - int len; - while ((len = packet_read_line(fd[0], line, sizeof(line)))) { + while (packet_read_line(fd[0], line, sizeof(line))) { if (!prefixcmp(line, "shallow ")) { if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); @@ -483,7 +482,7 @@ static int sideband_demux(int fd, void *data) { int *xd = data; - return recv_sideband("fetch-pack", xd[0], fd, 2); + return recv_sideband("fetch-pack", xd[0], fd); } static int get_pack(int xd[2], char **pack_lockfile) @@ -606,7 +605,7 @@ static struct ref *do_fetch_pack(int fd[2], /* When cloning, it is not unusual to have * no common commit. */ - fprintf(stderr, "warning: no common commits\n"); + warning("no common commits"); if (get_pack(fd, pack_lockfile)) die("git fetch-pack: fetch failed."); @@ -801,15 +800,13 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, int fd; mtime.sec = st.st_mtime; -#ifdef USE_NSEC - mtime.usec = st.st_mtim.usec; -#endif + mtime.nsec = ST_MTIME_NSEC(st); if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC - || st.st_mtim.usec != mtime.usec + || ST_MTIME_NSEC(st) != mtime.nsec #endif ) die("shallow file was changed during fetch"); diff --git a/builtin-fetch.c b/builtin-fetch.c index 7fb35fca9d..3c998ea740 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -197,11 +197,7 @@ static int update_local_ref(struct ref *ref, struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); - const char *pretty_ref = ref->name + ( - !prefixcmp(ref->name, "refs/heads/") ? 11 : - !prefixcmp(ref->name, "refs/tags/") ? 10 : - !prefixcmp(ref->name, "refs/remotes/") ? 13 : - 0); + const char *pretty_ref = prettify_ref(ref); *display = 0; type = sha1_object_info(ref->new_sha1, NULL); @@ -544,7 +540,8 @@ static void check_not_current_branch(struct ref *ref_map) for (; ref_map; ref_map = ref_map->next) if (ref_map->peer_ref && !strcmp(current_branch->refname, ref_map->peer_ref->name)) - die("Refusing to fetch into current branch"); + die("Refusing to fetch into current branch %s " + "of non-bare repository", current_branch->refname); } static int do_fetch(struct transport *transport, diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index df18f4070f..a7883690d7 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -256,8 +256,7 @@ static void shortlog(const char *name, unsigned char *sha1, int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) { int limit = 20, i = 0, pos = 0; - char line[1024]; - char *p = line, *sep = ""; + char *sep = ""; unsigned char head_sha1[20]; const char *current_branch; @@ -271,9 +270,8 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) { /* get a line */ while (pos < in->len) { int len; - char *newline; + char *newline, *p = in->buf + pos; - p = in->buf + pos; newline = strchr(p, '\n'); len = newline ? newline - p : strlen(p); pos += len + !!newline; diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index e46b7adc97..5cbb4b081d 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -943,7 +943,6 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset) return -1; *sort_tail = s = xcalloc(1, sizeof(*s)); - sort_tail = &s->next; if (*arg == '-') { s->reverse = 1; diff --git a/builtin-fsck.c b/builtin-fsck.c index 64dffa5421..6436bc2248 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -160,7 +160,7 @@ static void check_reachable_object(struct object *obj) * do a full fsck */ if (!obj->parsed) { - if (has_sha1_pack(obj->sha1, NULL)) + if (has_sha1_pack(obj->sha1)) return; /* it is in pack - forget about it */ printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); errors_found |= ERROR_REACHABLE; diff --git a/builtin-gc.c b/builtin-gc.c index 8d990ed493..fc556ed7f3 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -23,7 +23,7 @@ static const char * const builtin_gc_usage[] = { }; static int pack_refs = 1; -static int aggressive_window = -1; +static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; static const char *prune_expire = "2.weeks.ago"; @@ -200,6 +200,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (aggressive) { append_option(argv_repack, "-f", MAX_ADD); + append_option(argv_repack, "--depth=250", MAX_ADD); if (aggressive_window > 0) { sprintf(buf, "--window=%d", aggressive_window); append_option(argv_repack, buf, MAX_ADD); diff --git a/builtin-grep.c b/builtin-grep.c index 3f12ba3826..89489ddcf8 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -22,6 +22,28 @@ static int builtin_grep; +static int grep_config(const char *var, const char *value, void *cb) +{ + struct grep_opt *opt = cb; + + if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) { + opt->color = git_config_colorbool(var, value, -1); + return 0; + } + if (!strcmp(var, "grep.color.external") || + !strcmp(var, "color.grep.external")) { + return git_config_string(&(opt->color_external), var, value); + } + if (!strcmp(var, "grep.color.match") || + !strcmp(var, "color.grep.match")) { + if (!value) + return config_error_nonbool(var); + color_parse(value, var, opt->color_match); + return 0; + } + return git_color_default_config(var, value, cb); +} + /* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. @@ -269,6 +291,21 @@ static int flush_grep(struct grep_opt *opt, return status; } +static void grep_add_color(struct strbuf *sb, const char *escape_seq) +{ + size_t orig_len = sb->len; + + while (*escape_seq) { + if (*escape_seq == 'm') + strbuf_addch(sb, ';'); + else if (*escape_seq != '\033' && *escape_seq != '[') + strbuf_addch(sb, *escape_seq); + escape_seq++; + } + if (sb->len > orig_len && sb->buf[sb->len - 1] == ';') + strbuf_setlen(sb, sb->len - 1); +} + static int external_grep(struct grep_opt *opt, const char **paths, int cached) { int i, nr, argc, hit, len, status; @@ -339,6 +376,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-e"); push_arg(p->pattern); } + if (opt->color) { + struct strbuf sb = STRBUF_INIT; + + grep_add_color(&sb, opt->color_match); + setenv("GREP_COLOR", sb.buf, 1); + + strbuf_reset(&sb); + strbuf_addstr(&sb, "mt="); + grep_add_color(&sb, opt->color_match); + strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se="); + setenv("GREP_COLORS", sb.buf, 1); + + strbuf_release(&sb); + + if (opt->color_external && strlen(opt->color_external) > 0) + push_arg(opt->color_external); + } hit = 0; argc = nr; @@ -536,6 +590,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; + strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); + opt.color = -1; + git_config(grep_config, &opt); + if (opt.color == -1) + opt.color = git_use_color_default; + /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or @@ -732,6 +792,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.relative = 0; continue; } + if (!strcmp("--color", arg)) { + opt.color = 1; + continue; + } + if (!strcmp("--no-color", arg)) { + opt.color = 0; + continue; + } if (!strcmp("--", arg)) { /* later processing wants to have this at argv[1] */ argv--; @@ -757,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } } + if (opt.color && !opt.color_external) + builtin_grep = 1; if (!opt.pattern_list) die("no pattern given."); if ((opt.regflags != REG_NEWLINE) && opt.fixed) diff --git a/builtin-init-db.c b/builtin-init-db.c index ee3911f8ee..fc63d0fce5 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -130,8 +130,7 @@ static void copy_templates(const char *template_dir) } dir = opendir(template_path); if (!dir) { - fprintf(stderr, "warning: templates not found %s\n", - template_dir); + warning("templates not found %s", template_dir); return; } @@ -144,8 +143,8 @@ static void copy_templates(const char *template_dir) if (repository_format_version && repository_format_version != GIT_REPO_VERSION) { - fprintf(stderr, "warning: not copying templates of " - "a wrong format version %d from '%s'\n", + warning("not copying templates of " + "a wrong format version %d from '%s'", repository_format_version, template_dir); closedir(dir); diff --git a/builtin-log.c b/builtin-log.c index 0f0adf2bab..27bc0dce23 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -17,6 +17,7 @@ #include "run-command.h" #include "shortlog.h" #include "remote.h" +#include "string-list.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@ -428,6 +429,8 @@ static const char *fmt_patch_suffix = ".patch"; static int numbered = 0; static int auto_number = 1; +static char *default_attach = NULL; + static char **extra_hdr; static int extra_hdr_nr; static int extra_hdr_alloc; @@ -459,6 +462,10 @@ static void add_header(const char *value) extra_hdr[extra_hdr_nr++] = xstrndup(value, len); } +#define THREAD_SHALLOW 1 +#define THREAD_DEEP 2 +static int thread = 0; + static int git_format_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "format.headers")) { @@ -488,6 +495,25 @@ static int git_format_config(const char *var, const char *value, void *cb) auto_number = auto_number && numbered; return 0; } + if (!strcmp(var, "format.attach")) { + if (value && *value) + default_attach = xstrdup(value); + else + default_attach = xstrdup(git_version_string); + return 0; + } + if (!strcmp(var, "format.thread")) { + if (value && !strcasecmp(value, "deep")) { + thread = THREAD_DEEP; + return 0; + } + if (value && !strcasecmp(value, "shallow")) { + thread = THREAD_SHALLOW; + return 0; + } + thread = git_config_bool(var, value) && THREAD_SHALLOW; + return 0; + } return git_log_config(var, value, cb); } @@ -547,7 +573,7 @@ static FILE *realstdout = NULL; static const char *output_directory = NULL; static int outdir_offset; -static int reopen_stdout(const char *oneline, int nr, int total) +static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev) { char filename[PATH_MAX]; int len = 0; @@ -572,7 +598,9 @@ static int reopen_stdout(const char *oneline, int nr, int total) strcpy(filename + len, fmt_patch_suffix); } - fprintf(realstdout, "%s\n", filename + outdir_offset); + if (!DIFF_OPT_TST(&rev->diffopt, QUIET)) + fprintf(realstdout, "%s\n", filename + outdir_offset); + if (freopen(filename, "w", stdout) == NULL) return error("Cannot open patch file %s",filename); @@ -661,7 +689,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, die("Cover letter needs email format"); if (!use_stdout && reopen_stdout(numbered_files ? - NULL : "cover-letter", 0, rev->total)) + NULL : "cover-letter", 0, rev)) return; head_sha1 = sha1_to_hex(head->object.sha1); @@ -766,7 +794,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int numbered_files = 0; /* _just_ numbers */ int subject_prefix = 0; int ignore_if_in_upstream = 0; - int thread = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; @@ -787,6 +814,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.subject_prefix = fmt_patch_subject_prefix; + if (default_attach) { + rev.mime_boundary = default_attach; + rev.no_inline = 1; + } + /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is @@ -849,6 +881,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.mime_boundary = argv[i] + 9; rev.no_inline = 1; } + else if (!strcmp(argv[i], "--no-attach")) { + rev.mime_boundary = NULL; + rev.no_inline = 0; + } else if (!strcmp(argv[i], "--inline")) { rev.mime_boundary = git_version_string; rev.no_inline = 0; @@ -859,8 +895,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } else if (!strcmp(argv[i], "--ignore-if-in-upstream")) ignore_if_in_upstream = 1; - else if (!strcmp(argv[i], "--thread")) - thread = 1; + else if (!strcmp(argv[i], "--thread") + || !strcmp(argv[i], "--thread=shallow")) + thread = THREAD_SHALLOW; + else if (!strcmp(argv[i], "--thread=deep")) + thread = THREAD_DEEP; + else if (!strcmp(argv[i], "--no-thread")) + thread = 0; else if (!prefixcmp(argv[i], "--in-reply-to=")) in_reply_to = argv[i] + 14; else if (!strcmp(argv[i], "--in-reply-to")) { @@ -877,6 +918,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) cover_letter = 1; else if (!strcmp(argv[i], "--no-binary")) no_binary_diff = 1; + else if (!prefixcmp(argv[i], "--add-header=")) + add_header(argv[i] + 13); else argv[j++] = argv[i]; } @@ -1009,8 +1052,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) numbered = 1; if (numbered) rev.total = total + start_number - 1; - if (in_reply_to) - rev.ref_message_id = clean_message_id(in_reply_to); + if (in_reply_to || thread || cover_letter) + rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); + if (in_reply_to) { + const char *msgid = clean_message_id(in_reply_to); + string_list_append(msgid, rev.ref_message_ids); + } if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); @@ -1029,21 +1076,39 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) /* Have we already had a message ID? */ if (rev.message_id) { /* - * If we've got the ID to be a reply - * to, discard the current ID; - * otherwise, make everything a reply - * to that. + * For deep threading: make every mail + * a reply to the previous one, no + * matter what other options are set. + * + * For shallow threading: + * + * Without --cover-letter and + * --in-reply-to, make every mail a + * reply to the one before. + * + * With --in-reply-to but no + * --cover-letter, make every mail a + * reply to the <reply-to>. + * + * With --cover-letter, make every + * mail but the cover letter a reply + * to the cover letter. The cover + * letter is a reply to the + * --in-reply-to, if specified. */ - if (rev.ref_message_id) + if (thread == THREAD_SHALLOW + && rev.ref_message_ids->nr > 0 + && (!cover_letter || rev.nr > 1)) free(rev.message_id); else - rev.ref_message_id = rev.message_id; + string_list_append(rev.message_id, + rev.ref_message_ids); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : get_oneline_for_filename(commit, keep_subject), - rev.nr, rev.total)) + rev.nr, &rev)) die("Failed to create output files"); shown = log_tree_commit(&rev, commit); free(commit->buffer); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index ca6f33d046..88e2697aeb 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -10,6 +10,7 @@ #include "dir.h" #include "builtin.h" #include "tree.h" +#include "parse-options.h" static int abbrev; static int show_deleted; @@ -28,6 +29,7 @@ static const char **pathspec; static int error_unmatch; static char *ps_matched; static const char *with_tree; +static int exc_given; static const char *tag_cached = ""; static const char *tag_unmerged = ""; @@ -174,7 +176,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; int dtype = ce_to_dtype(ce); - if (excluded(dir, ce->name, &dtype) != dir->show_ignored) + if (excluded(dir, ce->name, &dtype) != + !!(dir->flags & DIR_SHOW_IGNORED)) continue; if (show_unmerged && !ce_stage(ce)) continue; @@ -189,7 +192,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) struct stat st; int err; int dtype = ce_to_dtype(ce); - if (excluded(dir, ce->name, &dtype) != dir->show_ignored) + if (excluded(dir, ce->name, &dtype) != + !!(dir->flags & DIR_SHOW_IGNORED)) continue; if (ce->ce_flags & CE_UPDATE) continue; @@ -374,157 +378,139 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_ return errors; } -static const char ls_files_usage[] = - "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " - "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] " - "[ --exclude-per-directory=<filename> ] [--exclude-standard] " - "[--full-name] [--abbrev] [--] [<file>]*"; +static const char * const ls_files_usage[] = { + "git ls-files [options] [<file>]*", + NULL +}; + +static int option_parse_z(const struct option *opt, + const char *arg, int unset) +{ + line_terminator = unset ? '\n' : '\0'; + + return 0; +} + +static int option_parse_exclude(const struct option *opt, + const char *arg, int unset) +{ + struct exclude_list *list = opt->value; + + exc_given = 1; + add_exclude(arg, "", 0, list); + + return 0; +} + +static int option_parse_exclude_from(const struct option *opt, + const char *arg, int unset) +{ + struct dir_struct *dir = opt->value; + + exc_given = 1; + add_excludes_from_file(dir, arg); + + return 0; +} + +static int option_parse_exclude_standard(const struct option *opt, + const char *arg, int unset) +{ + struct dir_struct *dir = opt->value; + + exc_given = 1; + setup_standard_excludes(dir); + + return 0; +} int cmd_ls_files(int argc, const char **argv, const char *prefix) { - int i; - int exc_given = 0, require_work_tree = 0; + int require_work_tree = 0, show_tag = 0; struct dir_struct dir; + struct option builtin_ls_files_options[] = { + { OPTION_CALLBACK, 'z', NULL, NULL, NULL, + "paths are separated with NUL character", + PARSE_OPT_NOARG, option_parse_z }, + OPT_BOOLEAN('t', NULL, &show_tag, + "identify the file status with tags"), + OPT_BOOLEAN('v', NULL, &show_valid_bit, + "use lowercase letters for 'assume unchanged' files"), + OPT_BOOLEAN('c', "cached", &show_cached, + "show cached files in the output (default)"), + OPT_BOOLEAN('d', "deleted", &show_deleted, + "show deleted files in the output"), + OPT_BOOLEAN('m', "modified", &show_modified, + "show modified files in the output"), + OPT_BOOLEAN('o', "others", &show_others, + "show other files in the output"), + OPT_BIT('i', "ignored", &dir.flags, + "show ignored files in the output", + DIR_SHOW_IGNORED), + OPT_BOOLEAN('s', "stage", &show_stage, + "show staged contents' object name in the output"), + OPT_BOOLEAN('k', "killed", &show_killed, + "show files on the filesystem that need to be removed"), + OPT_BIT(0, "directory", &dir.flags, + "show 'other' directories' name only", + DIR_SHOW_OTHER_DIRECTORIES), + OPT_BIT(0, "no-empty-directory", &dir.flags, + "don't show empty directories", + DIR_HIDE_EMPTY_DIRECTORIES), + OPT_BOOLEAN('u', "unmerged", &show_unmerged, + "show unmerged files in the output"), + { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern", + "skip files matching pattern", + 0, option_parse_exclude }, + { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file", + "exclude patterns are read from <file>", + 0, option_parse_exclude_from }, + OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file", + "read additional per-directory exclude patterns in <file>"), + { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL, + "add the standard git exclusions", + PARSE_OPT_NOARG, option_parse_exclude_standard }, + { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL, + "make the output relative to the project top directory", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL }, + OPT_BOOLEAN(0, "error-unmatch", &error_unmatch, + "if any <file> is not in the index, treat this as an error"), + OPT_STRING(0, "with-tree", &with_tree, "tree-ish", + "pretend that paths removed since <tree-ish> are still present"), + OPT__ABBREV(&abbrev), + OPT_END() + }; memset(&dir, 0, sizeof(dir)); if (prefix) prefix_offset = strlen(prefix); git_config(git_default_config, NULL); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-z")) { - line_terminator = 0; - continue; - } - if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) { - tag_cached = "H "; - tag_unmerged = "M "; - tag_removed = "R "; - tag_modified = "C "; - tag_other = "? "; - tag_killed = "K "; - if (arg[1] == 'v') - show_valid_bit = 1; - continue; - } - if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) { - show_cached = 1; - continue; - } - if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { - show_deleted = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { - show_modified = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) { - show_others = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { - dir.show_ignored = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { - show_stage = 1; - continue; - } - if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) { - show_killed = 1; - require_work_tree = 1; - continue; - } - if (!strcmp(arg, "--directory")) { - dir.show_other_directories = 1; - continue; - } - if (!strcmp(arg, "--no-empty-directory")) { - dir.hide_empty_directories = 1; - continue; - } - if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { - /* There's no point in showing unmerged unless - * you also show the stage information. - */ - show_stage = 1; - show_unmerged = 1; - continue; - } - if (!strcmp(arg, "-x") && i+1 < argc) { - exc_given = 1; - add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); - continue; - } - if (!prefixcmp(arg, "--exclude=")) { - exc_given = 1; - add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); - continue; - } - if (!strcmp(arg, "-X") && i+1 < argc) { - exc_given = 1; - add_excludes_from_file(&dir, argv[++i]); - continue; - } - if (!prefixcmp(arg, "--exclude-from=")) { - exc_given = 1; - add_excludes_from_file(&dir, arg+15); - continue; - } - if (!prefixcmp(arg, "--exclude-per-directory=")) { - exc_given = 1; - dir.exclude_per_dir = arg + 24; - continue; - } - if (!strcmp(arg, "--exclude-standard")) { - exc_given = 1; - setup_standard_excludes(&dir); - continue; - } - if (!strcmp(arg, "--full-name")) { - prefix_offset = 0; - continue; - } - if (!strcmp(arg, "--error-unmatch")) { - error_unmatch = 1; - continue; - } - if (!prefixcmp(arg, "--with-tree=")) { - with_tree = arg + 12; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg+9, NULL, 10); - if (abbrev && abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - continue; - } - if (!strcmp(arg, "--abbrev")) { - abbrev = DEFAULT_ABBREV; - continue; - } - if (*arg == '-') - usage(ls_files_usage); - break; + argc = parse_options(argc, argv, builtin_ls_files_options, + ls_files_usage, 0); + if (show_tag || show_valid_bit) { + tag_cached = "H "; + tag_unmerged = "M "; + tag_removed = "R "; + tag_modified = "C "; + tag_other = "? "; + tag_killed = "K "; } + if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed) + require_work_tree = 1; + if (show_unmerged) + /* + * There's no point in showing unmerged unless + * you also show the stage information. + */ + show_stage = 1; + if (dir.exclude_per_dir) + exc_given = 1; if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); /* be nice with submodule patsh ending in a slash */ read_cache(); @@ -543,7 +529,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) ps_matched = xcalloc(1, num); } - if (dir.show_ignored && !exc_given) { + if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); exit(1); diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index fca46312f6..22008dfa8f 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -60,7 +60,6 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, { int retval = 0; const char *type = blob_type; - unsigned long size; if (S_ISGITLINK(mode)) { /* @@ -90,17 +89,20 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, if (!(ls_options & LS_NAME_ONLY)) { if (ls_options & LS_SHOW_SIZE) { + char size_text[24]; if (!strcmp(type, blob_type)) { - sha1_object_info(sha1, &size); - printf("%06o %s %s %7lu\t", mode, type, - abbrev ? find_unique_abbrev(sha1, abbrev) - : sha1_to_hex(sha1), - size); + unsigned long size; + if (sha1_object_info(sha1, &size) == OBJ_BAD) + strcpy(size_text, "BAD"); + else + snprintf(size_text, sizeof(size_text), + "%lu", size); } else - printf("%06o %s %s %7c\t", mode, type, - abbrev ? find_unique_abbrev(sha1, abbrev) - : sha1_to_hex(sha1), - '-'); + strcpy(size_text, "-"); + printf("%06o %s %s %7s\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1), + size_text); } else printf("%06o %s %s\t", mode, type, abbrev ? find_unique_abbrev(sha1, abbrev) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 2789ccdf7d..1eeeb4de6d 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -537,7 +537,6 @@ static int decode_header_bq(struct strbuf *it) */ strbuf_add(&outbuf, in, ep - in); } - in = ep; } /* E.g. * ep : "=?iso-2022-jp?B?GyR...?= foo" diff --git a/builtin-merge.c b/builtin-merge.c index 6d2160d0a3..4c119359e7 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -636,7 +636,7 @@ static int checkout_fast_forward(unsigned char *head, unsigned char *remote) memset(&opts, 0, sizeof(opts)); memset(&t, 0, sizeof(t)); memset(&dir, 0, sizeof(dir)); - dir.show_ignored = 1; + dir.flags |= DIR_SHOW_IGNORED; dir.exclude_per_dir = ".gitignore"; opts.dir = &dir; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 8ca46c8deb..9fc3b35547 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1966,11 +1966,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) const unsigned char *sha1; struct object *o; - for (i = 0; i < revs->num_ignore_packed; i++) { - if (matches_pack_name(p, revs->ignore_packed[i])) - break; - } - if (revs->num_ignore_packed <= i) + if (!p->pack_local || p->pack_keep) continue; if (open_pack_index(p)) die("cannot open pack index"); @@ -1999,6 +1995,29 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) free(in_pack.array); } +static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1) +{ + static struct packed_git *last_found = (void *)1; + struct packed_git *p; + + p = (last_found != (void *)1) ? last_found : packed_git; + + while (p) { + if ((!p->pack_local || p->pack_keep) && + find_pack_entry_one(sha1, p)) { + last_found = p; + return 1; + } + if (p == last_found) + p = packed_git; + else + p = p->next; + if (p == last_found) + p = p->next; + } + return 0; +} + static void loosen_unused_packed_objects(struct rev_info *revs) { struct packed_git *p; @@ -2006,11 +2025,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) const unsigned char *sha1; for (p = packed_git; p; p = p->next) { - for (i = 0; i < revs->num_ignore_packed; i++) { - if (matches_pack_name(p, revs->ignore_packed[i])) - break; - } - if (revs->num_ignore_packed <= i) + if (!p->pack_local || p->pack_keep) continue; if (open_pack_index(p)) @@ -2018,7 +2033,8 @@ static void loosen_unused_packed_objects(struct rev_info *revs) for (i = 0; i < p->num_objects; i++) { sha1 = nth_packed_object_sha1(p, i); - if (!locate_object_entry(sha1)) + if (!locate_object_entry(sha1) && + !has_sha1_pack_kept_or_nonlocal(sha1)) if (force_object_loose(sha1, p->mtime)) die("unable to force loose object"); } @@ -2208,7 +2224,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) continue; } if (!strcmp("--unpacked", arg) || - !prefixcmp(arg, "--unpacked=") || !strcmp("--reflog", arg) || !strcmp("--all", arg)) { use_internal_rev_list = 1; diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 10cb8df845..2d5b2cd353 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -23,7 +23,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) memcpy(hex+2, de->d_name, 38); if (get_sha1_hex(hex, sha1)) continue; - if (!has_sha1_pack(sha1, NULL)) + if (!has_sha1_pack(sha1)) continue; memcpy(pathname + len, de->d_name, 38); if (opts & DRY_RUN) diff --git a/builtin-push.c b/builtin-push.c index ca36fb1e58..2eabcd3bdf 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -48,6 +48,71 @@ static void set_refspecs(const char **refs, int nr) } } +static void setup_push_tracking(void) +{ + struct strbuf refspec = STRBUF_INIT; + struct branch *branch = branch_get(NULL); + if (!branch) + die("You are not currently on a branch."); + if (!branch->merge_nr) + die("The current branch %s is not tracking anything.", + branch->name); + if (branch->merge_nr != 1) + die("The current branch %s is tracking multiple branches, " + "refusing to push.", branch->name); + strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src); + add_refspec(refspec.buf); +} + +static const char *warn_unconfigured_push_msg[] = { + "You did not specify any refspecs to push, and the current remote", + "has not configured any push refspecs. The default action in this", + "case is to push all matching refspecs, that is, all branches", + "that exist both locally and remotely will be updated. This may", + "not necessarily be what you want to happen.", + "", + "You can specify what action you want to take in this case, and", + "avoid seeing this message again, by configuring 'push.default' to:", + " 'nothing' : Do not push anything", + " 'matching' : Push all matching branches (default)", + " 'tracking' : Push the current branch to whatever it is tracking", + " 'current' : Push the current branch" +}; + +static void warn_unconfigured_push(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++) + warning("%s", warn_unconfigured_push_msg[i]); +} + +static void setup_default_push_refspecs(void) +{ + git_config(git_default_config, NULL); + switch (push_default) { + case PUSH_DEFAULT_UNSPECIFIED: + warn_unconfigured_push(); + /* fallthrough */ + + case PUSH_DEFAULT_MATCHING: + add_refspec(":"); + break; + + case PUSH_DEFAULT_TRACKING: + setup_push_tracking(); + break; + + case PUSH_DEFAULT_CURRENT: + add_refspec("HEAD"); + break; + + case PUSH_DEFAULT_NOTHING: + die("You didn't specify any refspecs to push, and " + "push.default is \"nothing\"."); + break; + } +} + static int do_push(const char *repo, int flags) { int i, errs; @@ -79,11 +144,12 @@ static int do_push(const char *repo, int flags) return error("--all and --mirror are incompatible"); } - if (!refspec - && !(flags & TRANSPORT_PUSH_ALL) - && remote->push_refspec_nr) { - refspec = remote->push_refspec; - refspec_nr = remote->push_refspec_nr; + if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) { + if (remote->push_refspec_nr) { + refspec = remote->push_refspec; + refspec_nr = remote->push_refspec_nr; + } else if (!(flags & TRANSPORT_PUSH_MIRROR)) + setup_default_push_refspecs(); } errs = 0; for (i = 0; i < remote->url_nr; i++) { diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 38fef34d3f..8e0273864d 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -170,7 +170,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) die("more than one --exclude-per-directory are given."); dir = xcalloc(1, sizeof(*opts.dir)); - dir->show_ignored = 1; + dir->flags |= DIR_SHOW_IGNORED; dir->exclude_per_dir = arg + 24; opts.dir = dir; /* We do not need to nor want to do read-directory diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 849f1fe6f9..a970b39505 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -675,7 +675,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) setup_path(); if (!enter_repo(dir, 0)) - die("'%s': unable to chdir or not a git archive", dir); + die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to push into a shallow repository"); diff --git a/builtin-remote.c b/builtin-remote.c index ac69d37c8a..9ef846f6a4 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -12,12 +12,17 @@ static const char * const builtin_remote_usage[] = { "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>", "git remote rename <old> <new>", "git remote rm <name>", + "git remote set-head <name> [-a | -d | <branch>]", "git remote show [-n] <name>", "git remote prune [-n | --dry-run] <name>", "git remote [-v | --verbose] update [group]", NULL }; +#define GET_REF_STATES (1<<0) +#define GET_HEAD_NAMES (1<<1) +#define GET_PUSH_REF_STATES (1<<2) + static int verbose; static int show_all(void); @@ -143,8 +148,9 @@ static int add(int argc, const char **argv) } struct branch_info { - char *remote; + char *remote_name; struct string_list merge; + int rebase; }; static struct string_list branch_list; @@ -161,10 +167,11 @@ static const char *abbrev_ref(const char *name, const char *prefix) static int config_read_branches(const char *key, const char *value, void *cb) { if (!prefixcmp(key, "branch.")) { + const char *orig_key = key; char *name; struct string_list_item *item; struct branch_info *info; - enum { REMOTE, MERGE } type; + enum { REMOTE, MERGE, REBASE } type; key += 7; if (!postfixcmp(key, ".remote")) { @@ -173,6 +180,9 @@ static int config_read_branches(const char *key, const char *value, void *cb) } else if (!postfixcmp(key, ".merge")) { name = xstrndup(key, strlen(key) - 6); type = MERGE; + } else if (!postfixcmp(key, ".rebase")) { + name = xstrndup(key, strlen(key) - 7); + type = REBASE; } else return 0; @@ -182,10 +192,10 @@ static int config_read_branches(const char *key, const char *value, void *cb) item->util = xcalloc(sizeof(struct branch_info), 1); info = item->util; if (type == REMOTE) { - if (info->remote) - warning("more than one branch.%s", key); - info->remote = xstrdup(value); - } else { + if (info->remote_name) + warning("more than one %s", orig_key); + info->remote_name = xstrdup(value); + } else if (type == MERGE) { char *space = strchr(value, ' '); value = abbrev_branch(value); while (space) { @@ -196,7 +206,8 @@ static int config_read_branches(const char *key, const char *value, void *cb) space = strchr(value, ' '); } string_list_append(xstrdup(value), &info->merge); - } + } else + info->rebase = git_config_bool(orig_key, value); } return 0; } @@ -206,12 +217,12 @@ static void read_branches(void) if (branch_list.nr) return; git_config(config_read_branches, NULL); - sort_string_list(&branch_list); } struct ref_states { struct remote *remote; - struct string_list new, stale, tracked; + struct string_list new, stale, tracked, heads, push; + int queried; }; static int handle_one_branch(const char *refname, @@ -227,10 +238,8 @@ static int handle_one_branch(const char *refname, const char *name = abbrev_branch(refspec.src); /* symbolic refs pointing nowhere were handled already */ if ((flags & REF_ISSYMREF) || - unsorted_string_list_has_string(&states->tracked, - name) || - unsorted_string_list_has_string(&states->new, - name)) + string_list_has_string(&states->tracked, name) || + string_list_has_string(&states->new, name)) return 0; item = string_list_append(name, &states->stale); item->util = xstrdup(refname); @@ -238,39 +247,154 @@ static int handle_one_branch(const char *refname, return 0; } -static int get_ref_states(const struct ref *ref, struct ref_states *states) +static int get_ref_states(const struct ref *remote_refs, struct ref_states *states) { struct ref *fetch_map = NULL, **tail = &fetch_map; + struct ref *ref; int i; for (i = 0; i < states->remote->fetch_refspec_nr; i++) - if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1)) + if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1)) die("Could not get fetch map for refspec %s", states->remote->fetch_refspec[i]); states->new.strdup_strings = states->tracked.strdup_strings = 1; for (ref = fetch_map; ref; ref = ref->next) { - struct string_list *target = &states->tracked; unsigned char sha1[20]; - void *util = NULL; - if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1)) - target = &states->new; - else { - target = &states->tracked; - if (hashcmp(sha1, ref->new_sha1)) - util = &states; - } - string_list_append(abbrev_branch(ref->name), target)->util = util; + string_list_append(abbrev_branch(ref->name), &states->new); + else + string_list_append(abbrev_branch(ref->name), &states->tracked); } free_refs(fetch_map); + sort_string_list(&states->new); + sort_string_list(&states->tracked); for_each_ref(handle_one_branch, states); sort_string_list(&states->stale); return 0; } +struct push_info { + char *dest; + int forced; + enum { + PUSH_STATUS_CREATE = 0, + PUSH_STATUS_DELETE, + PUSH_STATUS_UPTODATE, + PUSH_STATUS_FASTFORWARD, + PUSH_STATUS_OUTOFDATE, + PUSH_STATUS_NOTQUERIED, + } status; +}; + +static int get_push_ref_states(const struct ref *remote_refs, + struct ref_states *states) +{ + struct remote *remote = states->remote; + struct ref *ref, *local_refs, *push_map, **push_tail; + if (remote->mirror) + return 0; + + local_refs = get_local_heads(); + ref = push_map = copy_ref_list(remote_refs); + while (ref->next) + ref = ref->next; + push_tail = &ref->next; + + match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr, + remote->push_refspec, MATCH_REFS_NONE); + + states->push.strdup_strings = 1; + for (ref = push_map; ref; ref = ref->next) { + struct string_list_item *item; + struct push_info *info; + + if (!ref->peer_ref) + continue; + hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); + + item = string_list_append(abbrev_branch(ref->peer_ref->name), + &states->push); + item->util = xcalloc(sizeof(struct push_info), 1); + info = item->util; + info->forced = ref->force; + info->dest = xstrdup(abbrev_branch(ref->name)); + + if (is_null_sha1(ref->new_sha1)) { + info->status = PUSH_STATUS_DELETE; + } else if (!hashcmp(ref->old_sha1, ref->new_sha1)) + info->status = PUSH_STATUS_UPTODATE; + else if (is_null_sha1(ref->old_sha1)) + info->status = PUSH_STATUS_CREATE; + else if (has_sha1_file(ref->old_sha1) && + ref_newer(ref->new_sha1, ref->old_sha1)) + info->status = PUSH_STATUS_FASTFORWARD; + else + info->status = PUSH_STATUS_OUTOFDATE; + } + free_refs(local_refs); + free_refs(push_map); + return 0; +} + +static int get_push_ref_states_noquery(struct ref_states *states) +{ + int i; + struct remote *remote = states->remote; + struct string_list_item *item; + struct push_info *info; + + if (remote->mirror) + return 0; + + states->push.strdup_strings = 1; + if (!remote->push_refspec_nr) { + item = string_list_append("(matching)", &states->push); + info = item->util = xcalloc(sizeof(struct push_info), 1); + info->status = PUSH_STATUS_NOTQUERIED; + info->dest = xstrdup(item->string); + } + for (i = 0; i < remote->push_refspec_nr; i++) { + struct refspec *spec = remote->push + i; + if (spec->matching) + item = string_list_append("(matching)", &states->push); + else if (strlen(spec->src)) + item = string_list_append(spec->src, &states->push); + else + item = string_list_append("(delete)", &states->push); + + info = item->util = xcalloc(sizeof(struct push_info), 1); + info->forced = spec->force; + info->status = PUSH_STATUS_NOTQUERIED; + info->dest = xstrdup(spec->dst ? spec->dst : item->string); + } + return 0; +} + +static int get_head_names(const struct ref *remote_refs, struct ref_states *states) +{ + struct ref *ref, *matches; + struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; + struct refspec refspec; + + refspec.force = 0; + refspec.pattern = 1; + refspec.src = refspec.dst = "refs/heads/*"; + states->heads.strdup_strings = 1; + get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); + matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), + fetch_map, 1); + for(ref = matches; ref; ref = ref->next) + string_list_append(abbrev_branch(ref->name), &states->heads); + + free_refs(fetch_map); + free_refs(matches); + + return 0; +} + struct known_remote { struct known_remote *next; struct remote *remote; @@ -466,7 +590,7 @@ static int mv(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote && !strcmp(info->remote, rename.old)) { + if (info->remote_name && !strcmp(info->remote_name, rename.old)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); if (git_config_set(buf.buf, rename.new)) { @@ -484,9 +608,8 @@ static int mv(int argc, const char **argv) struct string_list_item *item = remote_branches.items + i; int flag = 0; unsigned char sha1[20]; - const char *symref; - symref = resolve_ref(item->string, sha1, 1, &flag); + resolve_ref(item->string, sha1, 1, &flag); if (!(flag & REF_ISSYMREF)) continue; if (delete_ref(item->string, NULL, REF_NODEREF)) @@ -576,7 +699,7 @@ static int rm(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote && !strcmp(info->remote, remote->name)) { + if (info->remote_name && !strcmp(info->remote_name, remote->name)) { const char *keys[] = { "remote", "merge", NULL }, **k; for (k = keys; *k; k++) { strbuf_reset(&buf); @@ -618,18 +741,37 @@ static int rm(int argc, const char **argv) return result; } -static void show_list(const char *title, struct string_list *list, - const char *extra_arg) +void clear_push_info(void *util, const char *string) { - int i; + struct push_info *info = util; + free(info->dest); + free(info); +} - if (!list->nr) - return; +static void free_remote_ref_states(struct ref_states *states) +{ + string_list_clear(&states->new, 0); + string_list_clear(&states->stale, 0); + string_list_clear(&states->tracked, 0); + string_list_clear(&states->heads, 0); + string_list_clear_func(&states->push, clear_push_info); +} + +static int append_ref_to_tracked_list(const char *refname, + const unsigned char *sha1, int flags, void *cb_data) +{ + struct ref_states *states = cb_data; + struct refspec refspec; + + if (flags & REF_ISSYMREF) + return 0; + + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (!remote_find_tracking(states->remote, &refspec)) + string_list_append(abbrev_branch(refspec.src), &states->tracked); - printf(title, list->nr > 1 ? "es" : "", extra_arg); - printf("\n"); - for (i = 0; i < list->nr; i++) - printf(" %s\n", list->items[i].string); + return 0; } static int get_remote_ref_states(const char *name, @@ -637,7 +779,7 @@ static int get_remote_ref_states(const char *name, int query) { struct transport *transport; - const struct ref *ref; + const struct ref *remote_refs; states->remote = remote_get(name); if (!states->remote) @@ -648,102 +790,333 @@ static int get_remote_ref_states(const char *name, if (query) { transport = transport_get(NULL, states->remote->url_nr > 0 ? states->remote->url[0] : NULL); - ref = transport_get_remote_refs(transport); + remote_refs = transport_get_remote_refs(transport); transport_disconnect(transport); - get_ref_states(ref, states); + states->queried = 1; + if (query & GET_REF_STATES) + get_ref_states(remote_refs, states); + if (query & GET_HEAD_NAMES) + get_head_names(remote_refs, states); + if (query & GET_PUSH_REF_STATES) + get_push_ref_states(remote_refs, states); + } else { + for_each_ref(append_ref_to_tracked_list, states); + sort_string_list(&states->tracked); + get_push_ref_states_noquery(states); } return 0; } -static int append_ref_to_tracked_list(const char *refname, - const unsigned char *sha1, int flags, void *cb_data) +struct show_info { + struct string_list *list; + struct ref_states *states; + int width, width2; + int any_rebase; +}; + +int add_remote_to_show_info(struct string_list_item *item, void *cb_data) { - struct ref_states *states = cb_data; - struct refspec refspec; + struct show_info *info = cb_data; + int n = strlen(item->string); + if (n > info->width) + info->width = n; + string_list_insert(item->string, info->list); + return 0; +} - memset(&refspec, 0, sizeof(refspec)); - refspec.dst = (char *)refname; - if (!remote_find_tracking(states->remote, &refspec)) - string_list_append(abbrev_branch(refspec.src), &states->tracked); +int show_remote_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *info = cb_data; + struct ref_states *states = info->states; + const char *name = item->string; + + if (states->queried) { + const char *fmt = "%s"; + const char *arg = ""; + if (string_list_has_string(&states->new, name)) { + fmt = " new (next fetch will store in remotes/%s)"; + arg = states->remote->name; + } else if (string_list_has_string(&states->tracked, name)) + arg = " tracked"; + else if (string_list_has_string(&states->stale, name)) + arg = " stale (use 'git remote prune' to remove)"; + else + arg = " ???"; + printf(" %-*s", info->width, name); + printf(fmt, arg); + printf("\n"); + } else + printf(" %s\n", name); + + return 0; +} + +int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct ref_states *states = show_info->states; + struct branch_info *branch_info = branch_item->util; + struct string_list_item *item; + int n; + + if (!branch_info->merge.nr || !branch_info->remote_name || + strcmp(states->remote->name, branch_info->remote_name)) + return 0; + if ((n = strlen(branch_item->string)) > show_info->width) + show_info->width = n; + if (branch_info->rebase) + show_info->any_rebase = 1; + + item = string_list_insert(branch_item->string, show_info->list); + item->util = branch_info; + + return 0; +} + +int show_local_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct branch_info *branch_info = item->util; + struct string_list *merge = &branch_info->merge; + const char *also; + int i; + + if (branch_info->rebase && branch_info->merge.nr > 1) { + error("invalid branch.%s.merge; cannot rebase onto > 1 branch", + item->string); + return 0; + } + + printf(" %-*s ", show_info->width, item->string); + if (branch_info->rebase) { + printf("rebases onto remote %s\n", merge->items[0].string); + return 0; + } else if (show_info->any_rebase) { + printf(" merges with remote %s\n", merge->items[0].string); + also = " and with remote"; + } else { + printf("merges with remote %s\n", merge->items[0].string); + also = " and with remote"; + } + for (i = 1; i < merge->nr; i++) + printf(" %-*s %s %s\n", show_info->width, "", also, + merge->items[i].string); return 0; } +int add_push_to_show_info(struct string_list_item *push_item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct push_info *push_info = push_item->util; + struct string_list_item *item; + int n; + if ((n = strlen(push_item->string)) > show_info->width) + show_info->width = n; + if ((n = strlen(push_info->dest)) > show_info->width2) + show_info->width2 = n; + item = string_list_append(push_item->string, show_info->list); + item->util = push_item->util; + return 0; +} + +/* + * Sorting comparison for a string list that has push_info + * structs in its util field + */ +static int cmp_string_with_push(const void *va, const void *vb) +{ + const struct string_list_item *a = va; + const struct string_list_item *b = vb; + const struct push_info *a_push = a->util; + const struct push_info *b_push = b->util; + int cmp = strcmp(a->string, b->string); + return cmp ? cmp : strcmp(a_push->dest, b_push->dest); +} + +int show_push_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct push_info *push_info = item->util; + char *src = item->string, *status = NULL; + + switch (push_info->status) { + case PUSH_STATUS_CREATE: + status = "create"; + break; + case PUSH_STATUS_DELETE: + status = "delete"; + src = "(none)"; + break; + case PUSH_STATUS_UPTODATE: + status = "up to date"; + break; + case PUSH_STATUS_FASTFORWARD: + status = "fast forwardable"; + break; + case PUSH_STATUS_OUTOFDATE: + status = "local out of date"; + break; + case PUSH_STATUS_NOTQUERIED: + break; + } + if (status) + printf(" %-*s %s to %-*s (%s)\n", show_info->width, src, + push_info->forced ? "forces" : "pushes", + show_info->width2, push_info->dest, status); + else + printf(" %-*s %s to %s\n", show_info->width, src, + push_info->forced ? "forces" : "pushes", + push_info->dest); + return 0; +} + static int show(int argc, const char **argv) { - int no_query = 0, result = 0; + int no_query = 0, result = 0, query_flag = 0; struct option options[] = { OPT_GROUP("show specific options"), OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"), OPT_END() }; struct ref_states states; + struct string_list info_list = { NULL, 0, 0, 0 }; + struct show_info info; argc = parse_options(argc, argv, options, builtin_remote_usage, 0); if (argc < 1) return show_all(); + if (!no_query) + query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES); + memset(&states, 0, sizeof(states)); + memset(&info, 0, sizeof(info)); + info.states = &states; + info.list = &info_list; for (; argc; argc--, argv++) { int i; - get_remote_ref_states(*argv, &states, !no_query); + get_remote_ref_states(*argv, &states, query_flag); printf("* remote %s\n URL: %s\n", *argv, states.remote->url_nr > 0 ? states.remote->url[0] : "(no URL)"); - - for (i = 0; i < branch_list.nr; i++) { - struct string_list_item *branch = branch_list.items + i; - struct branch_info *info = branch->util; - int j; - - if (!info->merge.nr || strcmp(*argv, info->remote)) - continue; - printf(" Remote branch%s merged with 'git pull' " - "while on branch %s\n ", - info->merge.nr > 1 ? "es" : "", - branch->string); - for (j = 0; j < info->merge.nr; j++) - printf(" %s", info->merge.items[j].string); - printf("\n"); + if (no_query) + printf(" HEAD branch: (not queried)\n"); + else if (!states.heads.nr) + printf(" HEAD branch: (unknown)\n"); + else if (states.heads.nr == 1) + printf(" HEAD branch: %s\n", states.heads.items[0].string); + else { + printf(" HEAD branch (remote HEAD is ambiguous," + " may be one of the following):\n"); + for (i = 0; i < states.heads.nr; i++) + printf(" %s\n", states.heads.items[i].string); } - if (!no_query) { - show_list(" New remote branch%s (next fetch " - "will store in remotes/%s)", - &states.new, states.remote->name); - show_list(" Stale tracking branch%s (use 'git remote " - "prune')", &states.stale, ""); - } + /* remote branch info */ + info.width = 0; + for_each_string_list(add_remote_to_show_info, &states.new, &info); + for_each_string_list(add_remote_to_show_info, &states.tracked, &info); + for_each_string_list(add_remote_to_show_info, &states.stale, &info); + if (info.list->nr) + printf(" Remote branch%s:%s\n", + info.list->nr > 1 ? "es" : "", + no_query ? " (status not queried)" : ""); + for_each_string_list(show_remote_info_item, info.list, &info); + string_list_clear(info.list, 0); + + /* git pull info */ + info.width = 0; + info.any_rebase = 0; + for_each_string_list(add_local_to_show_info, &branch_list, &info); + if (info.list->nr) + printf(" Local branch%s configured for 'git pull':\n", + info.list->nr > 1 ? "es" : ""); + for_each_string_list(show_local_info_item, info.list, &info); + string_list_clear(info.list, 0); + + /* git push info */ + if (states.remote->mirror) + printf(" Local refs will be mirrored by 'git push'\n"); + + info.width = info.width2 = 0; + for_each_string_list(add_push_to_show_info, &states.push, &info); + qsort(info.list->items, info.list->nr, + sizeof(*info.list->items), cmp_string_with_push); + if (info.list->nr) + printf(" Local ref%s configured for 'git push'%s:\n", + info.list->nr > 1 ? "s" : "", + no_query ? " (status not queried)" : ""); + for_each_string_list(show_push_info_item, info.list, &info); + string_list_clear(info.list, 0); + + free_remote_ref_states(&states); + } - if (no_query) - for_each_ref(append_ref_to_tracked_list, &states); - show_list(" Tracked remote branch%s", &states.tracked, ""); - - if (states.remote->push_refspec_nr) { - printf(" Local branch%s pushed with 'git push'\n", - states.remote->push_refspec_nr > 1 ? - "es" : ""); - for (i = 0; i < states.remote->push_refspec_nr; i++) { - struct refspec *spec = states.remote->push + i; - printf(" %s%s%s%s\n", - spec->force ? "+" : "", - abbrev_branch(spec->src), - spec->dst ? ":" : "", - spec->dst ? abbrev_branch(spec->dst) : ""); - } - } + return result; +} - /* NEEDSWORK: free remote */ - string_list_clear(&states.new, 0); - string_list_clear(&states.stale, 0); - string_list_clear(&states.tracked, 0); +static int set_head(int argc, const char **argv) +{ + int i, opt_a = 0, opt_d = 0, result = 0; + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; + char *head_name = NULL; + + struct option options[] = { + OPT_GROUP("set-head specific options"), + OPT_BOOLEAN('a', "auto", &opt_a, + "set refs/remotes/<name>/HEAD according to remote"), + OPT_BOOLEAN('d', "delete", &opt_d, + "delete refs/remotes/<name>/HEAD"), + OPT_END() + }; + argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + if (argc) + strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); + + if (!opt_a && !opt_d && argc == 2) { + head_name = xstrdup(argv[1]); + } else if (opt_a && !opt_d && argc == 1) { + struct ref_states states; + memset(&states, 0, sizeof(states)); + get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES); + if (!states.heads.nr) + result |= error("Cannot determine remote HEAD"); + else if (states.heads.nr > 1) { + result |= error("Multiple remote HEAD branches. " + "Please choose one explicitly with:"); + for (i = 0; i < states.heads.nr; i++) + fprintf(stderr, " git remote set-head %s %s\n", + argv[0], states.heads.items[i].string); + } else + head_name = xstrdup(states.heads.items[0].string); + free_remote_ref_states(&states); + } else if (opt_d && !opt_a && argc == 1) { + if (delete_ref(buf.buf, NULL, REF_NODEREF)) + result |= error("Could not delete %s", buf.buf); + } else + usage_with_options(builtin_remote_usage, options); + + if (head_name) { + unsigned char sha1[20]; + strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name); + /* make sure it's valid */ + if (!resolve_ref(buf2.buf, sha1, 1, NULL)) + result |= error("Not a valid ref: %s", buf2.buf); + else if (create_symref(buf.buf, buf2.buf, "remote set-head")) + result |= error("Could not setup %s", buf.buf); + if (opt_a) + printf("%s/HEAD set to %s\n", argv[0], head_name); + free(head_name); } + strbuf_release(&buf); + strbuf_release(&buf2); return result; } @@ -771,7 +1144,7 @@ static int prune(int argc, const char **argv) for (; argc; argc--, argv++) { int i; - get_remote_ref_states(*argv, &states, 1); + get_remote_ref_states(*argv, &states, GET_REF_STATES); if (states.stale.nr) { printf("Pruning %s\n", *argv); @@ -792,10 +1165,7 @@ static int prune(int argc, const char **argv) warn_dangling_symref(dangling_msg, refname); } - /* NEEDSWORK: free remote */ - string_list_clear(&states.new, 0); - string_list_clear(&states.stale, 0); - string_list_clear(&states.tracked, 0); + free_remote_ref_states(&states); } return result; @@ -920,6 +1290,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix) result = mv(argc, argv); else if (!strcmp(argv[0], "rm")) result = rm(argc, argv); + else if (!strcmp(argv[0], "set-head")) + result = set_head(argc, argv); else if (!strcmp(argv[0], "show")) result = show(argc, argv); else if (!strcmp(argv[0], "prune")) diff --git a/builtin-rerere.c b/builtin-rerere.c index bd8fc77a7a..020af7377b 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -13,28 +13,17 @@ static const char git_rerere_usage[] = static int cutoff_noresolve = 15; static int cutoff_resolve = 60; -static const char *rr_path(const char *name, const char *file) -{ - return git_path("rr-cache/%s/%s", name, file); -} - static time_t rerere_created_at(const char *name) { struct stat st; - return stat(rr_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime; -} - -static int has_resolution(const char *name) -{ - struct stat st; - return !stat(rr_path(name, "postimage"), &st); + return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime; } static void unlink_rr_item(const char *name) { - unlink(rr_path(name, "thisimage")); - unlink(rr_path(name, "preimage")); - unlink(rr_path(name, "postimage")); + unlink(rerere_path(name, "thisimage")); + unlink(rerere_path(name, "preimage")); + unlink(rerere_path(name, "postimage")); rmdir(git_path("rr-cache/%s", name)); } @@ -65,7 +54,7 @@ static void garbage_collect(struct string_list *rr) then = rerere_created_at(e->d_name); if (!then) continue; - cutoff = (has_resolution(e->d_name) + cutoff = (has_rerere_resolution(e->d_name) ? cutoff_resolve : cutoff_noresolve); if (then < now - cutoff * 86400) string_list_append(e->d_name, &to_remove); @@ -124,7 +113,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) if (!strcmp(argv[1], "clear")) { for (i = 0; i < merge_rr.nr; i++) { const char *name = (const char *)merge_rr.items[i].util; - if (!has_resolution(name)) + if (!has_rerere_resolution(name)) unlink_rr_item(name); } unlink(git_path("rr-cache/MERGE_RR")); @@ -137,7 +126,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; - diff_two(rr_path(name, "preimage"), path, path, path); + diff_two(rerere_path(name, "preimage"), path, path, path); } else usage(git_rerere_usage); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 436afa45f5..40d5fcb6b0 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -574,6 +574,45 @@ static struct commit_list *find_bisection(struct commit_list *list, return best; } +static inline int log2i(int n) +{ + int log2 = 0; + + for (; n > 1; n >>= 1) + log2++; + + return log2; +} + +static inline int exp2i(int n) +{ + return 1 << n; +} + +/* + * Estimate the number of bisect steps left (after the current step) + * + * For any x between 0 included and 2^n excluded, the probability for + * n - 1 steps left looks like: + * + * P(2^n + x) == (2^n - x) / (2^n + x) + * + * and P(2^n + x) < 0.5 means 2^n < 3x + */ +static int estimate_bisect_steps(int all) +{ + int n, x, e; + + if (all < 3) + return 0; + + n = log2i(all); + e = exp2i(n); + x = all - e; + + return (e < 3 * x) ? n : n - 1; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct commit_list *list; @@ -688,12 +727,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) "bisect_nr=%d\n" "bisect_good=%d\n" "bisect_bad=%d\n" - "bisect_all=%d\n", + "bisect_all=%d\n" + "bisect_steps=%d\n", hex, cnt - 1, all - reaches - 1, reaches - 1, - all); + all, + estimate_bisect_steps(all)); return 0; } } diff --git a/builtin-rm.c b/builtin-rm.c index c11f455858..269d60890a 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -59,8 +59,7 @@ static int check_local_mod(unsigned char *head, int index_only) if (lstat(ce->name, &st) < 0) { if (errno != ENOENT) - fprintf(stderr, "warning: '%s': %s", - ce->name, strerror(errno)); + warning("'%s': %s", ce->name, strerror(errno)); /* It already vanished from the working tree */ continue; } diff --git a/builtin-send-pack.c b/builtin-send-pack.c index d65d019692..d5a1c48d0e 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -1,6 +1,5 @@ #include "cache.h" #include "commit.h" -#include "tag.h" #include "refs.h" #include "pkt-line.h" #include "run-command.h" @@ -11,9 +10,7 @@ static const char send_pack_usage[] = "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n" " --all and explicit <ref> specification are mutually exclusive."; -static struct send_pack_args args = { - /* .receivepack = */ "git-receive-pack", -}; +static struct send_pack_args args; static int feed_object(const unsigned char *sha1, int fd, int negative) { @@ -32,7 +29,7 @@ static int feed_object(const unsigned char *sha1, int fd, int negative) /* * Make a pack stream and spit it out into file descriptor fd */ -static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra) +static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args) { /* * The child becomes pack-objects --revs; we feed @@ -50,7 +47,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext struct child_process po; int i; - if (args.use_thin_pack) + if (args->use_thin_pack) argv[4] = "--thin"; memset(&po, 0, sizeof(po)); po.argv = argv; @@ -84,82 +81,6 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext return 0; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, 1); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, 1); - unmark_and_free(used, 1); - return found; -} - -static struct ref *local_refs, **local_tail; -static struct ref *remote_refs, **remote_tail; - -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len; - - /* we already know it starts with refs/ to get here */ - if (check_ref_format(refname + 5)) - return 0; - - len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - static int receive_status(int in, struct ref *refs) { struct ref *hint; @@ -247,16 +168,6 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static const char *prettify_ref(const struct ref *ref) -{ - const char *name = ref->name; - return name + ( - !prefixcmp(name, "refs/heads/") ? 11 : - !prefixcmp(name, "refs/tags/") ? 10 : - !prefixcmp(name, "refs/remotes/") ? 13 : - 0); -} - #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) @@ -385,27 +296,19 @@ static int refs_pushed(struct ref *ref) return 0; } -static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) +int send_pack(struct send_pack_args *args, + int fd[], struct child_process *conn, + struct ref *remote_refs, + struct extra_have_objects *extra_have) { + int in = fd[0]; + int out = fd[1]; struct ref *ref; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; - int flags = MATCH_REFS_NONE; int ret; - struct extra_have_objects extra_have; - - memset(&extra_have, 0, sizeof(extra_have)); - if (args.send_all) - flags |= MATCH_REFS_ALL; - if (args.send_mirror) - flags |= MATCH_REFS_MIRROR; - - /* No funny business with the matcher */ - remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL, - &extra_have); - get_local_heads(); /* Does the other end support the reporting? */ if (server_supports("report-status")) @@ -413,19 +316,9 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (server_supports("delete-refs")) allow_deleting_refs = 1; - /* match them up */ - if (!remote_tail) - remote_tail = &remote_refs; - if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, flags)) { - close(out); - return -1; - } - if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" "Perhaps you should specify a branch such as 'master'.\n"); - close(out); return 0; } @@ -437,7 +330,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); - else if (!args.send_mirror) + else if (!args->send_mirror) continue; ref->deletion = is_null_sha1(ref->new_sha1); @@ -476,7 +369,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->new_sha1, ref->old_sha1)); - if (ref->nonfastforward && !ref->force && !args.force_update) { + if (ref->nonfastforward && !ref->force && !args->force_update) { ref->status = REF_STATUS_REJECT_NONFASTFORWARD; continue; } @@ -484,7 +377,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (!ref->deletion) new_refs++; - if (!args.dry_run) { + if (!args->dry_run) { char *old_hex = sha1_to_hex(ref->old_sha1); char *new_hex = sha1_to_hex(ref->new_sha1); @@ -505,27 +398,19 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest } packet_flush(out); - if (new_refs && !args.dry_run) { - if (pack_objects(out, remote_refs, &extra_have) < 0) + if (new_refs && !args->dry_run) { + if (pack_objects(out, remote_refs, extra_have, args) < 0) { + for (ref = remote_refs; ref; ref = ref->next) + ref->status = REF_STATUS_NONE; return -1; + } } - else - close(out); if (expect_status_report) ret = receive_status(in, remote_refs); else ret = 0; - print_push_status(dest, remote_refs); - - if (!args.dry_run && remote) { - for (ref = remote_refs; ref; ref = ref->next) - update_tracking_ref(remote, ref); - } - - if (!refs_pushed(remote_refs)) - fprintf(stderr, "Everything up-to-date\n"); if (ret < 0) return ret; for (ref = remote_refs; ref; ref = ref->next) { @@ -574,11 +459,19 @@ static void verify_remote_names(int nr_heads, const char **heads) int cmd_send_pack(int argc, const char **argv, const char *prefix) { - int i, nr_heads = 0; - const char **heads = NULL; + int i, nr_refspecs = 0; + const char **refspecs = NULL; const char *remote_name = NULL; struct remote *remote = NULL; const char *dest = NULL; + int fd[2]; + struct child_process *conn; + struct extra_have_objects extra_have; + struct ref *remote_refs, **remote_tail, *local_refs; + int ret; + int send_all = 0; + const char *receivepack = "git-receive-pack"; + int flags; argv++; for (i = 1; i < argc; i++, argv++) { @@ -586,11 +479,11 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) if (*arg == '-') { if (!prefixcmp(arg, "--receive-pack=")) { - args.receivepack = arg + 15; + receivepack = arg + 15; continue; } if (!prefixcmp(arg, "--exec=")) { - args.receivepack = arg + 7; + receivepack = arg + 7; continue; } if (!prefixcmp(arg, "--remote=")) { @@ -598,7 +491,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--all")) { - args.send_all = 1; + send_all = 1; continue; } if (!strcmp(arg, "--dry-run")) { @@ -627,8 +520,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) dest = arg; continue; } - heads = (const char **) argv; - nr_heads = argc - i; + refspecs = (const char **) argv; + nr_refspecs = argc - i; break; } if (!dest) @@ -637,8 +530,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) * --all and --mirror are incompatible; neither makes sense * with any refspecs. */ - if ((heads && (args.send_all || args.send_mirror)) || - (args.send_all && args.send_mirror)) + if ((refspecs && (send_all || args.send_mirror)) || + (send_all && args.send_mirror)) usage(send_pack_usage); if (remote_name) { @@ -649,24 +542,50 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) } } - return send_pack(&args, dest, remote, nr_heads, heads); -} + conn = git_connect(fd, dest, receivepack, args.verbose ? CONNECT_VERBOSE : 0); -int send_pack(struct send_pack_args *my_args, - const char *dest, struct remote *remote, - int nr_heads, const char **heads) -{ - int fd[2], ret; - struct child_process *conn; + memset(&extra_have, 0, sizeof(extra_have)); + + get_remote_heads(fd[0], &remote_refs, 0, NULL, REF_NORMAL, + &extra_have); - memcpy(&args, my_args, sizeof(args)); + verify_remote_names(nr_refspecs, refspecs); - verify_remote_names(nr_heads, heads); + local_refs = get_local_heads(); - conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0); - ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads); + flags = MATCH_REFS_NONE; + + if (send_all) + flags |= MATCH_REFS_ALL; + if (args.send_mirror) + flags |= MATCH_REFS_MIRROR; + + /* match them up */ + remote_tail = &remote_refs; + while (*remote_tail) + remote_tail = &((*remote_tail)->next); + if (match_refs(local_refs, remote_refs, &remote_tail, + nr_refspecs, refspecs, flags)) { + return -1; + } + + ret = send_pack(&args, fd, conn, remote_refs, &extra_have); + + close(fd[1]); close(fd[0]); - /* do_send_pack always closes fd[1] */ + ret |= finish_connect(conn); - return !!ret; + + print_push_status(dest, remote_refs); + + if (!args.dry_run && remote) { + struct ref *ref; + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(remote, ref); + } + + if (!ret && !refs_pushed(remote_refs)) + fprintf(stderr, "Everything up-to-date\n"); + + return ret; } diff --git a/builtin-shortlog.c b/builtin-shortlog.c index badd912038..b28091b445 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -101,7 +101,6 @@ static void insert_one_record(struct shortlog *log, } while (*oneline && isspace(*oneline) && *oneline != '\n') oneline++; - len = eol - oneline; format_subject(&subject, oneline, " "); buffer = strbuf_detach(&subject, NULL); diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 306b850c72..828e6f86de 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -365,8 +365,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, return 0; } if (MAX_REVS <= ref_name_cnt) { - fprintf(stderr, "warning: ignoring %s; " - "cannot handle more than %d refs\n", + warning("ignoring %s; cannot handle more than %d refs", refname, MAX_REVS); return 0; } diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 572b114119..dc76c5090f 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -140,7 +140,7 @@ static int exclude_existing(const char *match) continue; } if (check_ref_format(ref)) { - fprintf(stderr, "warning: ref '%s' ignored\n", ref); + warning("ref '%s' ignored", ref); continue; } if (!string_list_has_string(&existing_refs, ref)) { diff --git a/builtin-update-index.c b/builtin-update-index.c index dd43d5bef4..1fde893cfa 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -195,7 +195,7 @@ static int process_path(const char *path) struct stat st; len = strlen(path); - if (has_symlink_leading_path(len, path)) + if (has_symlink_leading_path(path, len)) return error("'%s' is beyond a symbolic link", path); /* diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index a9b02fa32f..0206b416cb 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -35,7 +35,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) strcpy(buf, argv[1]); /* enter-repo smudges its argument */ if (!enter_repo(buf, 0)) - die("not a git archive"); + die("'%s' does not appear to be a git repository", buf); /* put received options in sent_argv[] */ sent_argc = 1; @@ -140,8 +140,8 @@ struct ondisk_cache_entry_extended { }; struct cache_entry { - unsigned int ce_ctime; - unsigned int ce_mtime; + struct cache_time ce_ctime; + struct cache_time ce_mtime; unsigned int ce_dev; unsigned int ce_ino; unsigned int ce_mode; @@ -282,7 +282,7 @@ struct index_state { struct cache_entry **cache; unsigned int cache_nr, cache_alloc, cache_changed; struct cache_tree *cache_tree; - time_t timestamp; + struct cache_time timestamp; void *alloc; unsigned name_hash_initialized : 1, initialized : 1; @@ -428,7 +428,7 @@ extern int read_index_preload(struct index_state *, const char **pathspec); extern int read_index_from(struct index_state *, const char *path); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); -extern int write_index(const struct index_state *, int newfd); +extern int write_index(struct index_state *, int newfd); extern int discard_index(struct index_state *); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); @@ -443,6 +443,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); extern int remove_index_entry_at(struct index_state *, int pos); +extern void remove_marked_cache_entries(struct index_state *istate); extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 #define ADD_CACHE_PRETEND 2 @@ -541,8 +542,17 @@ enum rebase_setup_type { AUTOREBASE_ALWAYS, }; +enum push_default_type { + PUSH_DEFAULT_UNSPECIFIED = -1, + PUSH_DEFAULT_NOTHING = 0, + PUSH_DEFAULT_MATCHING, + PUSH_DEFAULT_TRACKING, + PUSH_DEFAULT_CURRENT, +}; + extern enum branch_track git_branch_track; extern enum rebase_setup_type autorebase; +extern enum push_default_type push_default; #define GIT_REPO_VERSION 0 extern int repository_format_version; @@ -644,7 +654,7 @@ extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned l extern int move_temp_to_file(const char *tmpfile, const char *filename); -extern int has_sha1_pack(const unsigned char *sha1, const char **ignore); +extern int has_sha1_pack(const unsigned char *sha1); extern int has_sha1_file(const unsigned char *sha1); extern int has_loose_object_nonlocal(const unsigned char *sha1); @@ -724,11 +734,13 @@ 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 int has_symlink_leading_path(const char *name, int len); +extern int has_symlink_or_noent_leading_path(const char *name, int len); +extern int has_dirs_only_path(const char *name, int len, int prefix_len); +extern void invalidate_lstat_cache(const char *name, int len); extern void clear_lstat_cache(void); +extern void schedule_dir_for_removal(const char *name, int len); +extern void remove_scheduled_dirs(void); extern struct alternate_object_database { struct alternate_object_database *next; @@ -801,7 +813,7 @@ struct ref { #define REF_HEADS (1u << 1) #define REF_TAGS (1u << 2) -extern struct ref *find_ref_by_name(struct ref *list, const char *name); +extern struct ref *find_ref_by_name(const struct ref *list, const char *name); #define CONNECT_VERBOSE (1u << 0) extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); @@ -839,7 +851,6 @@ extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsign extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *); -extern int matches_pack_name(struct packed_git *p, const char *name); /* Dumb servers support */ extern int update_server_info(int); @@ -1,8 +1,6 @@ #include "cache.h" #include "color.h" -#define COLOR_RESET "\033[m" - int git_use_color_default = 0; static int parse_color(const char *name, int len) @@ -54,7 +52,7 @@ void color_parse_mem(const char *value, int value_len, const char *var, int bg = -2; if (!strncasecmp(value, "reset", len)) { - strcpy(dst, "\033[m"); + strcpy(dst, GIT_COLOR_RESET); return; } @@ -175,7 +173,7 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt, r += fprintf(fp, "%s", color); r += vfprintf(fp, fmt, args); if (*color) - r += fprintf(fp, "%s", COLOR_RESET); + r += fprintf(fp, "%s", GIT_COLOR_RESET); if (trail) r += fprintf(fp, "%s", trail); return r; @@ -217,7 +215,7 @@ int color_fwrite_lines(FILE *fp, const char *color, 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)) + fputs(GIT_COLOR_RESET, fp) < 0)) return -1; if (!p) return 0; @@ -4,6 +4,16 @@ /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ #define COLOR_MAXLEN 24 +#define GIT_COLOR_NORMAL "" +#define GIT_COLOR_RESET "\033[m" +#define GIT_COLOR_BOLD "\033[1m" +#define GIT_COLOR_RED "\033[31m" +#define GIT_COLOR_GREEN "\033[32m" +#define GIT_COLOR_YELLOW "\033[33m" +#define GIT_COLOR_BLUE "\033[34m" +#define GIT_COLOR_CYAN "\033[36m" +#define GIT_COLOR_BG_RED "\033[41m" + /* * This variable stores the value of color.ui */ diff --git a/combine-diff.c b/combine-diff.c index bccc018ab2..066ce841ed 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -526,7 +526,6 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent, return; /* result deleted */ while (1) { - struct sline *sl = &sline[lno]; unsigned long hunk_end; unsigned long rlines; const char *hunk_comment = NULL; @@ -592,7 +591,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent, struct lline *ll; int j; unsigned long p_mask; - sl = &sline[lno++]; + struct sline *sl = &sline[lno++]; ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head; while (ll) { fputs(c_old, stdout); @@ -713,9 +712,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, result_size = buf.len; result = strbuf_detach(&buf, NULL); elem->mode = canon_mode(st.st_mode); - } - else if (0 <= (fd = open(elem->path, O_RDONLY)) && - !fstat(fd, &st)) { + } else if (0 <= (fd = open(elem->path, O_RDONLY))) { size_t len = xsize_t(st.st_size); ssize_t done; int is_file, i; diff --git a/compat/mingw.c b/compat/mingw.c index 3dbe6a77ff..2839d9df6e 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -4,6 +4,119 @@ unsigned int _CRT_fmode = _O_BINARY; +static int err_win_to_posix(DWORD winerr) +{ + int error = ENOSYS; + switch(winerr) { + case ERROR_ACCESS_DENIED: error = EACCES; break; + case ERROR_ACCOUNT_DISABLED: error = EACCES; break; + case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; + case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; + case ERROR_ALREADY_EXISTS: error = EEXIST; break; + case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; + case ERROR_BAD_COMMAND: error = EIO; break; + case ERROR_BAD_DEVICE: error = ENODEV; break; + case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; + case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_LENGTH: error = EINVAL; break; + case ERROR_BAD_PATHNAME: error = ENOENT; break; + case ERROR_BAD_PIPE: error = EPIPE; break; + case ERROR_BAD_UNIT: error = ENODEV; break; + case ERROR_BAD_USERNAME: error = EINVAL; break; + case ERROR_BROKEN_PIPE: error = EPIPE; break; + case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; + case ERROR_BUSY: error = EBUSY; break; + case ERROR_BUSY_DRIVE: error = EBUSY; break; + case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; + case ERROR_CANNOT_MAKE: error = EACCES; break; + case ERROR_CANTOPEN: error = EIO; break; + case ERROR_CANTREAD: error = EIO; break; + case ERROR_CANTWRITE: error = EIO; break; + case ERROR_CRC: error = EIO; break; + case ERROR_CURRENT_DIRECTORY: error = EACCES; break; + case ERROR_DEVICE_IN_USE: error = EBUSY; break; + case ERROR_DEV_NOT_EXIST: error = ENODEV; break; + case ERROR_DIRECTORY: error = EINVAL; break; + case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; + case ERROR_DISK_CHANGE: error = EIO; break; + case ERROR_DISK_FULL: error = ENOSPC; break; + case ERROR_DRIVE_LOCKED: error = EBUSY; break; + case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; + case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; + case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; + case ERROR_FILE_EXISTS: error = EEXIST; break; + case ERROR_FILE_INVALID: error = ENODEV; break; + case ERROR_FILE_NOT_FOUND: error = ENOENT; break; + case ERROR_GEN_FAILURE: error = EIO; break; + case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; + case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; + case ERROR_INVALID_ACCESS: error = EACCES; break; + case ERROR_INVALID_ADDRESS: error = EFAULT; break; + case ERROR_INVALID_BLOCK: error = EFAULT; break; + case ERROR_INVALID_DATA: error = EINVAL; break; + case ERROR_INVALID_DRIVE: error = ENODEV; break; + case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; + case ERROR_INVALID_FLAGS: error = EINVAL; break; + case ERROR_INVALID_FUNCTION: error = ENOSYS; break; + case ERROR_INVALID_HANDLE: error = EBADF; break; + case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; + case ERROR_INVALID_NAME: error = EINVAL; break; + case ERROR_INVALID_OWNER: error = EINVAL; break; + case ERROR_INVALID_PARAMETER: error = EINVAL; break; + case ERROR_INVALID_PASSWORD: error = EPERM; break; + case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; + case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; + case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; + case ERROR_INVALID_WORKSTATION: error = EACCES; break; + case ERROR_IO_DEVICE: error = EIO; break; + case ERROR_IO_INCOMPLETE: error = EINTR; break; + case ERROR_LOCKED: error = EBUSY; break; + case ERROR_LOCK_VIOLATION: error = EACCES; break; + case ERROR_LOGON_FAILURE: error = EACCES; break; + case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; + case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; + case ERROR_MORE_DATA: error = EPIPE; break; + case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; + case ERROR_NOACCESS: error = EFAULT; break; + case ERROR_NONE_MAPPED: error = EINVAL; break; + case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; + case ERROR_NOT_READY: error = EAGAIN; break; + case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; + case ERROR_NO_DATA: error = EPIPE; break; + case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; + case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; + case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; + case ERROR_OPEN_FAILED: error = EIO; break; + case ERROR_OPEN_FILES: error = EBUSY; break; + case ERROR_OPERATION_ABORTED: error = EINTR; break; + case ERROR_OUTOFMEMORY: error = ENOMEM; break; + case ERROR_PASSWORD_EXPIRED: error = EACCES; break; + case ERROR_PATH_BUSY: error = EBUSY; break; + case ERROR_PATH_NOT_FOUND: error = ENOENT; break; + case ERROR_PIPE_BUSY: error = EBUSY; break; + case ERROR_PIPE_CONNECTED: error = EPIPE; break; + case ERROR_PIPE_LISTENING: error = EPIPE; break; + case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; + case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; + case ERROR_READ_FAULT: error = EIO; break; + case ERROR_SEEK: error = EIO; break; + case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; + case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; + case ERROR_SHARING_VIOLATION: error = EACCES; break; + case ERROR_STACK_OVERFLOW: error = ENOMEM; break; + case ERROR_SWAPERROR: error = ENOENT; break; + case ERROR_TOO_MANY_MODULES: error = EMFILE; break; + case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; + case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; + case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; + case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; + case ERROR_WRITE_FAULT: error = EIO; break; + case ERROR_WRITE_PROTECT: error = EROFS; break; + } + return error; +} + #undef open int mingw_open (const char *filename, int oflags, ...) { @@ -46,7 +159,8 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); - buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_size = fdata.nFileSizeLow | + (((off_t)fdata.nFileSizeHigh)<<32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); @@ -101,7 +215,7 @@ int mingw_fstat(int fd, struct stat *buf) } /* direct non-file handles to MS's fstat() */ if (GetFileType(fh) != FILE_TYPE_DISK) - return fstat(fd, buf); + return _fstati64(fd, buf); if (GetFileInformationByHandle(fh, &fdata)) { buf->st_ino = 0; @@ -109,7 +223,8 @@ int mingw_fstat(int fd, struct stat *buf) buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); - buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_size = fdata.nFileSizeLow | + (((off_t)fdata.nFileSizeHigh)<<32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); @@ -342,7 +457,7 @@ static const char *quote_arg(const char *arg) const char *p = arg; if (!*p) force_quotes = 1; while (*p) { - if (isspace(*p) || *p == '*' || *p == '?' || *p == '{') + if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'') force_quotes = 1; else if (*p == '"') n++; @@ -1003,3 +1118,24 @@ void mingw_open_html(const char *unixpath) printf("Launching default browser to display HTML ...\n"); ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0); } + +int link(const char *oldpath, const char *newpath) +{ + typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES); + static T create_hard_link = NULL; + if (!create_hard_link) { + create_hard_link = (T) GetProcAddress( + GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); + if (!create_hard_link) + create_hard_link = (T)-1; + } + if (create_hard_link == (T)-1) { + errno = ENOSYS; + return -1; + } + if (!create_hard_link(newpath, oldpath, NULL)) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + return 0; +} diff --git a/compat/mingw.h b/compat/mingw.h index a255898801..762eb143a7 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -67,8 +67,6 @@ static inline int readlink(const char *path, char *buf, size_t bufsiz) { errno = ENOSYS; return -1; } static inline int symlink(const char *oldpath, const char *newpath) { errno = ENOSYS; return -1; } -static inline int link(const char *oldpath, const char *newpath) -{ errno = ENOSYS; return -1; } static inline int fchmod(int fildes, mode_t mode) { errno = ENOSYS; return -1; } static inline int fork(void) @@ -134,6 +132,7 @@ int getpagesize(void); /* defined in MinGW's libgcc.a */ struct passwd *getpwuid(int uid); int setitimer(int type, struct itimerval *in, struct itimerval *out); int sigaction(int sig, struct sigaction *in, struct sigaction *out); +int link(const char *oldpath, const char *newpath); /* * replacements of existing functions @@ -160,14 +159,22 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz); int mingw_rename(const char*, const char*); #define rename mingw_rename +#ifdef USE_WIN32_MMAP +int mingw_getpagesize(void); +#define getpagesize mingw_getpagesize +#endif + /* Use mingw_lstat() instead of lstat()/stat() and * mingw_fstat() instead of fstat() on Windows. */ +#define off_t off64_t +#define stat _stati64 +#define lseek _lseeki64 int mingw_lstat(const char *file_name, struct stat *buf); int mingw_fstat(int fd, struct stat *buf); #define fstat mingw_fstat #define lstat mingw_lstat -#define stat(x,y) mingw_lstat(x,y) +#define _stati64(x,y) mingw_lstat(x,y) int mingw_utime(const char *file_name, const struct utimbuf *times); #define utime mingw_utime diff --git a/compat/win32mmap.c b/compat/win32mmap.c new file mode 100644 index 0000000000..779d796cd5 --- /dev/null +++ b/compat/win32mmap.c @@ -0,0 +1,53 @@ +#include "../git-compat-util.h" + +/* + * Note that this doesn't return the actual pagesize, but + * the allocation granularity. If future Windows specific git code + * needs the real getpagesize function, we need to find another solution. + */ +int mingw_getpagesize(void) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwAllocationGranularity; +} + +void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + HANDLE hmap; + void *temp; + size_t len; + struct stat st; + uint64_t o = offset; + uint32_t l = o & 0xFFFFFFFF; + uint32_t h = (o >> 32) & 0xFFFFFFFF; + + if (!fstat(fd, &st)) + len = xsize_t(st.st_size); + else + die("mmap: could not determine filesize"); + + if ((length + offset) > len) + length = len - offset; + + if (!(flags & MAP_PRIVATE)) + die("Invalid usage of mmap when built with USE_WIN32_MMAP"); + + hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_WRITECOPY, + 0, 0, 0); + + if (!hmap) + return MAP_FAILED; + + temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start); + + if (!CloseHandle(hmap)) + warning("unable to close file mapping handle\n"); + + return temp ? temp : MAP_FAILED; +} + +int git_munmap(void *start, size_t length) +{ + return !UnmapViewOfFile(start); +} diff --git a/compat/winansi.c b/compat/winansi.c index e2d96dfe6f..44dc293ad3 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -18,8 +18,6 @@ This file is git-specific. Therefore, this file does not attempt to implement any codes that are not used by git. - - TODO: K */ static HANDLE console; @@ -79,6 +77,20 @@ static void set_console_attr(void) SetConsoleTextAttribute(console, attributes); } +static void erase_in_line(void) +{ + CONSOLE_SCREEN_BUFFER_INFO sbi; + + if (!console) + return; + + GetConsoleScreenBufferInfo(console, &sbi); + FillConsoleOutputCharacterA(console, ' ', + sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, + NULL); +} + + static const char *set_attr(const char *str) { const char *func; @@ -218,7 +230,7 @@ static const char *set_attr(const char *str) set_console_attr(); break; case 'K': - /* TODO */ + erase_in_line(); break; default: /* Unsupported code */ @@ -565,6 +565,31 @@ static int git_default_branch_config(const char *var, const char *value) return 0; } +static int git_default_push_config(const char *var, const char *value) +{ + if (!strcmp(var, "push.default")) { + if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "nothing")) + push_default = PUSH_DEFAULT_NOTHING; + else if (!strcmp(value, "matching")) + push_default = PUSH_DEFAULT_MATCHING; + else if (!strcmp(value, "tracking")) + push_default = PUSH_DEFAULT_TRACKING; + else if (!strcmp(value, "current")) + push_default = PUSH_DEFAULT_CURRENT; + else { + error("Malformed value for %s: %s", var, value); + return error("Must be one of nothing, matching, " + "tracking or current."); + } + return 0; + } + + /* Add other config variables here and to Documentation/config.txt. */ + return 0; +} + static int git_default_mailmap_config(const char *var, const char *value) { if (!strcmp(var, "mailmap.file")) @@ -588,6 +613,9 @@ int git_default_config(const char *var, const char *value, void *dummy) if (!prefixcmp(var, "branch.")) return git_default_branch_config(var, value); + if (!prefixcmp(var, "push.")) + return git_default_push_config(var, value); + if (!prefixcmp(var, "mailmap.")) return git_default_mailmap_config(var, value); @@ -644,28 +672,37 @@ int git_config_global(void) int git_config(config_fn_t fn, void *data) { - int ret = 0; + int ret = 0, found = 0; char *repo_config = NULL; const char *home = NULL; /* Setting $GIT_CONFIG makes git read _only_ the given config file. */ if (config_exclusive_filename) return git_config_from_file(fn, config_exclusive_filename, data); - if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), data); + found += 1; + } home = getenv("HOME"); if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - if (!access(user_config, R_OK)) + if (!access(user_config, R_OK)) { ret += git_config_from_file(fn, user_config, data); + found += 1; + } free(user_config); } repo_config = git_pathdup("config"); - ret += git_config_from_file(fn, repo_config, data); + if (!access(repo_config, R_OK)) { + ret += git_config_from_file(fn, repo_config, data); + found += 1; + } free(repo_config); + if (found == 0) + return -1; return ret; } diff --git a/configure.ac b/configure.ac index 082a03d3cf..4e728bca35 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,8 @@ else \ if test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide path for --with-$1=PATH]); \ else \ + m4_toupper($1)_PATH=$withval; \ + AC_MSG_NOTICE([Setting m4_toupper($1)_PATH to $withval]); \ GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \ fi; \ fi; \ @@ -61,6 +63,8 @@ elif test "$withval" = "yes"; then \ m4_toupper(NO_$1)=; \ else \ m4_toupper(NO_$1)=; \ + m4_toupper($1)DIR=$withval; \ + AC_MSG_NOTICE([Setting m4_toupper($1)DIR to $withval]); \ GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \ fi \ ])# GIT_PARSE_WITH @@ -76,6 +80,32 @@ AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[ AC_SEARCH_LIBS([$1],, [$2],[$3]) ],[$3])]) + +dnl +dnl GIT_STASH_FLAGS(BASEPATH_VAR) +dnl ----------------------------- +dnl Allow for easy stashing of LDFLAGS and CPPFLAGS before running +dnl tests that may want to take user settings into account. +AC_DEFUN([GIT_STASH_FLAGS],[ +if test -n "$1"; then + old_CPPFLAGS="$CPPFLAGS" + old_LDFLAGS="$LDFLAGS" + CPPFLAGS="-I$1/include $CPPFLAGS" + LDFLAGS="-L$1/$lib $LDFLAGS" +fi +]) + +dnl +dnl GIT_UNSTASH_FLAGS(BASEPATH_VAR) +dnl ----------------------------- +dnl Restore the stashed *FLAGS values. +AC_DEFUN([GIT_UNSTASH_FLAGS],[ +if test -n "$1"; then + CPPFLAGS="$old_CPPFLAGS" + LDFLAGS="$old_LDFLAGS" +fi +]) + ## Site configuration related to programs (before tests) ## --with-PACKAGE[=ARG] and --without-PACKAGE # @@ -86,9 +116,124 @@ AC_ARG_WITH([lib], [if test "$withval" = "no" || test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ else \ + lib=$withval; \ + AC_MSG_NOTICE([Setting lib to '$lib']); \ GIT_CONF_APPEND_LINE(lib=$withval); \ fi; \ ],[]) + +if test -z "$lib"; then + AC_MSG_NOTICE([Setting lib to 'lib' (the default)]) + lib=lib +fi + +AC_ARG_ENABLE([pthreads], + [AS_HELP_STRING([--enable-pthreads=FLAGS], + [FLAGS is the value to pass to the compiler to enable POSIX Threads.] + [The default if FLAGS is not specified is to try first -pthread] + [and then -lpthread.] + [--without-pthreads will disable threading.])], +[ +if test "x$enableval" = "xyes"; then + AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads]) +elif test "x$enableval" != "xno"; then + PTHREAD_CFLAGS=$enableval + AC_MSG_NOTICE([Setting '$PTHREAD_CFLAGS' as the FLAGS to enable POSIX Threads]) +else + AC_MSG_NOTICE([POSIX Threads will be disabled.]) + NO_PTHREADS=YesPlease + USER_NOPTHREAD=1 +fi], +[ + AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads.]) +]) + +## Site configuration (override autodetection) +## --with-PACKAGE[=ARG] and --without-PACKAGE +AC_MSG_NOTICE([CHECKS for site configuration]) +# +# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability +# tests. These tests take up a significant amount of the total test time +# but are not needed unless you plan to talk to SVN repos. +# +# Define MOZILLA_SHA1 environment variable when running make to make use of +# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast +# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default +# choice) has very fast version optimized for i586. +# +# Define PPC_SHA1 environment variable when running make to make use of +# a bundled SHA1 routine optimized for PowerPC. +# +# Define ARM_SHA1 environment variable when running make to make use of +# a bundled SHA1 routine optimized for ARM. +# +# Define NO_OPENSSL environment variable if you do not have OpenSSL. +# This also implies MOZILLA_SHA1. +# +# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +AC_ARG_WITH(openssl, +AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)]) +AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]),\ +GIT_PARSE_WITH(openssl)) +# +# Define NO_CURL if you do not have curl installed. git-http-pull and +# git-http-push are not built, and you cannot use http:// and https:// +# transports. +# +# Define CURLDIR=/foo/bar if your curl header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +AC_ARG_WITH(curl, +AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)]) +AS_HELP_STRING([], [ARG can be also prefix for curl library and headers]), +GIT_PARSE_WITH(curl)) +# +# 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. +AC_ARG_WITH(expat, +AS_HELP_STRING([--with-expat], +[support git-push using http:// and https:// transports via WebDAV (default is YES)]) +AS_HELP_STRING([], [ARG can be also prefix for expat library and headers]), +GIT_PARSE_WITH(expat)) +# +# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink +# installed in /sw, but don't want GIT to link against any libraries +# installed there. If defined you may specify your own (or Fink's) +# include directories and library directories by defining CFLAGS +# and LDFLAGS appropriately. +# +# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, +# have DarwinPorts installed in /opt/local, but don't want GIT to +# link against any libraries installed there. If defined you may +# specify your own (or DarwinPort's) include directories and +# library directories by defining CFLAGS and LDFLAGS appropriately. +# +# Define NO_MMAP if you want to avoid mmap. +# +# Define NO_ICONV if your libc does not properly support iconv. +AC_ARG_WITH(iconv, +AS_HELP_STRING([--without-iconv], +[if your architecture doesn't properly support iconv]) +AS_HELP_STRING([--with-iconv=PATH], +[PATH is prefix for libiconv library and headers]) +AS_HELP_STRING([], +[used only if you need linking with libiconv]), +GIT_PARSE_WITH(iconv)) + +## --enable-FEATURE[=ARG] and --disable-FEATURE +# +# Define USE_NSEC below if you want git to care about sub-second file mtimes +# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and +# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely +# randomly break unless your underlying filesystem supports those sub-second +# times (my ext3 doesn't). +# +# Define USE_STDEV below if you want git to care about the underlying device +# change being considered an inode change from the update-index perspective. + # # Define SHELL_PATH to provide path to shell. GIT_ARG_SET_PATH(shell) @@ -167,7 +312,7 @@ fi AC_CHECK_PROGS(ASCIIDOC, [asciidoc]) if test -n "$ASCIIDOC"; then AC_MSG_CHECKING([for asciidoc version]) - asciidoc_version=`$ASCIIDOC --version 2>&1` + asciidoc_version=`$ASCIIDOC --version 2>/dev/null` case "${asciidoc_version}" in asciidoc' '8*) ASCIIDOC8=YesPlease @@ -191,33 +336,57 @@ AC_MSG_NOTICE([CHECKS for libraries]) # # Define NO_OPENSSL environment variable if you do not have OpenSSL. # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin). + +GIT_STASH_FLAGS($OPENSSLDIR) + AC_CHECK_LIB([crypto], [SHA1_Init], [NEEDS_SSL_WITH_CRYPTO=], [AC_CHECK_LIB([ssl], [SHA1_Init], [NEEDS_SSL_WITH_CRYPTO=YesPlease NEEDS_SSL_WITH_CRYPTO=], [NO_OPENSSL=YesPlease])]) + +GIT_UNSTASH_FLAGS($OPENSSLDIR) + AC_SUBST(NEEDS_SSL_WITH_CRYPTO) AC_SUBST(NO_OPENSSL) + # # Define NO_CURL if you do not have libcurl installed. git-http-pull and # git-http-push are not built, and you cannot use http:// and https:// # transports. + +GIT_STASH_FLAGS($CURLDIR) + AC_CHECK_LIB([curl], [curl_global_init], [NO_CURL=], [NO_CURL=YesPlease]) + +GIT_UNSTASH_FLAGS($CURLDIR) + AC_SUBST(NO_CURL) + # # 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. + +GIT_STASH_FLAGS($EXPATDIR) + AC_CHECK_LIB([expat], [XML_ParserCreate], [NO_EXPAT=], [NO_EXPAT=YesPlease]) + +GIT_UNSTASH_FLAGS($EXPATDIR) + AC_SUBST(NO_EXPAT) + # # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin and # some Solaris installations). # Define NO_ICONV if neither libc nor libiconv support iconv. + +GIT_STASH_FLAGS($ICONVDIR) + AC_DEFUN([ICONVTEST_SRC], [ #include <iconv.h> @@ -227,25 +396,46 @@ int main(void) return 0; } ]) -AC_MSG_CHECKING([for iconv in -lc]) -AC_LINK_IFELSE(ICONVTEST_SRC, + +if test -n "$ICONVDIR"; then + lib_order="-liconv -lc" +else + lib_order="-lc -liconv" +fi + +NO_ICONV=YesPlease + +for l in $lib_order; do + if test "$l" = "-liconv"; then + NEEDS_LIBICONV=YesPlease + else + NEEDS_LIBICONV= + fi + + old_LIBS="$LIBS" + LIBS="$LIBS $l" + AC_MSG_CHECKING([for iconv in $l]) + AC_LINK_IFELSE(ICONVTEST_SRC, [AC_MSG_RESULT([yes]) - NEEDS_LIBICONV=], - [AC_MSG_RESULT([no]) - old_LIBS="$LIBS" - LIBS="$LIBS -liconv" - AC_MSG_CHECKING([for iconv in -liconv]) - AC_LINK_IFELSE(ICONVTEST_SRC, - [AC_MSG_RESULT([yes]) - NEEDS_LIBICONV=YesPlease], - [AC_MSG_RESULT([no]) - NO_ICONV=YesPlease]) - LIBS="$old_LIBS"]) + NO_ICONV= + break], + [AC_MSG_RESULT([no])]) + LIBS="$old_LIBS" +done + +#in case of break +LIBS="$old_LIBS" + +GIT_UNSTASH_FLAGS($ICONVDIR) + AC_SUBST(NEEDS_LIBICONV) AC_SUBST(NO_ICONV) -test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv" + # # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib. + +GIT_STASH_FLAGS($ZLIB_PATH) + AC_DEFUN([ZLIBTEST_SRC], [ #include <zlib.h> @@ -263,7 +453,11 @@ AC_LINK_IFELSE(ZLIBTEST_SRC, [AC_MSG_RESULT([no]) NO_DEFLATE_BOUND=yes]) LIBS="$old_LIBS" + +GIT_UNSTASH_FLAGS($ZLIB_PATH) + AC_SUBST(NO_DEFLATE_BOUND) + # # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). @@ -297,13 +491,18 @@ int main(void) return 0; } ]]) + +GIT_STASH_FLAGS($ICONVDIR) + AC_MSG_CHECKING([for old iconv()]) AC_COMPILE_IFELSE(OLDICONVTEST_SRC, [AC_MSG_RESULT([no])], [AC_MSG_RESULT([yes]) OLD_ICONV=UnfortunatelyYes]) -AC_SUBST(OLD_ICONV) +GIT_UNSTASH_FLAGS($ICONVDIR) + +AC_SUBST(OLD_ICONV) ## Checks for typedefs, structures, and compiler characteristics. AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics]) @@ -494,114 +693,65 @@ AC_SUBST(NO_MKDTEMP) # # Define PTHREAD_LIBS to the linker flag used for Pthread support and define # THREADED_DELTA_SEARCH if Pthreads are available. -AC_LANG_CONFTEST([AC_LANG_PROGRAM( - [[#include <pthread.h>]], - [[pthread_mutex_t test_mutex;]] -)]) -${CC} -pthread conftest.c -o conftest.o > /dev/null 2>&1 -if test $? -eq 0;then - PTHREAD_LIBS="-pthread" - THREADED_DELTA_SEARCH=YesPlease +AC_DEFUN([PTHREADTEST_SRC], [ +#include <pthread.h> + +int main(void) +{ + pthread_mutex_t test_mutex; + return (0); +} +]) + +dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM( +dnl [[#include <pthread.h>]], +dnl [[pthread_mutex_t test_mutex;]] +dnl )]) + +NO_PTHREADS=UnfortunatelyYes +THREADED_DELTA_SEARCH= +PTHREAD_LIBS= + +if test -n "$USER_NOPTHREAD"; then + AC_MSG_NOTICE([Skipping POSIX Threads at user request.]) +# handle these separately since PTHREAD_CFLAGS could be '-lpthreads +# -D_REENTRANT' or some such. +elif test -z "$PTHREAD_CFLAGS"; then + for opt in -pthread -lpthread; do + old_CFLAGS="$CFLAGS" + CFLAGS="$opt $CFLAGS" + AC_MSG_CHECKING([Checking for POSIX Threads with '$opt']) + AC_LINK_IFELSE(PTHREADTEST_SRC, + [AC_MSG_RESULT([yes]) + NO_PTHREADS= + PTHREAD_LIBS="$opt" + THREADED_DELTA_SEARCH=YesPlease + break + ], + [AC_MSG_RESULT([no])]) + CFLAGS="$old_CFLAGS" + done else - ${CC} -lpthread conftest.c -o conftest.o > /dev/null 2>&1 - if test $? -eq 0;then - PTHREAD_LIBS="-lpthread" - THREADED_DELTA_SEARCH=YesPlease - else - NO_PTHREADS=UnfortunatelyYes - fi + old_CFLAGS="$CFLAGS" + CFLAGS="$PTHREAD_CFLAGS $CFLAGS" + AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS']) + AC_LINK_IFELSE(PTHREADTEST_SRC, + [AC_MSG_RESULT([yes]) + NO_PTHREADS= + PTHREAD_LIBS="$PTHREAD_CFLAGS" + THREADED_DELTA_SEARCH=YesPlease + ], + [AC_MSG_RESULT([no])]) + + CFLAGS="$old_CFLAGS" fi + +CFLAGS="$old_CFLAGS" + AC_SUBST(PTHREAD_LIBS) AC_SUBST(NO_PTHREADS) AC_SUBST(THREADED_DELTA_SEARCH) -## Site configuration (override autodetection) -## --with-PACKAGE[=ARG] and --without-PACKAGE -AC_MSG_NOTICE([CHECKS for site configuration]) -# -# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability -# tests. These tests take up a significant amount of the total test time -# but are not needed unless you plan to talk to SVN repos. -# -# Define MOZILLA_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast -# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default -# choice) has very fast version optimized for i586. -# -# Define PPC_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine optimized for PowerPC. -# -# Define ARM_SHA1 environment variable when running make to make use of -# a bundled SHA1 routine optimized for ARM. -# -# Define NO_OPENSSL environment variable if you do not have OpenSSL. -# This also implies MOZILLA_SHA1. -# -# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -AC_ARG_WITH(openssl, -AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)]) -AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]),\ -GIT_PARSE_WITH(openssl)) -# -# Define NO_CURL if you do not have curl installed. git-http-pull and -# git-http-push are not built, and you cannot use http:// and https:// -# transports. -# -# Define CURLDIR=/foo/bar if your curl header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -AC_ARG_WITH(curl, -AS_HELP_STRING([--with-curl],[support http(s):// transports (default is YES)]) -AS_HELP_STRING([], [ARG can be also prefix for curl library and headers]), -GIT_PARSE_WITH(curl)) -# -# 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. -AC_ARG_WITH(expat, -AS_HELP_STRING([--with-expat], -[support git-push using http:// and https:// transports via WebDAV (default is YES)]) -AS_HELP_STRING([], [ARG can be also prefix for expat library and headers]), -GIT_PARSE_WITH(expat)) -# -# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink -# installed in /sw, but don't want GIT to link against any libraries -# installed there. If defined you may specify your own (or Fink's) -# include directories and library directories by defining CFLAGS -# and LDFLAGS appropriately. -# -# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, -# have DarwinPorts installed in /opt/local, but don't want GIT to -# link against any libraries installed there. If defined you may -# specify your own (or DarwinPort's) include directories and -# library directories by defining CFLAGS and LDFLAGS appropriately. -# -# Define NO_MMAP if you want to avoid mmap. -# -# Define NO_ICONV if your libc does not properly support iconv. -AC_ARG_WITH(iconv, -AS_HELP_STRING([--without-iconv], -[if your architecture doesn't properly support iconv]) -AS_HELP_STRING([--with-iconv=PATH], -[PATH is prefix for libiconv library and headers]) -AS_HELP_STRING([], -[used only if you need linking with libiconv]), -GIT_PARSE_WITH(iconv)) - -## --enable-FEATURE[=ARG] and --disable-FEATURE -# -# Define USE_NSEC below if you want git to care about sub-second file mtimes -# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and -# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely -# randomly break unless your underlying filesystem supports those sub-second -# times (my ext3 doesn't). -# -# Define USE_STDEV below if you want git to care about the underlying device -# change being considered an inode change from the update-index perspective. - - ## Output files AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"]) AC_OUTPUT @@ -373,8 +373,6 @@ static void git_tcp_connect(int fd[2], char *host, int flags) static char *git_proxy_command; -static const char *rhost_name; -static int rhost_len; static int git_proxy_command_options(const char *var, const char *value, void *cb) @@ -383,6 +381,8 @@ static int git_proxy_command_options(const char *var, const char *value, const char *for_pos; int matchlen = -1; int hostlen; + const char *rhost_name = cb; + int rhost_len = strlen(rhost_name); if (git_proxy_command) return 0; @@ -426,11 +426,8 @@ static int git_proxy_command_options(const char *var, const char *value, static int git_use_proxy(const char *host) { - rhost_name = host; - rhost_len = strlen(host); git_proxy_command = getenv("GIT_PROXY_COMMAND"); - git_config(git_proxy_command_options, NULL); - rhost_name = NULL; + git_config(git_proxy_command_options, (void*)host); return (git_proxy_command && *git_proxy_command); } @@ -507,7 +504,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { char *url = xstrdup(url_orig); - char *host, *path = url; + char *host, *path; char *end; int c; struct child_process *conn; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8431837f97..e72ce2428d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -62,7 +62,7 @@ esac __gitdir () { if [ -z "${1-}" ]; then - if [ -n "$__git_dir" ]; then + if [ -n "${__git_dir-}" ]; then echo "$__git_dir" elif [ -d .git ]; then echo .git @@ -80,68 +80,72 @@ __gitdir () # returns text to add to bash PS1 prompt (includes branch name) __git_ps1 () { - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ] - then - if test -f "$g/rebase-apply/rebasing" - then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then r="|REBASE" - elif test -f "$g/rebase-apply/applying" - then + elif [ -f "$g/rebase-apply/applying" ]; then r="|AM" else r="|AM/REBASE" fi b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ] - then + elif [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" - elif [ -d "$g/rebase-merge" ] - then + elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ] - then + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" b="$(git symbolic-ref HEAD 2>/dev/null)" else - if [ -f "$g/BISECT_LOG" ] - then + if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)" - then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)" - then - b="$(cut -c1-7 "$g/HEAD")..." + if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then + if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then + if [ -r "$g/HEAD" ]; then + b="$(cut -c1-7 "$g/HEAD")..." + fi fi fi fi local w local i + local c - 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="#" + if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + else + b="GIT_DIR!" + fi + elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(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 fi - if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$w$i$r" - else - printf " (%s)" "${b##refs/heads/}$w$i$r" + if [ -n "$b" ]; then + if [ -n "${1-}" ]; then + printf "$1" "$c${b##refs/heads/}$w$i$r" + else + printf " (%s)" "$c${b##refs/heads/}$w$i$r" + fi fi fi } @@ -299,7 +303,7 @@ __git_remotes () __git_merge_strategies () { - if [ -n "$__git_merge_strategylist" ]; then + if [ -n "${__git_merge_strategylist-}" ]; then echo "$__git_merge_strategylist" return fi @@ -383,9 +387,88 @@ __git_complete_revlist () esac } +__git_complete_remote_or_refspec () +{ + local cmd="${COMP_WORDS[1]}" + local cur="${COMP_WORDS[COMP_CWORD]}" + local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; + -*) ;; + *) remote="$i"; break ;; + esac + c=$((++c)) + done + if [ -z "$remote" ]; then + __gitcomp "$(__git_remotes)" + return + fi + if [ $no_complete_refspec = 1 ]; then + COMPREPLY=() + return + fi + [ "$remote" = "." ] && remote= + case "$cur" in + *:*) + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + cur="${cur#*:}" + lhs=0 + ;; + +*) + pfx="+" + cur="${cur#+}" + ;; + esac + case "$cmd" in + fetch) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + pull) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + push) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs)" "$pfx" "$cur" + else + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + fi + ;; + esac +} + +__git_complete_strategy () +{ + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + __gitcomp "$(__git_merge_strategies)" + return 0 + esac + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --strategy=*) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" + return 0 + ;; + esac + return 1 +} + __git_all_commands () { - if [ -n "$__git_all_commandlist" ]; then + if [ -n "${__git_all_commandlist-}" ]; then echo "$__git_all_commandlist" return fi @@ -403,7 +486,7 @@ __git_all_commandlist="$(__git_all_commands 2>/dev/null)" __git_porcelain_commands () { - if [ -n "$__git_porcelain_commandlist" ]; then + if [ -n "${__git_porcelain_commandlist-}" ]; then echo "$__git_porcelain_commandlist" return fi @@ -816,7 +899,7 @@ _git_diff () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--cached --pickaxe-all --pickaxe-regex + __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex --base --ours --theirs $__git_diff_common_options " @@ -826,42 +909,42 @@ _git_diff () __git_complete_file } +__git_fetch_options=" + --quiet --verbose --append --upload-pack --force --keep --depth= + --tags --no-tags +" + _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" - ;; - *) - __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" - ;; - esac - fi + case "$cur" in + --*) + __gitcomp "$__git_fetch_options" + return + ;; + esac + __git_complete_remote_or_refspec } _git_format_patch () { local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + --thread=*) + __gitcomp " + deep shallow + " "" "${cur##--thread=}" + return + ;; --*) __gitcomp " - --stdout --attach --thread + --stdout --attach --no-attach --thread --thread= --output-directory --numbered --start-number --numbered-files --keep-subject --signoff - --in-reply-to= + --in-reply-to= --cc= --full-index --binary --not --all --cover-letter @@ -875,6 +958,21 @@ _git_format_patch () __git_complete_revlist } +_git_fsck () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --tags --root --unreachable --cache --no-reflogs --full + --strict --verbose --lost-found + " + return + ;; + esac + COMPREPLY=() +} + _git_gc () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1014,6 +1112,11 @@ _git_log () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --date=*) __gitcomp " relative iso8601 rfc2822 short local default @@ -1029,7 +1132,7 @@ _git_log () --follow --abbrev-commit --abbrev= --relative-date --date= - --pretty= + --pretty= --format= --oneline --cherry-pick --graph --decorate @@ -1045,24 +1148,19 @@ _git_log () __git_complete_revlist } +__git_merge_options=" + --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff +" + _git_merge () { + __git_complete_strategy && return + local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) - __gitcomp " - --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff - " + __gitcomp "$__git_merge_options" return esac __gitcomp "$(__git_refs)" @@ -1111,40 +1209,44 @@ _git_name_rev () _git_pull () { - local cur="${COMP_WORDS[COMP_CWORD]}" + __git_complete_strategy && return - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" - fi + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --rebase --no-rebase + $__git_merge_options + $__git_fetch_options + " + return + ;; + esac + __git_complete_remote_or_refspec } _git_push () { local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then + case "${COMP_WORDS[COMP_CWORD-1]}" in + --repo) __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" - ;; - +*) - __gitcomp "$(__git_refs)" + "${cur#+}" - ;; - *) - __gitcomp "$(__git_refs)" - ;; - esac - fi + return + esac + case "$cur" in + --repo=*) + __gitcomp "$(__git_remotes)" "" "${cur##--repo=}" + return + ;; + --*) + __gitcomp " + --all --mirror --tags --dry-run --force --verbose + --receive-pack= --repo= + " + return + ;; + esac + __git_complete_remote_or_refspec } _git_rebase () @@ -1154,16 +1256,8 @@ _git_rebase () __gitcomp "--continue --skip --abort" return fi - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac + __git_complete_strategy && return case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) __gitcomp "--onto --merge --strategy --interactive" return @@ -1176,8 +1270,8 @@ _git_send_email () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose - --dry-run --envelope-sender --from --identity + __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to + --compose --dry-run --envelope-sender --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server @@ -1443,7 +1537,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm show prune update" + local subcommands="add rename rm show prune update set-head" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -1541,8 +1635,13 @@ _git_show () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --*) - __gitcomp "--pretty= + __gitcomp "--pretty= --format= $__git_diff_common_options " return @@ -1802,6 +1901,7 @@ _git () diff) _git_diff ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; + fsck) _git_fsck ;; gc) _git_gc ;; grep) _git_grep ;; help) _git_help ;; @@ -1841,7 +1941,7 @@ _gitk () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" local merge="" if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 0cda3d2eea..0deda3a0e4 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -4,7 +4,7 @@ # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible # git-difftool-helper script. This script exports # GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper. # Any arguments that are unknown to this script are forwarded to 'git diff'. use strict; @@ -49,12 +49,12 @@ sub generate_command } if ($arg eq '-t' or $arg eq '--tool') { usage() if $#ARGV <= $idx; - $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; + $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; $skip_next = 1; next; } if ($arg =~ /^--tool=/) { - $ENV{GIT_MERGE_TOOL} = substr($arg, 7); + $ENV{GIT_DIFF_TOOL} = substr($arg, 7); next; } if ($arg eq '--no-prompt') { diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index db3af6a833..9c0a13452a 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -128,8 +128,10 @@ launch_merge_tool () { cleanup_temp_files } -# Verifies that mergetool.<tool>.cmd exists +# Verifies that (difftool|mergetool).<tool>.cmd exists valid_custom_tool() { + merge_tool_cmd="$(git config difftool.$1.cmd)" + test -z "$merge_tool_cmd" && merge_tool_cmd="$(git config mergetool.$1.cmd)" test -n "$merge_tool_cmd" } @@ -150,8 +152,11 @@ valid_tool() { } # Sets up the merge_tool_path variable. -# This handles the mergetool.<tool>.path configuration. +# This handles the difftool.<tool>.path configuration. +# This also falls back to mergetool defaults. init_merge_tool_path() { + merge_tool_path=$(git config difftool."$1".path) + test -z "$merge_tool_path" && merge_tool_path=$(git config mergetool."$1".path) if test -z "$merge_tool_path"; then case "$1" in @@ -165,15 +170,19 @@ init_merge_tool_path() { fi } -# Allow the GIT_MERGE_TOOL variable to provide a default value +# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" +test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL" -# If not merge tool was specified then use the merge.tool +# If merge tool was not specified then use the diff.tool # configuration variable. If that's invalid then reset merge_tool. +# Fallback to merge.tool. if test -z "$merge_tool"; then + merge_tool=$(git config diff.tool) + test -z "$merge_tool" && merge_tool=$(git config merge.tool) if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then - echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "git config option diff.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default..." unset merge_tool fi diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 6e2610cda6..2b7bc03ec3 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -32,23 +32,23 @@ OPTIONS vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' -will use the configuration variable `merge.tool`. If the -configuration variable `merge.tool` is not set, 'git difftool' +will use the configuration variable `diff.tool`. If the +configuration variable `diff.tool` is not set, 'git-difftool' will pick a suitable default. + You can explicitly provide a full path to the tool by setting the -configuration variable `mergetool.<tool>.path`. For example, you +configuration variable `difftool.<tool>.path`. For example, you can configure the absolute path to kdiff3 by setting -`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the +`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + Instead of running one of the known merge tool programs, 'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration -variable `mergetool.<tool>.cmd`. +variable `difftool.<tool>.cmd`. + When 'git-difftool' is invoked with this tool (either through the -`-t` or `--tool` option or the `merge.tool` configuration variable) +`-t` or `--tool` option or the `diff.tool` configuration variable) the configured command line will be invoked with the following variables available: `$LOCAL` is set to the name of the temporary file containing the contents of the diff pre-image and `$REMOTE` @@ -61,24 +61,24 @@ with custom merge tool commands and has the same value as `$LOCAL`. CONFIG VARIABLES ---------------- -merge.tool:: - The default merge tool to use. -+ -See the `--tool=<tool>` option above for more details. +'git-difftool' falls back to 'git-mergetool' config variables when the +difftool equivalents have not been defined. -merge.keepBackup:: - The original, unedited file content can be saved to a file with - a `.orig` extension. Defaults to `true` (i.e. keep the backup files). +diff.tool:: + The default merge tool to use. -mergetool.<tool>.path:: +difftool.<tool>.path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. -mergetool.<tool>.cmd:: +difftool.<tool>.cmd:: Specify the command to invoke the specified merge tool. + See the `--tool=<tool>` option above for more details. +merge.keepBackup:: + The original, unedited file content can be saved to a file with + a `.orig` extension. Defaults to `true` (i.e. keep the backup files). SEE ALSO -------- diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3832f60225..342529db30 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1142,7 +1142,7 @@ class P4Sync(Command): s = '' for (key, val) in self.users.items(): - s += "%s\t%s\n" % (key, val) + s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1)) open(self.getUserCacheFilename(), "wb").write(s) self.userMapFromPerforceServer = True diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 23aeb257b9..6309d146e7 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -14,13 +14,18 @@ die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV; my $branch_name = 'import-tars'; my $branch_ref = "refs/heads/$branch_name"; -my $committer_name = 'T Ar Creator'; -my $committer_email = 'tar@example.com'; +my $author_name = $ENV{'GIT_AUTHOR_NAME'} || 'T Ar Creator'; +my $author_email = $ENV{'GIT_AUTHOR_EMAIL'} || 'tar@example.com'; +my $committer_name = $ENV{'GIT_COMMITTER_NAME'} || `git config --get user.name`; +my $committer_email = $ENV{'GIT_COMMITTER_EMAIL'} || `git config --get user.email`; + +chomp($committer_name, $committer_email); open(FI, '|-', 'git', 'fast-import', '--quiet') or die "Unable to start git fast-import: $!\n"; foreach my $tar_file (@ARGV) { + my $commit_time = time; $tar_file =~ m,([^/]+)$,; my $tar_name = $1; @@ -39,7 +44,7 @@ foreach my $tar_file (@ARGV) die "Unrecognized compression format: $tar_file\n"; } - my $commit_time = 0; + my $author_time = 0; my $next_mark = 1; my $have_top_dir = 1; my ($top_dir, %files); @@ -92,7 +97,7 @@ foreach my $tar_file (@ARGV) } $files{$path} = [$next_mark++, $mode]; - $commit_time = $mtime if $mtime > $commit_time; + $author_time = $mtime if $mtime > $author_time; $path =~ m,^([^/]+)/,; $top_dir = $1 unless $top_dir; $have_top_dir = 0 if $top_dir ne $1; @@ -100,6 +105,7 @@ foreach my $tar_file (@ARGV) print FI <<EOF; commit $branch_ref +author $author_name <$author_email> $author_time +0000 committer $committer_name <$committer_email> $commit_time +0000 data <<END_OF_COMMIT_MESSAGE Imported from $tar_file. @@ -119,7 +125,7 @@ EOF print FI <<EOF; tag $tar_name from $branch_ref -tagger $committer_name <$committer_email> $commit_time +0000 +tagger $author_name <$author_email> $author_time +0000 data <<END_OF_TAG_MESSAGE Package $tar_name END_OF_TAG_MESSAGE @@ -229,7 +229,7 @@ static char *path_ok(char *directory) } if (!path) { - logerror("'%s': unable to chdir or not a git archive", dir); + logerror("'%s' does not appear to be a git repository", dir); return NULL; } diff --git a/diff-lib.c b/diff-lib.c index 79d0606834..a310fb2ad0 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -31,7 +31,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st) return -1; return 1; } - if (has_symlink_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_leading_path(ce->name, ce_namelen(ce))) return 1; if (S_ISDIR(st->st_mode)) { unsigned char sub[20]; diff --git a/diff-no-index.c b/diff-no-index.c index 7273a7a0b6..42c1dd8ad3 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -38,6 +38,10 @@ static int get_mode(const char *path, int *mode) if (!path || !strcmp(path, "/dev/null")) *mode = 0; +#ifdef _WIN32 + else if (!strcasecmp(path, "nul")) + *mode = 0; +#endif else if (!strcmp(path, "-")) *mode = create_ce_mode(0666); else if (lstat(path, &st)) @@ -30,14 +30,14 @@ int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static char diff_colors[][COLOR_MAXLEN] = { - "\033[m", /* reset */ - "", /* PLAIN (normal) */ - "\033[1m", /* METAINFO (bold) */ - "\033[36m", /* FRAGINFO (cyan) */ - "\033[31m", /* OLD (red) */ - "\033[32m", /* NEW (green) */ - "\033[33m", /* COMMIT (yellow) */ - "\033[41m", /* WHITESPACE (red background) */ + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_BOLD, /* METAINFO */ + GIT_COLOR_CYAN, /* FRAGINFO */ + GIT_COLOR_RED, /* OLD */ + GIT_COLOR_GREEN, /* NEW */ + GIT_COLOR_YELLOW, /* COMMIT */ + GIT_COLOR_BG_RED, /* WHITESPACE */ }; static void diff_filespec_load_driver(struct diff_filespec *one); @@ -875,7 +875,7 @@ static void fill_print_name(struct diffstat_file *file) static void show_stats(struct diffstat_t* data, struct diff_options *options) { - int i, len, add, del, total, adds = 0, dels = 0; + int i, len, add, del, adds = 0, dels = 0; int max_change = 0, max_len = 0; int total_files = data->nr; int width, name_width; @@ -978,14 +978,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) */ add = added; del = deleted; - total = add + del; adds += add; dels += del; if (width <= max_change) { add = scale_linear(add, width, max_change); del = scale_linear(del, width, max_change); - total = add + del; } show_name(options->file, prefix, name, len, reset, set); fprintf(options->file, "%5d%s", added + deleted, @@ -1759,7 +1757,8 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int struct stat st; int pos, len; - /* We do not read the cache ourselves here, because the + /* + * We do not read the cache ourselves here, because the * benchmark with my previous version that always reads cache * shows that it makes things worse for diff-tree comparing * two linux-2.6 kernel trees in an already checked out work @@ -1783,7 +1782,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int * objects however would tend to be slower as they need * to be individually opened and inflated. */ - if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL)) + if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1)) return 0; len = strlen(name); @@ -1800,6 +1799,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int return 0; /* + * If ce is marked as "assume unchanged", there is no + * guarantee that work tree matches what we are looking for. + */ + if (ce->ce_flags & CE_VALID) + return 0; + + /* * If ce matches the file in the work tree, we can reuse it. */ if (ce_uptodate(ce) || @@ -2574,13 +2580,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) /* xdiff options */ else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE; + DIFF_XDL_SET(options, IGNORE_WHITESPACE); else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE); else if (!strcmp(arg, "--ignore-space-at-eol")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL); else if (!strcmp(arg, "--patience")) - options->xdl_opts |= XDF_PATIENCE_DIFF; + DIFF_XDL_SET(options, PATIENCE_DIFF); /* flags options */ else if (!strcmp(arg, "--binary")) { @@ -2601,10 +2607,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, COLOR_DIFF); else if (!strcmp(arg, "--no-color")) 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 (!strcmp(arg, "--color-words")) { + DIFF_OPT_SET(options, COLOR_DIFF); + DIFF_OPT_SET(options, COLOR_DIFF_WORDS); + } else if (!prefixcmp(arg, "--color-words=")) { - options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + DIFF_OPT_SET(options, COLOR_DIFF); + DIFF_OPT_SET(options, COLOR_DIFF_WORDS); options->word_regex = arg + 14; } else if (!strcmp(arg, "--exit-code")) @@ -69,6 +69,9 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) +#define DIFF_XDL_TST(opts, flag) ((opts)->xdl_opts & XDF_##flag) +#define DIFF_XDL_SET(opts, flag) ((opts)->xdl_opts |= XDF_##flag) +#define DIFF_XDL_CLR(opts, flag) ((opts)->xdl_opts &= ~XDF_##flag) struct diff_options { const char *filter; diff --git a/diffcore-break.c b/diffcore-break.c index 31cdcfe8bc..d7097bb576 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -45,7 +45,7 @@ static int should_break(struct diff_filespec *src, * The value we return is 1 if we want the pair to be broken, * or 0 if we do not. */ - unsigned long delta_size, base_size, max_size; + unsigned long delta_size, max_size; unsigned long src_copied, literal_added, src_removed; *merge_score_p = 0; /* assume no deletion --- "do not break" @@ -64,7 +64,6 @@ static int should_break(struct diff_filespec *src, if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0)) return 0; /* error but caught downstream */ - base_size = ((src->size < dst->size) ? src->size : dst->size); max_size = ((src->size > dst->size) ? src->size : dst->size); if (max_size < MINIMUM_BREAK_SIZE) return 0; /* we do not break too small filepair */ @@ -487,14 +487,14 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, return recurse_into_directory; case index_gitdir: - if (dir->show_other_directories) + if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) return ignore_directory; return show_directory; case index_nonexistent: - if (dir->show_other_directories) + if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) break; - if (!dir->no_gitlinks) { + if (!(dir->flags & DIR_NO_GITLINKS)) { unsigned char sha1[20]; if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0) return show_directory; @@ -503,7 +503,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir, } /* This is the "show_other_directories" case */ - if (!dir->hide_empty_directories) + if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return show_directory; if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify)) return ignore_directory; @@ -601,7 +601,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co dtype = DTYPE(de); exclude = excluded(dir, fullname, &dtype); - if (exclude && dir->collect_ignored + if (exclude && (dir->flags & DIR_COLLECT_IGNORED) && in_pathspec(fullname, baselen + len, simplify)) dir_add_ignored(dir, fullname, baselen + len); @@ -609,7 +609,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co * Excluded? If we don't explicitly want to show * ignored files, ignore it */ - if (exclude && !dir->show_ignored) + if (exclude && !(dir->flags & DIR_SHOW_IGNORED)) continue; if (dtype == DT_UNKNOWN) @@ -621,7 +621,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co * even if we don't ignore them, since the * directory may contain files that we do.. */ - if (!exclude && dir->show_ignored) { + if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) { if (dtype != DT_DIR) continue; } @@ -634,7 +634,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co len++; switch (treat_directory(dir, fullname, baselen + len, simplify)) { case show_directory: - if (exclude != dir->show_ignored) + if (exclude != !!(dir->flags + & DIR_SHOW_IGNORED)) continue; break; case recurse_into_directory: @@ -720,7 +721,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i { struct path_simplify *simplify; - if (has_symlink_leading_path(strlen(path), path)) + if (has_symlink_leading_path(path, strlen(path))) return dir->nr; simplify = create_simplify(pathspec); @@ -34,11 +34,13 @@ struct exclude_stack { struct dir_struct { int nr, alloc; int ignored_nr, ignored_alloc; - unsigned int show_ignored:1, - show_other_directories:1, - hide_empty_directories:1, - no_gitlinks:1, - collect_ignored:1; + enum { + DIR_SHOW_IGNORED = 1<<0, + DIR_SHOW_OTHER_DIRECTORIES = 1<<1, + DIR_HIDE_EMPTY_DIRECTORIES = 1<<2, + DIR_NO_GITLINKS = 1<<3, + DIR_COLLECT_IGNORED = 1<<4 + } flags; struct dir_entry **entries; struct dir_entry **ignored; @@ -2,15 +2,19 @@ #include "blob.h" #include "dir.h" -static void create_directories(const char *path, const struct checkout *state) +static void create_directories(const char *path, int path_len, + const struct checkout *state) { - int len = strlen(path); - char *buf = xmalloc(len + 1); - const char *slash = path; - - while ((slash = strchr(slash+1, '/')) != NULL) { - len = slash - path; - memcpy(buf, path, len); + char *buf = xmalloc(path_len + 1); + int len = 0; + + while (len < path_len) { + do { + buf[len] = path[len]; + len++; + } while (len < path_len && path[len] != '/'); + if (len >= path_len) + break; buf[len] = 0; /* @@ -20,7 +24,7 @@ static void create_directories(const char *path, const struct checkout *state) * 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)) + if (has_dirs_only_path(buf, len, state->base_dir_len)) continue; /* ok, it is already a directory. */ /* @@ -74,7 +78,7 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } -static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) +static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) { enum object_type type; void *new = read_sha1_file(ce->sha1, &type, size); @@ -89,36 +93,52 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { - int fd; - long wrote; - - switch (ce->ce_mode & S_IFMT) { - char *new; - struct strbuf buf; - unsigned long size; + unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; + int fd, ret, fstat_done = 0; + char *new; + struct strbuf buf = STRBUF_INIT; + unsigned long size; + size_t wrote, newsize = 0; + struct stat st; + switch (ce_mode_s_ifmt) { case S_IFREG: - new = read_blob_entry(ce, path, &size); + case S_IFLNK: + new = read_blob_entry(ce, &size); if (!new) return error("git checkout-index: unable to read sha1 file of %s (%s)", path, sha1_to_hex(ce->sha1)); + if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) { + ret = symlink(new, path); + free(new); + if (ret) + return error("git checkout-index: unable to create symlink %s (%s)", + path, strerror(errno)); + break; + } + /* * Convert from git internal format to working tree format */ - strbuf_init(&buf, 0); - if (convert_to_working_tree(ce->name, new, size, &buf)) { - size_t newsize = 0; + if (ce_mode_s_ifmt == S_IFREG && + convert_to_working_tree(ce->name, new, size, &buf)) { free(new); new = strbuf_detach(&buf, &newsize); size = newsize; } if (to_tempfile) { - strcpy(path, ".merge_file_XXXXXX"); + if (ce_mode_s_ifmt == S_IFREG) + strcpy(path, ".merge_file_XXXXXX"); + else + strcpy(path, ".merge_link_XXXXXX"); fd = mkstemp(path); - } else + } else if (ce_mode_s_ifmt == S_IFREG) { fd = create_file(path, ce->ce_mode); + } else { + fd = create_file(path, 0666); + } if (fd < 0) { free(new); return error("git checkout-index: unable to create file %s (%s)", @@ -126,41 +146,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } wrote = write_in_full(fd, new, size); + /* use fstat() only when path == ce->name */ + if (state->refresh_cache && !to_tempfile && !state->base_dir_len) { + fstat(fd, &st); + fstat_done = 1; + } close(fd); free(new); if (wrote != size) return error("git checkout-index: unable to write file %s", path); break; - case S_IFLNK: - new = read_blob_entry(ce, path, &size); - if (!new) - return error("git checkout-index: unable to read sha1 file of %s (%s)", - path, sha1_to_hex(ce->sha1)); - if (to_tempfile || !has_symlinks) { - if (to_tempfile) { - strcpy(path, ".merge_link_XXXXXX"); - fd = mkstemp(path); - } else - fd = create_file(path, 0666); - if (fd < 0) { - free(new); - return error("git checkout-index: unable to create " - "file %s (%s)", path, strerror(errno)); - } - wrote = write_in_full(fd, new, size); - close(fd); - free(new); - if (wrote != size) - return error("git checkout-index: unable to write file %s", - path); - } else { - wrote = symlink(new, path); - free(new); - if (wrote) - return error("git checkout-index: unable to create " - "symlink %s (%s)", path, strerror(errno)); - } - break; case S_IFGITLINK: if (to_tempfile) return error("git checkout-index: cannot create temporary subproject %s", path); @@ -172,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } if (state->refresh_cache) { - struct stat st; - lstat(ce->name, &st); + if (!fstat_done) + lstat(ce->name, &st); fill_stat_cache_info(ce, &st); } return 0; @@ -190,6 +185,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t memcpy(path, state->base_dir, len); strcpy(path + len, ce->name); + len += ce_namelen(ce); if (!lstat(path, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); @@ -218,6 +214,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t return error("unable to unlink old '%s' (%s)", path, strerror(errno)); } else if (state->not_new) return 0; - create_directories(path, state); + create_directories(path, len, state); return write_entry(ce, path, state, 0); } diff --git a/environment.c b/environment.c index e278bce0ea..4696885b22 100644 --- a/environment.c +++ b/environment.c @@ -42,6 +42,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; enum rebase_setup_type autorebase = AUTOREBASE_NEVER; +enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; /* Parallel index stat data preload? */ int core_preload_index = 0; diff --git a/fast-import.c b/fast-import.c index 3748ddf48d..db44da3e09 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1,4 +1,5 @@ /* +(See Documentation/git-fast-import.txt for maintained documentation.) Format of STDIN stream: stream ::= cmd*; @@ -18,8 +19,8 @@ Format of STDIN stream: new_commit ::= 'commit' sp ref_str lf mark? - ('author' sp name '<' email '>' when lf)? - 'committer' sp name '<' email '>' when lf + ('author' sp name sp '<' email '>' sp when lf)? + 'committer' sp name sp '<' email '>' sp when lf commit_msg ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)? ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)* @@ -43,7 +44,7 @@ Format of STDIN stream: new_tag ::= 'tag' sp tag_str lf 'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf - ('tagger' sp name '<' email '>' when lf)? + ('tagger' sp name sp '<' email '>' sp when lf)? tag_msg; tag_msg ::= data; @@ -1745,21 +1746,19 @@ static void parse_data(struct strbuf *sb) static int validate_raw_date(const char *src, char *result, int maxlen) { const char *orig_src = src; - char *endp, sign; - unsigned long date; + char *endp; errno = 0; - date = strtoul(src, &endp, 10); + strtoul(src, &endp, 10); if (errno || endp == src || *endp != ' ') return -1; src = endp + 1; if (*src != '-' && *src != '+') return -1; - sign = *src; - date = strtoul(src + 1, &endp, 10); + strtoul(src + 1, &endp, 10); if (errno || endp == src || *endp || (endp - orig_src) >= maxlen) return -1; @@ -148,20 +148,17 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) struct tree_desc desc; unsigned o_mode; const char *o_name; - const unsigned char *o_sha1; init_tree_desc(&desc, item->buffer, item->size); o_mode = 0; o_name = NULL; - o_sha1 = NULL; while (desc.size) { unsigned mode; const char *name; - const unsigned char *sha1; - sha1 = tree_entry_extract(&desc, &name, &mode); + tree_entry_extract(&desc, &name, &mode); if (strchr(name, '/')) has_full_path = 1; @@ -207,7 +204,6 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) o_mode = mode; o_name = name; - o_sha1 = sha1; } retval = 0; diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 064d4c68d0..def062a9e2 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -802,6 +802,10 @@ EOF || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; system('sh', '-c', $editor.' "$@"', $editor, $hunkfile); + if ($? != 0) { + return undef; + } + open $fh, '<', $hunkfile or die "failed to open hunk edit file for reading: " . $!; my @newtext = grep { !/^#/ } <$fh>; diff --git a/git-bisect.sh b/git-bisect.sh index 10ad340920..df0ae63b4e 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -77,7 +77,7 @@ bisect_start() { then # Reset to the rev from where we started. start_head=$(cat "$GIT_DIR/BISECT_START") - git checkout "$start_head" || exit + git checkout "$start_head" -- || exit else # Get rev from where we start. case "$head" in @@ -370,7 +370,7 @@ bisect_checkout() { _msg="$2" echo "Bisecting: $_msg" mark_expected_rev "$_rev" - git checkout -q "$_rev" || exit + git checkout -q "$_rev" -- || exit git show-branch "$_rev" } @@ -512,7 +512,7 @@ bisect_next() { # commit is also a "skip" commit (see above). exit_if_skipped_commits "$bisect_rev" - bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this" + bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)" } bisect_visualize() { @@ -549,7 +549,7 @@ bisect_reset() { *) usage ;; esac - git checkout "$branch" && bisect_clean_state + git checkout "$branch" -- && bisect_clean_state } bisect_clean_state() { diff --git a/git-compat-util.h b/git-compat-util.h index 878d83dd08..f09f244061 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -166,7 +166,7 @@ static inline const char *skip_prefix(const char *str, const char *prefix) return strncmp(str, prefix, len) ? NULL : str + len; } -#ifdef NO_MMAP +#if defined(NO_MMAP) || defined(USE_WIN32_MMAP) #ifndef PROT_READ #define PROT_READ 1 @@ -180,13 +180,19 @@ static inline const char *skip_prefix(const char *str, const char *prefix) extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern int git_munmap(void *start, size_t length); +#else /* NO_MMAP || USE_WIN32_MMAP */ + +#include <sys/mman.h> + +#endif /* NO_MMAP || USE_WIN32_MMAP */ + +#ifdef NO_MMAP + /* This value must be multiple of (pagesize * 2) */ #define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024) #else /* NO_MMAP */ -#include <sys/mman.h> - /* This value must be multiple of (pagesize * 2) */ #define DEFAULT_PACKED_GIT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ @@ -388,4 +394,18 @@ void git_qsort(void *base, size_t nmemb, size_t size, # define FORCE_DIR_SET_GID 0 #endif +#ifdef NO_NSEC +#undef USE_NSEC +#define ST_CTIME_NSEC(st) 0 +#define ST_MTIME_NSEC(st) 0 +#else +#ifdef USE_ST_TIMESPEC +#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec)) +#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec)) +#else +#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) +#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) +#endif +#endif + #endif diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 9a09ba1382..b90d3df3a7 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -232,7 +232,9 @@ while read sha1 type name do case "$force,$name" in ,$orig_namespace*) - die "Namespace $orig_namespace not empty" + die "Cannot create a new backup. +A previous backup already exists in $orig_namespace +Force overwriting the backup with -f" ;; t,$orig_namespace*) git update-ref -d "$name" $sha1 @@ -270,10 +272,10 @@ test $commits -eq 0 && die "Found nothing to rewrite" # Rewrite the commits -i=0 +git_filter_branch__commit_count=0 while read commit parents; do - i=$(($i+1)) - printf "\rRewrite $commit ($i/$commits)" + git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1)) + printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)" case "$filter_subdir" in "") diff --git a/git-instaweb.sh b/git-instaweb.sh index 0843372b57..5f4419b69b 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -49,7 +49,7 @@ resolve_full_httpd () { esac httpd_only="$(echo $httpd | cut -f1 -d' ')" - if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac + if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null 2>&1;; esac then full_httpd=$httpd else @@ -179,11 +179,74 @@ lighttpd_conf () { cat > "$conf" <<EOF server.document-root = "$fqgitdir/gitweb" server.port = $port -server.modules = ( "mod_cgi" ) +server.modules = ( "mod_setenv", "mod_cgi" ) server.indexfiles = ( "gitweb.cgi" ) server.pid-file = "$fqgitdir/pid" +server.errorlog = "$fqgitdir/gitweb/error.log" + +# to enable, add "mod_access", "mod_accesslog" to server.modules +# variable above and uncomment this +#accesslog.filename = "$fqgitdir/gitweb/access.log" + +setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" ) + cgi.assign = ( ".cgi" => "" ) -mimetype.assign = ( ".css" => "text/css" ) + +# mimetype mapping +mimetype.assign = ( + ".pdf" => "application/pdf", + ".sig" => "application/pgp-signature", + ".spl" => "application/futuresplash", + ".class" => "application/octet-stream", + ".ps" => "application/postscript", + ".torrent" => "application/x-bittorrent", + ".dvi" => "application/x-dvi", + ".gz" => "application/x-gzip", + ".pac" => "application/x-ns-proxy-autoconfig", + ".swf" => "application/x-shockwave-flash", + ".tar.gz" => "application/x-tgz", + ".tgz" => "application/x-tgz", + ".tar" => "application/x-tar", + ".zip" => "application/zip", + ".mp3" => "audio/mpeg", + ".m3u" => "audio/x-mpegurl", + ".wma" => "audio/x-ms-wma", + ".wax" => "audio/x-ms-wax", + ".ogg" => "application/ogg", + ".wav" => "audio/x-wav", + ".gif" => "image/gif", + ".jpg" => "image/jpeg", + ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".xbm" => "image/x-xbitmap", + ".xpm" => "image/x-xpixmap", + ".xwd" => "image/x-xwindowdump", + ".css" => "text/css", + ".html" => "text/html", + ".htm" => "text/html", + ".js" => "text/javascript", + ".asc" => "text/plain", + ".c" => "text/plain", + ".cpp" => "text/plain", + ".log" => "text/plain", + ".conf" => "text/plain", + ".text" => "text/plain", + ".txt" => "text/plain", + ".dtd" => "text/xml", + ".xml" => "text/xml", + ".mpeg" => "video/mpeg", + ".mpg" => "video/mpeg", + ".mov" => "video/quicktime", + ".qt" => "video/quicktime", + ".avi" => "video/x-msvideo", + ".asf" => "video/x-ms-asf", + ".asx" => "video/x-ms-asf", + ".wmv" => "video/x-ms-wmv", + ".bz2" => "application/x-bzip", + ".tbz" => "application/x-bzip-compressed-tar", + ".tar.bz2" => "application/x-bzip-compressed-tar", + "" => "text/plain" + ) EOF test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf" } diff --git a/git-pull.sh b/git-pull.sh index 25adddfddf..8a26763206 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -16,7 +16,7 @@ cd_to_toplevel test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." -strategy_args= no_stat= no_commit= squash= no_ff= log_arg= verbosity= +strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||") rebase=$(git config --bool branch.$curr_branch_short.rebase) @@ -28,9 +28,9 @@ do -v|--verbose) verbosity="$verbosity -v" ;; -n|--no-stat|--no-summary) - no_stat=-n ;; + diffstat=--no-stat ;; --stat|--summary) - no_stat=$1 ;; + diffstat=--stat ;; --log|--no-log) log_arg=$1 ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) @@ -188,7 +188,7 @@ fi merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit test true = "$rebase" && - exec git-rebase $strategy_args --onto $merge_head \ + exec git-rebase $diffstat $strategy_args --onto $merge_head \ ${oldremoteref:-$merge_head} -exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \ +exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \ "$merge_name" HEAD $merge_head $verbosity diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 3dc659dd58..314cd364b8 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -442,6 +442,30 @@ do_rest () { done } +# skip picking commits whose parents are unchanged +skip_unnecessary_picks () { + fd=3 + while read command sha1 rest + do + # fd=3 means we skip the command + case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in + 3,pick,"$ONTO"*|3,p,"$ONTO"*) + # pick a commit whose parent is current $ONTO -> skip + ONTO=$sha1 + ;; + 3,#*|3,,*) + # copy comments + ;; + *) + fd=1 + ;; + esac + echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd + done <"$TODO" >"$TODO.new" 3>>"$DONE" && + mv -f "$TODO".new "$TODO" || + die "Could not skip unnecessary pick commands" +} + # check if no other options are set is_standalone () { test $# -eq 2 -a "$2" = '--' && @@ -746,6 +770,8 @@ EOF has_action "$TODO" || die_abort "Nothing to do" + test -d "$REWRITTEN" || skip_unnecessary_picks + git update-ref ORIG_HEAD $HEAD output git checkout $ONTO && do_rest ;; diff --git a/git-rebase.sh b/git-rebase.sh index 368c0ef434..b83fd3f970 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' +USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -46,8 +46,10 @@ do_merge= dotest="$GIT_DIR"/rebase-merge prec=4 verbose= +diffstat=$(git config --bool rebase.stat) git_am_opt= rebase_root= +force_rebase= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -289,11 +291,27 @@ do esac do_merge=t ;; + -n|--no-stat) + diffstat= + ;; + --stat) + diffstat=t + ;; -v|--verbose) verbose=t + diffstat=t ;; --whitespace=*) git_am_opt="$git_am_opt $1" + case "$1" in + --whitespace=fix|--whitespace=strip) + force_rebase=t + ;; + esac + ;; + --committer-date-is-author-date|--ignore-date) + git_am_opt="$git_am_opt $1" + force_rebase=t ;; -C*) git_am_opt="$git_am_opt $1" @@ -301,6 +319,9 @@ do --root) rebase_root=t ;; + -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase) + force_rebase=t + ;; -*) usage ;; @@ -420,17 +441,15 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && # linear history? ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null then - # Lazily switch to the target branch if needed... - test -z "$switch_to" || git checkout "$switch_to" - echo >&2 "Current branch $branch_name is up to date." - exit 0 -fi - -if test -n "$verbose" -then - echo "Changes from $mb to $onto:" - # We want color (if set), but no pager - GIT_PAGER='' git diff --stat --summary "$mb" "$onto" + if test -z "$force_rebase" + then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || git checkout "$switch_to" + echo >&2 "Current branch $branch_name is up to date." + exit 0 + else + echo "Current branch $branch_name is up to date, rebase forced." + fi fi # Detach HEAD and reset the tree @@ -438,6 +457,16 @@ echo "First, rewinding head to replay your work on top of it..." git checkout -q "$onto^0" || die "could not detach HEAD" git update-ref ORIG_HEAD $branch +if test -n "$diffstat" +then + if test -n "$verbose" + then + echo "Changes from $mb to $onto:" + fi + # We want color (if set), but no pager + GIT_PAGER='' git diff --stat --summary "$mb" "$onto" +fi + # If the $onto is a proper descendant of the tip of the branch, then # we just fast forwarded. if test "$mb" = "$branch" diff --git a/git-repack.sh b/git-repack.sh index 00c597e97c..0868734723 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -60,6 +60,7 @@ case ",$all_into_one," in args='--unpacked --incremental' ;; ,t,) + args= existing= if [ -d "$PACKDIR" ]; then for e in `cd "$PACKDIR" && find . -type f -name '*.pack' \ | sed -e 's/^\.\///' -e 's/\.pack$//'` @@ -67,11 +68,10 @@ case ",$all_into_one," in if [ -e "$PACKDIR/$e.keep" ]; then : keep else - args="$args --unpacked=$e.pack" existing="$existing $e" fi done - if test -n "$args" -a -n "$unpack_unreachable" -a \ + if test -n "$existing" -a -n "$unpack_unreachable" -a \ -n "$remove_redundant" then args="$args $unpack_unreachable" diff --git a/git-send-email.perl b/git-send-email.perl index 09fe3d95c4..172b53c2d5 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -75,6 +75,8 @@ git send-email [options] <file | directory | rev-list options > --[no-]thread * Use In-Reply-To: field. Default on. Administering: + --confirm <str> * Confirm recipients before sending; + auto, cc, compose, always, or never. --quiet * Output one line of info per email. --dry-run * Don't actually send the emails. --[no-]validate * Perform patch sanity checks. Default on. @@ -181,7 +183,7 @@ sub do_edit { my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption); my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); -my ($validate); +my ($validate, $confirm); my (@suppress_cc); my %config_bool_settings = ( @@ -207,6 +209,7 @@ my %config_settings = ( "suppresscc" => \@suppress_cc, "envelopesender" => \$envelope_sender, "multiedit" => \$multiedit, + "confirm" => \$confirm, ); # Handle Uncouth Termination @@ -258,6 +261,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, "suppress-from!" => \$suppress_from, "suppress-cc=s" => \@suppress_cc, "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc, + "confirm=s" => \$confirm, "dry-run" => \$dry_run, "envelope-sender=s" => \$envelope_sender, "thread!" => \$thread, @@ -346,6 +350,14 @@ if ($suppress_cc{'body'}) { delete $suppress_cc{'body'}; } +# Set confirm's default value +my $confirm_unconfigured = !defined $confirm; +if ($confirm_unconfigured) { + $confirm = scalar %suppress_cc ? 'compose' : 'auto'; +}; +die "Unknown --confirm setting: '$confirm'\n" + unless $confirm =~ /^(?:auto|cc|compose|always|never)/; + # Debugging, print out the suppressions. if (0) { print "suppressions:\n"; @@ -594,32 +606,43 @@ EOT do_edit(@files); } +sub ask { + my ($prompt, %arg) = @_; + my $valid_re = $arg{valid_re}; + my $default = $arg{default}; + my $resp; + my $i = 0; + return defined $default ? $default : undef + unless defined $term->IN and defined fileno($term->IN) and + defined $term->OUT and defined fileno($term->OUT); + while ($i++ < 10) { + $resp = $term->readline($prompt); + if (!defined $resp) { # EOF + print "\n"; + return defined $default ? $default : undef; + } + if ($resp eq '' and defined $default) { + return $default; + } + if (!defined $valid_re or $resp =~ /$valid_re/) { + return $resp; + } + } + return undef; +} + my $prompting = 0; if (!defined $sender) { $sender = $repoauthor || $repocommitter || ''; - - while (1) { - $_ = $term->readline("Who should the emails appear to be from? [$sender] "); - last if defined $_; - print "\n"; - } - - $sender = $_ if ($_); + $sender = ask("Who should the emails appear to be from? [$sender] ", + default => $sender); print "Emails will be sent from: ", $sender, "\n"; $prompting++; } if (!@to) { - - - while (1) { - $_ = $term->readline("Who should the emails be sent to? ", ""); - last if defined $_; - print "\n"; - } - - my $to = $_; - push @to, parse_address_line($to); + my $to = ask("Who should the emails be sent to? "); + push @to, parse_address_line($to) if defined $to; # sanitized/validated later $prompting++; } @@ -639,13 +662,8 @@ sub expand_aliases { @bcclist = expand_aliases(@bcclist); if ($thread && !defined $initial_reply_to && $prompting) { - while (1) { - $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to); - last if defined $_; - print "\n"; - } - - $initial_reply_to = $_; + $initial_reply_to = ask( + "Message-ID to be used as In-Reply-To for the first email? "); } if (defined $initial_reply_to) { $initial_reply_to =~ s/^\s*<?//; @@ -663,25 +681,13 @@ if (!defined $smtp_server) { $smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug* } -if ($compose) { - while (1) { - $_ = $term->readline("Send this email? (y|n) "); - last if defined $_; - print "\n"; - } - - if (uc substr($_,0,1) ne 'Y') { - cleanup_compose_files(); - exit(0); - } - - if ($compose > 0) { - @files = ($compose_filename . ".final", @files); - } +if ($compose && $compose > 0) { + @files = ($compose_filename . ".final", @files); } # Variables we set as part of the loop over files -our ($message_id, %mail, $subject, $reply_to, $references, $message); +our ($message_id, %mail, $subject, $reply_to, $references, $message, + $needs_confirm, $message_num, $ask_default); sub extract_valid_address { my $address = shift; @@ -770,12 +776,13 @@ sub sanitize_address } # if recipient_name is already quoted, do nothing - if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) { + if ($recipient_name =~ /^("[[:ascii:]]*"|=\?utf-8\?q\?.*\?=)$/) { return $recipient; } # rfc2047 is needed if a non-ascii char is included if ($recipient_name =~ /[^[:ascii:]]/) { + $recipient_name =~ s/^"(.*)"$/$1/; $recipient_name = quote_rfc2047($recipient_name); } @@ -837,6 +844,35 @@ X-Mailer: git-send-email $gitversion unshift (@sendmail_parameters, '-f', $raw_from) if(defined $envelope_sender); + if ($needs_confirm && !$dry_run) { + print "\n$header\n"; + if ($needs_confirm eq "inform") { + $confirm_unconfigured = 0; # squelch this message for the rest of this run + $ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation + print " The Cc list above has been expanded by additional\n"; + print " addresses found in the patch commit message. By default\n"; + print " send-email prompts before sending whenever this occurs.\n"; + print " This behavior is controlled by the sendemail.confirm\n"; + print " configuration setting.\n"; + print "\n"; + print " For additional information, run 'git send-email --help'.\n"; + print " To retain the current behavior, but squelch this message,\n"; + print " run 'git config --global sendemail.confirm auto'.\n\n"; + } + $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ", + valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i, + default => $ask_default); + die "Send this email reply required" unless defined $_; + if (/^n/i) { + return; + } elsif (/^q/i) { + cleanup_compose_files(); + exit(0); + } elsif (/^a/i) { + $confirm = 'never'; + } + } + if ($dry_run) { # We don't want to send the email. } elsif ($smtp_server =~ m#^/#) { @@ -935,6 +971,7 @@ X-Mailer: git-send-email $gitversion $reply_to = $initial_reply_to; $references = $initial_reply_to || ''; $subject = $initial_subject; +$message_num = 0; foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; @@ -943,11 +980,12 @@ foreach my $t (@files) { my $author_encoding; my $has_content_type; my $body_encoding; - @cc = @initial_cc; + @cc = (); @xh = (); my $input_format = undef; my @header = (); $message = ""; + $message_num++; # First unfold multiline header fields while(<F>) { last if /^\s*$/; @@ -1080,6 +1118,14 @@ foreach my $t (@files) { } } + $needs_confirm = ( + $confirm eq "always" or + ($confirm =~ /^(?:auto|cc)$/ && @cc) or + ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1)); + $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc); + + @cc = (@initial_cc, @cc); + send_message(); # set up for the next message @@ -1094,13 +1140,10 @@ foreach my $t (@files) { $message_id = undef; } -if ($compose) { - cleanup_compose_files(); -} +cleanup_compose_files(); sub cleanup_compose_files() { - unlink($compose_filename, $compose_filename . ".final"); - + unlink($compose_filename, $compose_filename . ".final") if $compose; } $smtp->quit if $smtp; diff --git a/git-svn.perl b/git-svn.perl index 931d1a37bc..d9197989d2 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -68,6 +68,7 @@ my ($_stdin, $_help, $_edit, $_prefix, $_no_checkout, $_url, $_verbose, $_git_format, $_commit_url, $_tag); $Git::SVN::_follow_parent = 1; +$_q ||= 0; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, 'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache, @@ -80,7 +81,7 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'useSvnsyncProps' => \$Git::SVN::_use_svnsync_props, 'log-window-size=i' => \$Git::SVN::Ra::_log_window_size, 'no-checkout' => \$_no_checkout, - 'quiet|q' => \$_q, + 'quiet|q+' => \$_q, 'repack-flags|repack-args|repack-opts=s' => \$Git::SVN::_repack_flags, 'use-log-author' => \$Git::SVN::_use_log_author, @@ -2331,13 +2332,13 @@ sub do_git_commit { $self->{last_rev} = $log_entry->{revision}; $self->{last_commit} = $commit; - print "r$log_entry->{revision}"; + print "r$log_entry->{revision}" unless $::_q > 1; if (defined $log_entry->{svm_revision}) { - print " (\@$log_entry->{svm_revision})"; + print " (\@$log_entry->{svm_revision})" unless $::_q > 1; $self->rev_map_set($log_entry->{svm_revision}, $commit, 0, $self->svm_uuid); } - print " = $commit ($self->{ref_id})\n"; + print " = $commit ($self->{ref_id})\n" unless $::_q > 1; if (--$_gc_nr == 0) { $_gc_nr = $_gc_period; gc(); @@ -2351,7 +2352,10 @@ sub match_paths { if (my $path = $paths->{"/$self->{path}"}) { return ($path->{action} eq 'D') ? 0 : 1; } - $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//; + my $repos_root = $self->ra->{repos_root}; + my $extended_path = $self->{url} . '/' . $self->{path}; + $extended_path =~ s#^\Q$repos_root\E(/|$)##; + $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//; if (grep /$self->{path_regex}/, keys %$paths) { return 1; } @@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) { int err; + p->word_regexp = opt->word_regexp; + if (opt->fixed || is_fixed(p->pattern)) p->fixed = 1; if (opt->regflags & REG_ICASE) @@ -190,7 +192,8 @@ void compile_grep_patterns(struct grep_opt *opt) * A classic recursive descent parser would do. */ p = opt->pattern_list; - opt->pattern_expression = compile_pattern_expr(&p); + if (p) + opt->pattern_expression = compile_pattern_expr(&p); if (p) die("incomplete pattern expression: %s", p->pattern); } @@ -251,18 +254,6 @@ static int word_char(char ch) return isalnum(ch) || ch == '_'; } -static void show_line(struct grep_opt *opt, const char *bol, const char *eol, - const char *name, unsigned lno, char sign) -{ - if (opt->null_following_name) - sign = '\0'; - if (opt->pathname) - printf("%s%c", name, sign); - if (opt->linenum) - printf("%d%c", lno, sign); - printf("%.*s\n", (int)(eol-bol), bol); -} - static void show_name(struct grep_opt *opt, const char *name) { printf("%s%c", name, opt->null_following_name ? '\0' : '\n'); @@ -306,11 +297,12 @@ static struct { { "committer ", 10 }, }; -static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) +static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, + enum grep_context ctx, + regmatch_t *pmatch, int eflags) { int hit = 0; int saved_ch = 0; - regmatch_t pmatch[10]; if ((p->token != GREP_PATTERN) && ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD))) @@ -329,16 +321,12 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol } again: - if (!p->fixed) { - regex_t *exp = &p->regexp; - hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), - pmatch, 0); - } - else { + if (p->fixed) hit = !fixmatch(p->pattern, bol, pmatch); - } + else + hit = !regexec(&p->regexp, bol, 1, pmatch, eflags); - if (hit && opt->word_regexp) { + if (hit && p->word_regexp) { if ((pmatch[0].rm_so < 0) || (eol - bol) <= pmatch[0].rm_so || (pmatch[0].rm_eo < 0) || @@ -378,39 +366,33 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol return hit; } -static int match_expr_eval(struct grep_opt *o, - struct grep_expr *x, - char *bol, char *eol, - enum grep_context ctx, - int collect_hits) +static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, + enum grep_context ctx, int collect_hits) { int h = 0; + regmatch_t match; switch (x->node) { case GREP_NODE_ATOM: - h = match_one_pattern(o, x->u.atom, bol, eol, ctx); + h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0); break; case GREP_NODE_NOT: - h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0); + h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0); break; case GREP_NODE_AND: - if (!collect_hits) - return (match_expr_eval(o, x->u.binary.left, - bol, eol, ctx, 0) && - match_expr_eval(o, x->u.binary.right, - bol, eol, ctx, 0)); - h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); - h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); + if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0)) + return 0; + h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0); break; case GREP_NODE_OR: if (!collect_hits) - return (match_expr_eval(o, x->u.binary.left, + return (match_expr_eval(x->u.binary.left, bol, eol, ctx, 0) || - match_expr_eval(o, x->u.binary.right, + match_expr_eval(x->u.binary.right, bol, eol, ctx, 0)); - h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); + h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0); x->u.binary.left->hit |= h; - h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1); + h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1); break; default: die("Unexpected node type (internal error) %d", x->node); @@ -424,24 +406,104 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_expr *x = opt->pattern_expression; - return match_expr_eval(opt, x, bol, eol, ctx, collect_hits); + return match_expr_eval(x, bol, eol, ctx, collect_hits); } static int match_line(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_pat *p; + regmatch_t match; + if (opt->extended) return match_expr(opt, bol, eol, ctx, collect_hits); /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { - if (match_one_pattern(opt, p, bol, eol, ctx)) + if (match_one_pattern(p, bol, eol, ctx, &match, 0)) return 1; } return 0; } +static int match_next_pattern(struct grep_pat *p, char *bol, char *eol, + enum grep_context ctx, + regmatch_t *pmatch, int eflags) +{ + regmatch_t match; + + if (!match_one_pattern(p, bol, eol, ctx, &match, eflags)) + return 0; + if (match.rm_so < 0 || match.rm_eo < 0) + return 0; + if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) { + if (match.rm_so > pmatch->rm_so) + return 1; + if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo) + return 1; + } + pmatch->rm_so = match.rm_so; + pmatch->rm_eo = match.rm_eo; + return 1; +} + +static int next_match(struct grep_opt *opt, char *bol, char *eol, + enum grep_context ctx, regmatch_t *pmatch, int eflags) +{ + struct grep_pat *p; + int hit = 0; + + pmatch->rm_so = pmatch->rm_eo = -1; + if (bol < eol) { + for (p = opt->pattern_list; p; p = p->next) { + switch (p->token) { + case GREP_PATTERN: /* atom */ + case GREP_PATTERN_HEAD: + case GREP_PATTERN_BODY: + hit |= match_next_pattern(p, bol, eol, ctx, + pmatch, eflags); + break; + default: + break; + } + } + } + return hit; +} + +static void show_line(struct grep_opt *opt, char *bol, char *eol, + const char *name, unsigned lno, char sign) +{ + int rest = eol - bol; + + if (opt->null_following_name) + sign = '\0'; + if (opt->pathname) + printf("%s%c", name, sign); + if (opt->linenum) + printf("%d%c", lno, sign); + if (opt->color) { + regmatch_t match; + enum grep_context ctx = GREP_CONTEXT_BODY; + int ch = *eol; + int eflags = 0; + + *eol = '\0'; + while (next_match(opt, bol, eol, ctx, &match, eflags)) { + printf("%.*s%s%.*s%s", + (int)match.rm_so, bol, + opt->color_match, + (int)(match.rm_eo - match.rm_so), bol + match.rm_so, + GIT_COLOR_RESET); + bol += match.rm_eo; + rest -= match.rm_eo; + eflags = REG_NOTBOL; + } + *eol = ch; + } + printf("%.*s\n", rest, bol); +} + static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { @@ -1,5 +1,6 @@ #ifndef GREP_H #define GREP_H +#include "color.h" enum grep_pat_token { GREP_PATTERN, @@ -31,6 +32,7 @@ struct grep_pat { enum grep_header_field field; regex_t regexp; unsigned fixed:1; + unsigned word_regexp:1; }; enum grep_expr_node { @@ -76,6 +78,9 @@ struct grep_opt { unsigned relative:1; unsigned pathname:1; unsigned null_following_name:1; + int color; + char color_match[COLOR_MAXLEN]; + const char *color_external; int regflags; unsigned pre_context; unsigned post_context; diff --git a/http-push.c b/http-push.c index 30d2d34041..6ce5a1d550 100644 --- a/http-push.c +++ b/http-push.c @@ -97,7 +97,7 @@ struct repo struct remote_lock *locks; }; -static struct repo *remote; +static struct repo *repo; enum transfer_state { NEED_FETCH, @@ -324,7 +324,7 @@ static void start_fetch_loose(struct transfer_request *request) git_SHA1_Init(&request->c); - url = get_remote_object_url(remote->url, hex, 0); + url = get_remote_object_url(repo->url, hex, 0); request->url = xstrdup(url); /* If a previous temp file is present, process what was already @@ -389,7 +389,7 @@ static void start_fetch_loose(struct transfer_request *request) request->state = RUN_FETCH_LOOSE; if (!start_active_slot(slot)) { fprintf(stderr, "Unable to start GET request\n"); - remote->can_update_info_refs = 0; + repo->can_update_info_refs = 0; release_request(request); } } @@ -399,7 +399,7 @@ static void start_mkcol(struct transfer_request *request) char *hex = sha1_to_hex(request->obj->sha1); struct active_request_slot *slot; - request->url = get_remote_object_url(remote->url, hex, 1); + request->url = get_remote_object_url(repo->url, hex, 1); slot = get_active_slot(); slot->callback_func = process_response; @@ -434,10 +434,10 @@ static void start_fetch_packed(struct transfer_request *request) struct transfer_request *check_request = request_queue_head; struct active_request_slot *slot; - target = find_sha1_pack(request->obj->sha1, remote->packs); + target = find_sha1_pack(request->obj->sha1, repo->packs); if (!target) { fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", sha1_to_hex(request->obj->sha1)); - remote->can_update_info_refs = 0; + repo->can_update_info_refs = 0; release_request(request); return; } @@ -450,9 +450,9 @@ static void start_fetch_packed(struct transfer_request *request) snprintf(request->tmpfile, sizeof(request->tmpfile), "%s.temp", filename); - url = xmalloc(strlen(remote->url) + 64); + url = xmalloc(strlen(repo->url) + 64); sprintf(url, "%sobjects/pack/pack-%s.pack", - remote->url, sha1_to_hex(target->sha1)); + repo->url, sha1_to_hex(target->sha1)); /* Make sure there isn't another open request for this pack */ while (check_request) { @@ -469,7 +469,7 @@ static void start_fetch_packed(struct transfer_request *request) if (!packfile) { fprintf(stderr, "Unable to open local file %s for pack", request->tmpfile); - remote->can_update_info_refs = 0; + repo->can_update_info_refs = 0; free(url); return; } @@ -505,7 +505,7 @@ static void start_fetch_packed(struct transfer_request *request) request->state = RUN_FETCH_PACKED; if (!start_active_slot(slot)) { fprintf(stderr, "Unable to start GET request\n"); - remote->can_update_info_refs = 0; + repo->can_update_info_refs = 0; release_request(request); } } @@ -554,10 +554,10 @@ static void start_put(struct transfer_request *request) request->buffer.buf.len = stream.total_out; strbuf_addstr(&buf, "Destination: "); - append_remote_object_url(&buf, remote->url, hex, 0); + append_remote_object_url(&buf, repo->url, hex, 0); request->dest = strbuf_detach(&buf, NULL); - append_remote_object_url(&buf, remote->url, hex, 0); + append_remote_object_url(&buf, repo->url, hex, 0); strbuf_add(&buf, request->lock->tmpfile_suffix, 41); request->url = strbuf_detach(&buf, NULL); @@ -648,7 +648,7 @@ static int refresh_lock(struct remote_lock *lock) static void check_locks(void) { - struct remote_lock *lock = remote->locks; + struct remote_lock *lock = repo->locks; time_t current_time = time(NULL); int time_remaining; @@ -759,7 +759,7 @@ static void finish_request(struct transfer_request *request) } } else { if (request->http_code == 416) - fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n"); + warning("requested range invalid; we may already have all the data."); git_inflate_end(&request->stream); git_SHA1_Final(request->real_sha1, &request->c); @@ -788,7 +788,7 @@ static void finish_request(struct transfer_request *request) if (request->curl_result != CURLE_OK) { fprintf(stderr, "Unable to get pack file %s\n%s", request->url, curl_errorstr); - remote->can_update_info_refs = 0; + repo->can_update_info_refs = 0; } else { off_t pack_size = ftell(request->local_stream); @@ -798,7 +798,7 @@ static void finish_request(struct transfer_request *request) request->filename)) { target = (struct packed_git *)request->userData; target->pack_size = pack_size; - lst = &remote->packs; + lst = &repo->packs; while (*lst != target) lst = &((*lst)->next); *lst = (*lst)->next; @@ -806,7 +806,7 @@ static void finish_request(struct transfer_request *request) if (!verify_pack(target)) install_packed_git(target); else - remote->can_update_info_refs = 0; + repo->can_update_info_refs = 0; } } release_request(request); @@ -816,7 +816,7 @@ static void finish_request(struct transfer_request *request) #ifdef USE_CURL_MULTI static int fill_active_slot(void *unused) { - struct transfer_request *request = request_queue_head; + struct transfer_request *request; if (aborted) return 0; @@ -889,7 +889,7 @@ static int add_send_request(struct object *obj, struct remote_lock *lock) get_remote_object_list(obj->sha1[0]); if (obj->flags & (REMOTE | PUSHING)) return 0; - target = find_sha1_pack(obj->sha1, remote->packs); + target = find_sha1_pack(obj->sha1, repo->packs); if (target) { obj->flags |= REMOTE; return 0; @@ -930,8 +930,8 @@ static int fetch_index(unsigned char *sha1) struct slot_results results; /* Don't use the index if the pack isn't there */ - url = xmalloc(strlen(remote->url) + 64); - sprintf(url, "%sobjects/pack/pack-%s.pack", remote->url, hex); + url = xmalloc(strlen(repo->url) + 64); + sprintf(url, "%sobjects/pack/pack-%s.pack", repo->url, hex); slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_URL, url); @@ -956,7 +956,7 @@ static int fetch_index(unsigned char *sha1) if (push_verbosely) fprintf(stderr, "Getting index for pack %s\n", hex); - sprintf(url, "%sobjects/pack/pack-%s.idx", remote->url, hex); + sprintf(url, "%sobjects/pack/pack-%s.idx", repo->url, hex); filename = sha1_pack_index_name(sha1); snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename); @@ -1018,8 +1018,8 @@ static int setup_index(unsigned char *sha1) return -1; new_pack = parse_pack_index(sha1); - new_pack->next = remote->packs; - remote->packs = new_pack; + new_pack->next = repo->packs; + repo->packs = new_pack; return 0; } @@ -1037,8 +1037,8 @@ static int fetch_indices(void) if (push_verbosely) fprintf(stderr, "Getting pack list\n"); - url = xmalloc(strlen(remote->url) + 20); - sprintf(url, "%sobjects/info/packs", remote->url); + url = xmalloc(strlen(repo->url) + 20); + sprintf(url, "%sobjects/info/packs", repo->url); slot = get_active_slot(); slot->results = &results; @@ -1223,11 +1223,11 @@ static struct remote_lock *lock_remote(const char *path, long timeout) struct curl_slist *dav_headers = NULL; struct xml_ctx ctx; - url = xmalloc(strlen(remote->url) + strlen(path) + 1); - sprintf(url, "%s%s", remote->url, path); + url = xmalloc(strlen(repo->url) + strlen(path) + 1); + sprintf(url, "%s%s", repo->url, path); /* Make sure leading directories exist for the remote ref */ - ep = strchr(url + strlen(remote->url) + 1, '/'); + ep = strchr(url + strlen(repo->url) + 1, '/'); while (ep) { char saved_character = ep[1]; ep[1] = '\0'; @@ -1319,8 +1319,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout) } else { lock->url = url; lock->start_time = time(NULL); - lock->next = remote->locks; - remote->locks = lock; + lock->next = repo->locks; + repo->locks = lock; } return lock; @@ -1330,7 +1330,7 @@ static int unlock_remote(struct remote_lock *lock) { struct active_request_slot *slot; struct slot_results results; - struct remote_lock *prev = remote->locks; + struct remote_lock *prev = repo->locks; struct curl_slist *dav_headers; int rc = 0; @@ -1356,8 +1356,8 @@ static int unlock_remote(struct remote_lock *lock) curl_slist_free_all(dav_headers); - if (remote->locks == lock) { - remote->locks = lock->next; + if (repo->locks == lock) { + repo->locks = lock->next; } else { while (prev && prev->next != lock) prev = prev->next; @@ -1375,7 +1375,7 @@ static int unlock_remote(struct remote_lock *lock) static void remove_locks(void) { - struct remote_lock *lock = remote->locks; + struct remote_lock *lock = repo->locks; fprintf(stderr, "Removing remote locks...\n"); while (lock) { @@ -1457,7 +1457,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed) } } if (path) { - path += remote->path_len; + path += repo->path_len; ls->dentry_name = xstrdup(path); } } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) { @@ -1480,7 +1480,7 @@ static void remote_ls(const char *path, int flags, void (*userFunc)(struct remote_ls_ctx *ls), void *userData) { - char *url = xmalloc(strlen(remote->url) + strlen(path) + 1); + char *url = xmalloc(strlen(repo->url) + strlen(path) + 1); struct active_request_slot *slot; struct slot_results results; struct strbuf in_buffer = STRBUF_INIT; @@ -1496,7 +1496,7 @@ static void remote_ls(const char *path, int flags, ls.userData = userData; ls.userFunc = userFunc; - sprintf(url, "%s%s", remote->url, path); + sprintf(url, "%s%s", repo->url, path); strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST); @@ -1574,7 +1574,7 @@ static int locking_available(void) struct xml_ctx ctx; int lock_flags = 0; - strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url); + strbuf_addf(&out_buffer.buf, PROPFIND_SUPPORTEDLOCK_REQUEST, repo->url); dav_headers = curl_slist_append(dav_headers, "Depth: 0"); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); @@ -1586,7 +1586,7 @@ static int locking_available(void) curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url); + curl_easy_setopt(slot->curl, CURLOPT_URL, repo->url); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); @@ -1616,16 +1616,16 @@ static int locking_available(void) } XML_ParserFree(parser); if (!lock_flags) - error("Error: no DAV locking support on %s", - remote->url); + error("no DAV locking support on %s", + repo->url); } else { error("Cannot access URL %s, return code %d", - remote->url, results.curl_result); + repo->url, results.curl_result); lock_flags = 0; } } else { - error("Unable to start PROPFIND request on %s", remote->url); + error("Unable to start PROPFIND request on %s", repo->url); } strbuf_release(&out_buffer.buf); @@ -1792,21 +1792,8 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) return 1; } -static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - static void one_remote_ref(char *refname) { struct ref *ref; @@ -1814,10 +1801,10 @@ static void one_remote_ref(char *refname) ref = alloc_ref(refname); - if (http_fetch_ref(remote->url, ref) != 0) { + if (http_fetch_ref(repo->url, ref) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", - refname, remote->url); + refname, repo->url); free(ref); return; } @@ -1826,7 +1813,7 @@ static void one_remote_ref(char *refname) * Fetch a copy of the object if it doesn't exist locally - it * may be required for updating server info later. */ - if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) { + if (repo->can_update_info_refs && !has_sha1_file(ref->old_sha1)) { obj = lookup_unknown_object(ref->old_sha1); if (obj) { fprintf(stderr, " fetch %s for %s\n", @@ -1839,12 +1826,6 @@ static void one_remote_ref(char *refname) remote_tail = &ref->next; } -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - static void get_dav_remote_heads(void) { remote_tail = &remote_refs; @@ -1862,55 +1843,6 @@ static int is_zero_sha1(const unsigned char *sha1) return 1; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, TMP_MARK); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, TMP_MARK); - unmark_and_free(used, TMP_MARK); - return found; -} - static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; @@ -1921,10 +1853,10 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls) ref = alloc_ref(ls->dentry_name); - if (http_fetch_ref(remote->url, ref) != 0) { + if (http_fetch_ref(repo->url, ref) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", - ls->dentry_name, remote->url); + ls->dentry_name, repo->url); aborted = 1; free(ref); return; @@ -1999,12 +1931,12 @@ static void update_remote_info_refs(struct remote_lock *lock) static int remote_exists(const char *path) { - char *url = xmalloc(strlen(remote->url) + strlen(path) + 1); + char *url = xmalloc(strlen(repo->url) + strlen(path) + 1); struct active_request_slot *slot; struct slot_results results; int ret = -1; - sprintf(url, "%s%s", remote->url, path); + sprintf(url, "%s%s", repo->url, path); slot = get_active_slot(); slot->results = &results; @@ -2034,8 +1966,8 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) struct active_request_slot *slot; struct slot_results results; - url = xmalloc(strlen(remote->url) + strlen(path) + 1); - sprintf(url, "%s%s", remote->url, path); + url = xmalloc(strlen(repo->url) + strlen(path) + 1); + sprintf(url, "%s%s", repo->url, path); slot = get_active_slot(); slot->results = &results; @@ -2150,7 +2082,7 @@ static int delete_remote_branch(char *pattern, int force) "of your current HEAD.\n" "If you are sure you want to delete it," " run:\n\t'git http-push -D %s %s'", - remote_ref->name, remote->url, pattern); + remote_ref->name, repo->url, pattern); } } @@ -2158,8 +2090,8 @@ static int delete_remote_branch(char *pattern, int force) fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name); if (dry_run) return 0; - url = xmalloc(strlen(remote->url) + strlen(remote_ref->name) + 1); - sprintf(url, "%s%s", remote->url, remote_ref->name); + url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1); + sprintf(url, "%s%s", repo->url, remote_ref->name); slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); @@ -2195,14 +2127,15 @@ int main(int argc, char **argv) int rc = 0; int i; int new_refs; - struct ref *ref; + struct ref *ref, *local_refs; + struct remote *remote; char *rewritten_url = NULL; git_extract_argv0_path(argv[0]); setup_git_directory(); - remote = xcalloc(sizeof(*remote), 1); + repo = xcalloc(sizeof(*repo), 1); argv++; for (i = 1; i < argc; i++, argv++) { @@ -2235,14 +2168,14 @@ int main(int argc, char **argv) continue; } } - if (!remote->url) { + if (!repo->url) { char *path = strstr(arg, "//"); - remote->url = arg; - remote->path_len = strlen(arg); + repo->url = arg; + repo->path_len = strlen(arg); if (path) { - remote->path = strchr(path+2, '/'); - if (remote->path) - remote->path_len = strlen(remote->path); + repo->path = strchr(path+2, '/'); + if (repo->path) + repo->path_len = strlen(repo->path); } continue; } @@ -2255,7 +2188,7 @@ int main(int argc, char **argv) die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI"); #endif - if (!remote->url) + if (!repo->url) usage(http_push_usage); if (delete_branch && nr_refspec != 1) @@ -2263,17 +2196,24 @@ int main(int argc, char **argv) memset(remote_dir_exists, -1, 256); - http_init(NULL); + /* + * Create a minimum remote by hand to give to http_init(), + * primarily to allow it to look at the URL. + */ + remote = xcalloc(sizeof(*remote), 1); + ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc); + remote->url[remote->url_nr++] = repo->url; + http_init(remote); no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); - if (remote->url && remote->url[strlen(remote->url)-1] != '/') { - rewritten_url = xmalloc(strlen(remote->url)+2); - strcpy(rewritten_url, remote->url); + if (repo->url && repo->url[strlen(repo->url)-1] != '/') { + rewritten_url = xmalloc(strlen(repo->url)+2); + strcpy(rewritten_url, repo->url); strcat(rewritten_url, "/"); - remote->path = rewritten_url + (remote->path - remote->url); - remote->path_len++; - remote->url = rewritten_url; + repo->path = rewritten_url + (repo->path - repo->url); + repo->path_len++; + repo->url = rewritten_url; } /* Verify DAV compliance/lock support */ @@ -2285,24 +2225,24 @@ int main(int argc, char **argv) sigchain_push_common(remove_locks_on_signal); /* Check whether the remote has server info files */ - remote->can_update_info_refs = 0; - remote->has_info_refs = remote_exists("info/refs"); - remote->has_info_packs = remote_exists("objects/info/packs"); - if (remote->has_info_refs) { + repo->can_update_info_refs = 0; + repo->has_info_refs = remote_exists("info/refs"); + repo->has_info_packs = remote_exists("objects/info/packs"); + if (repo->has_info_refs) { info_ref_lock = lock_remote("info/refs", LOCK_TIME); if (info_ref_lock) - remote->can_update_info_refs = 1; + repo->can_update_info_refs = 1; else { - fprintf(stderr, "Error: cannot lock existing info/refs\n"); + error("cannot lock existing info/refs"); rc = 1; goto cleanup; } } - if (remote->has_info_packs) + if (repo->has_info_packs) fetch_indices(); /* Get a list of all local and remote heads to validate refspecs */ - get_local_heads(); + local_refs = get_local_heads(); fprintf(stderr, "Fetching remote heads...\n"); get_dav_remote_heads(); @@ -2456,8 +2396,8 @@ int main(int argc, char **argv) } /* Update remote server info if appropriate */ - if (remote->has_info_refs && new_refs) { - if (info_ref_lock && remote->can_update_info_refs) { + if (repo->has_info_refs && new_refs) { + if (info_ref_lock && repo->can_update_info_refs) { fprintf(stderr, "Updating remote server info\n"); if (!dry_run) update_remote_info_refs(info_ref_lock); @@ -2470,7 +2410,7 @@ int main(int argc, char **argv) free(rewritten_url); if (info_ref_lock) unlock_remote(info_ref_lock); - free(remote); + free(repo); curl_slist_free_all(no_pragma_header); @@ -1,7 +1,7 @@ #include "http.h" int data_received; -int active_requests = 0; +int active_requests; #ifdef USE_CURL_MULTI static int max_requests = -1; @@ -13,22 +13,23 @@ static CURL *curl_default; char curl_errorstr[CURL_ERROR_SIZE]; static int curl_ssl_verify = -1; -static const char *ssl_cert = NULL; +static const char *ssl_cert; #if LIBCURL_VERSION_NUM >= 0x070902 -static const char *ssl_key = NULL; +static const char *ssl_key; #endif #if LIBCURL_VERSION_NUM >= 0x070908 -static const char *ssl_capath = NULL; +static const char *ssl_capath; #endif -static const char *ssl_cainfo = NULL; +static const char *ssl_cainfo; static long curl_low_speed_limit = -1; static long curl_low_speed_time = -1; -static int curl_ftp_no_epsv = 0; -static const char *curl_http_proxy = NULL; +static int curl_ftp_no_epsv; +static const char *curl_http_proxy; +static char *user_name, *user_pass; static struct curl_slist *pragma_header; -static struct active_request_slot *active_queue_head = NULL; +static struct active_request_slot *active_queue_head; size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_) { @@ -94,53 +95,33 @@ static void process_curl_messages(void) static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { - if (curl_ssl_verify == -1) { - curl_ssl_verify = git_config_bool(var, value); - } - return 0; - } - - if (!strcmp("http.sslcert", var)) { - if (ssl_cert == NULL) - return git_config_string(&ssl_cert, var, value); + curl_ssl_verify = git_config_bool(var, value); return 0; } + if (!strcmp("http.sslcert", var)) + return git_config_string(&ssl_cert, var, value); #if LIBCURL_VERSION_NUM >= 0x070902 - if (!strcmp("http.sslkey", var)) { - if (ssl_key == NULL) - return git_config_string(&ssl_key, var, value); - return 0; - } + if (!strcmp("http.sslkey", var)) + return git_config_string(&ssl_key, var, value); #endif #if LIBCURL_VERSION_NUM >= 0x070908 - if (!strcmp("http.sslcapath", var)) { - if (ssl_capath == NULL) - return git_config_string(&ssl_capath, var, value); - return 0; - } + if (!strcmp("http.sslcapath", var)) + return git_config_string(&ssl_capath, var, value); #endif - if (!strcmp("http.sslcainfo", var)) { - if (ssl_cainfo == NULL) - return git_config_string(&ssl_cainfo, var, value); - return 0; - } - + if (!strcmp("http.sslcainfo", var)) + return git_config_string(&ssl_cainfo, var, value); #ifdef USE_CURL_MULTI if (!strcmp("http.maxrequests", var)) { - if (max_requests == -1) - max_requests = git_config_int(var, value); + max_requests = git_config_int(var, value); return 0; } #endif - if (!strcmp("http.lowspeedlimit", var)) { - if (curl_low_speed_limit == -1) - curl_low_speed_limit = (long)git_config_int(var, value); + curl_low_speed_limit = (long)git_config_int(var, value); return 0; } if (!strcmp("http.lowspeedtime", var)) { - if (curl_low_speed_time == -1) - curl_low_speed_time = (long)git_config_int(var, value); + curl_low_speed_time = (long)git_config_int(var, value); return 0; } @@ -148,19 +129,28 @@ static int http_options(const char *var, const char *value, void *cb) curl_ftp_no_epsv = git_config_bool(var, value); return 0; } - if (!strcmp("http.proxy", var)) { - if (curl_http_proxy == NULL) - return git_config_string(&curl_http_proxy, var, value); - return 0; - } + if (!strcmp("http.proxy", var)) + return git_config_string(&curl_http_proxy, var, value); /* Fall back on the default ones */ return git_default_config(var, value, cb); } -static CURL* get_curl_handle(void) +static void init_curl_http_auth(CURL *result) { - CURL* result = curl_easy_init(); + if (user_name) { + struct strbuf up = STRBUF_INIT; + if (!user_pass) + user_pass = xstrdup(getpass("Password: ")); + strbuf_addf(&up, "%s:%s", user_name, user_pass); + curl_easy_setopt(result, CURLOPT_USERPWD, + strbuf_detach(&up, NULL)); + } +} + +static CURL *get_curl_handle(void) +{ + CURL *result = curl_easy_init(); if (!curl_ssl_verify) { curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0); @@ -176,6 +166,8 @@ static CURL* get_curl_handle(void) curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); #endif + init_curl_http_auth(result); + if (ssl_cert != NULL) curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); #if LIBCURL_VERSION_NUM >= 0x070902 @@ -213,11 +205,60 @@ static CURL* get_curl_handle(void) return result; } +static void http_auth_init(const char *url) +{ + char *at, *colon, *cp, *slash; + int len; + + cp = strstr(url, "://"); + if (!cp) + return; + + /* + * Ok, the URL looks like "proto://something". Which one? + * "proto://<user>:<pass>@<host>/...", + * "proto://<user>@<host>/...", or just + * "proto://<host>/..."? + */ + cp += 3; + at = strchr(cp, '@'); + colon = strchr(cp, ':'); + slash = strchrnul(cp, '/'); + if (!at || slash <= at) + return; /* No credentials */ + if (!colon || at <= colon) { + /* Only username */ + len = at - cp; + user_name = xmalloc(len + 1); + memcpy(user_name, cp, len); + user_name[len] = '\0'; + user_pass = NULL; + } else { + len = colon - cp; + user_name = xmalloc(len + 1); + memcpy(user_name, cp, len); + user_name[len] = '\0'; + len = at - (colon + 1); + user_pass = xmalloc(len + 1); + memcpy(user_pass, colon + 1, len); + user_pass[len] = '\0'; + } +} + +static void set_from_env(const char **var, const char *envname) +{ + const char *val = getenv(envname); + if (val) + *var = val; +} + void http_init(struct remote *remote) { char *low_speed_limit; char *low_speed_time; + git_config(http_options, NULL); + curl_global_init(CURL_GLOBAL_ALL); if (remote && remote->http_proxy) @@ -242,14 +283,14 @@ void http_init(struct remote *remote) if (getenv("GIT_SSL_NO_VERIFY")) curl_ssl_verify = 0; - ssl_cert = getenv("GIT_SSL_CERT"); + set_from_env(&ssl_cert, "GIT_SSL_CERT"); #if LIBCURL_VERSION_NUM >= 0x070902 - ssl_key = getenv("GIT_SSL_KEY"); + set_from_env(&ssl_key, "GIT_SSL_KEY"); #endif #if LIBCURL_VERSION_NUM >= 0x070908 - ssl_capath = getenv("GIT_SSL_CAPATH"); + set_from_env(&ssl_capath, "GIT_SSL_CAPATH"); #endif - ssl_cainfo = getenv("GIT_SSL_CAINFO"); + set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO"); low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); if (low_speed_limit != NULL) @@ -258,8 +299,6 @@ void http_init(struct remote *remote) if (low_speed_time != NULL) curl_low_speed_time = strtol(low_speed_time, NULL, 10); - git_config(http_options, NULL); - if (curl_ssl_verify == -1) curl_ssl_verify = 1; @@ -271,6 +310,9 @@ void http_init(struct remote *remote) if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1; + if (remote && remote->url && remote->url[0]) + http_auth_init(remote->url[0]); + #ifndef NO_CURL_EASY_DUPHANDLE curl_default = get_curl_handle(); #endif @@ -322,15 +364,14 @@ struct active_request_slot *get_active_slot(void) /* Wait for a slot to open up if the queue is full */ while (active_requests >= max_requests) { curl_multi_perform(curlm, &num_transfers); - if (num_transfers < active_requests) { + if (num_transfers < active_requests) process_curl_messages(); - } } #endif - while (slot != NULL && slot->in_use) { + while (slot != NULL && slot->in_use) slot = slot->next; - } + if (slot == NULL) { newslot = xmalloc(sizeof(*newslot)); newslot->curl = NULL; @@ -341,9 +382,8 @@ struct active_request_slot *get_active_slot(void) if (slot == NULL) { active_queue_head = newslot; } else { - while (slot->next != NULL) { + while (slot->next != NULL) slot = slot->next; - } slot->next = newslot; } slot = newslot; @@ -404,7 +444,7 @@ struct fill_chain { struct fill_chain *next; }; -static struct fill_chain *fill_cfg = NULL; +static struct fill_chain *fill_cfg; void add_fill_function(void *data, int (*fill)(void *)) { @@ -535,9 +575,8 @@ static void finish_active_slot(struct active_request_slot *slot) } /* Run callback if appropriate */ - if (slot->callback_func != NULL) { + if (slot->callback_func != NULL) slot->callback_func(slot->callback_data); - } } void finish_all_active_slots(void) @@ -567,37 +606,29 @@ static inline int needs_quote(int ch) static inline int hex(int v) { - if (v < 10) return '0' + v; - else return 'A' + v - 10; + if (v < 10) + return '0' + v; + else + return 'A' + v - 10; } static char *quote_ref_url(const char *base, const char *ref) { + struct strbuf buf = STRBUF_INIT; const char *cp; - char *dp, *qref; - int len, baselen, ch; + int ch; + + strbuf_addstr(&buf, base); + if (buf.len && buf.buf[buf.len - 1] != '/' && *ref != '/') + strbuf_addstr(&buf, "/"); - baselen = strlen(base); - len = baselen + 2; /* '/' after base and terminating NUL */ - for (cp = ref; (ch = *cp) != 0; cp++, len++) + for (cp = ref; (ch = *cp) != 0; cp++) if (needs_quote(ch)) - len += 2; /* extra two hex plus replacement % */ - qref = xmalloc(len); - memcpy(qref, base, baselen); - dp = qref + baselen; - *(dp++) = '/'; - for (cp = ref; (ch = *cp) != 0; cp++) { - if (needs_quote(ch)) { - *dp++ = '%'; - *dp++ = hex((ch >> 4) & 0xF); - *dp++ = hex(ch & 0xF); - } + strbuf_addf(&buf, "%%%02x", ch); else - *dp++ = ch; - } - *dp = 0; + strbuf_addch(&buf, *cp); - return qref; + return strbuf_detach(&buf, NULL); } int http_fetch_ref(const char *base, struct ref *ref) diff --git a/imap-send.c b/imap-send.c index f91293c23f..8154cb2116 100644 --- a/imap-send.c +++ b/imap-send.c @@ -135,6 +135,7 @@ struct imap_server_conf { char *pass; int use_ssl; int ssl_verify; + int use_html; }; struct imap_store_conf { @@ -578,7 +579,7 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen); free(cmd->cb.data); if (n != cmd->cb.dlen || - (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) { + socket_write(&imap->buf.sock, "\r\n", 2) != 2) { free(cmd->cmd); free(cmd); return NULL; @@ -1263,6 +1264,53 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid) return DRV_OK; } +static void encode_html_chars(struct strbuf *p) +{ + int i; + for (i = 0; i < p->len; i++) { + if (p->buf[i] == '&') + strbuf_splice(p, i, 1, "&", 5); + if (p->buf[i] == '<') + strbuf_splice(p, i, 1, "<", 4); + if (p->buf[i] == '>') + strbuf_splice(p, i, 1, ">", 4); + if (p->buf[i] == '"') + strbuf_splice(p, i, 1, """, 6); + } +} +static void wrap_in_html(struct msg_data *msg) +{ + struct strbuf buf = STRBUF_INIT; + struct strbuf **lines; + struct strbuf **p; + static char *content_type = "Content-Type: text/html;\n"; + static char *pre_open = "<pre>\n"; + static char *pre_close = "</pre>\n"; + int added_header = 0; + + strbuf_attach(&buf, msg->data, msg->len, msg->len); + lines = strbuf_split(&buf, '\n'); + strbuf_release(&buf); + for (p = lines; *p; p++) { + if (! added_header) { + if ((*p)->len == 1 && *((*p)->buf) == '\n') { + strbuf_addstr(&buf, content_type); + strbuf_addbuf(&buf, *p); + strbuf_addstr(&buf, pre_open); + added_header = 1; + continue; + } + } + else + encode_html_chars(*p); + strbuf_addbuf(&buf, *p); + } + strbuf_addstr(&buf, pre_close); + strbuf_list_free(lines); + msg->len = buf.len; + msg->data = strbuf_detach(&buf, NULL); +} + #define CHUNKSIZE 0x1000 static int read_message(FILE *f, struct msg_data *msg) @@ -1339,6 +1387,7 @@ static struct imap_server_conf server = { NULL, /* pass */ 0, /* use_ssl */ 1, /* ssl_verify */ + 0, /* use_html */ }; static char *imap_folder; @@ -1377,6 +1426,8 @@ static int git_imap_config(const char *key, const char *val, void *cb) server.tunnel = xstrdup(val); else if (!strcmp("sslverify", key)) server.ssl_verify = git_config_bool(key, val); + else if (!strcmp("preformattedHTML", key)) + server.use_html = git_config_bool(key, val); return 0; } @@ -1439,6 +1490,8 @@ int main(int argc, char **argv) fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); if (!split_msg(&all_msgs, &msg, &ofs)) break; + if (server.use_html) + wrap_in_html(&msg); r = imap_store_msg(ctx, &msg, &uid); if (r != DRV_OK) break; diff --git a/index-pack.c b/index-pack.c index 7fee872533..75468228d3 100644 --- a/index-pack.c +++ b/index-pack.c @@ -232,7 +232,7 @@ static void free_base_data(struct base_data *c) static void prune_base_data(struct base_data *retain) { - struct base_data *b = base_cache; + struct base_data *b; for (b = base_cache; base_cache_used > delta_base_cache_limit && b; b = b->child) { diff --git a/log-tree.c b/log-tree.c index 84a74e544b..9565c184db 100644 --- a/log-tree.c +++ b/log-tree.c @@ -6,6 +6,7 @@ #include "log-tree.h" #include "reflog-walk.h" #include "refs.h" +#include "string-list.h" struct decoration name_decoration = { "object names" }; @@ -79,18 +80,18 @@ void show_decorations(struct rev_info *opt, struct commit *commit) */ static int detect_any_signoff(char *letter, int size) { - char ch, *cp; + char *cp; int seen_colon = 0; int seen_at = 0; int seen_name = 0; int seen_head = 0; cp = letter + size; - while (letter <= --cp && (ch = *cp) == '\n') + while (letter <= --cp && *cp == '\n') continue; while (letter <= cp) { - ch = *cp--; + char ch = *cp--; if (ch == '\n') break; @@ -211,9 +212,13 @@ void log_write_email_headers(struct rev_info *opt, const char *name, printf("Message-Id: <%s>\n", opt->message_id); graph_show_oneline(opt->graph); } - if (opt->ref_message_id) { - printf("In-Reply-To: <%s>\nReferences: <%s>\n", - opt->ref_message_id, opt->ref_message_id); + if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) { + int i, n; + n = opt->ref_message_ids->nr; + printf("In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string); + for (i = 0; i < n; i++) + printf("%s<%s>\n", (i > 0 ? "\t" : "References: "), + opt->ref_message_ids->items[i].string); graph_show_oneline(opt->graph); } if (opt->mime_boundary) { @@ -99,7 +99,8 @@ static void add_mapping(struct string_list *map, old_name, old_email, new_name, new_email); } -static char *parse_name_and_email(char *buffer, char **name, char **email) +static char *parse_name_and_email(char *buffer, char **name, + char **email, int allow_empty_email) { char *left, *right, *nstart, *nend; *name = *email = 0; @@ -108,7 +109,7 @@ static char *parse_name_and_email(char *buffer, char **name, char **email) return NULL; if ((right = strchr(left+1, '>')) == NULL) return NULL; - if (left+1 == right) + if (!allow_empty_email && (left+1 == right)) return NULL; /* remove whitespace from beginning and end of name */ @@ -159,8 +160,8 @@ static int read_single_mailmap(struct string_list *map, const char *filename, ch } continue; } - if ((name2 = parse_name_and_email(buffer, &name1, &email1)) != NULL) - parse_name_and_email(name2, &name2, &email2); + if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL) + parse_name_and_email(name2, &name2, &email2, 1); if (email1) add_mapping(map, name1, email1, name2, email2); diff --git a/merge-recursive.c b/merge-recursive.c index ee853b990d..3e1bc3e07f 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -801,22 +801,19 @@ static int process_renames(struct merge_options *o, } for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) { - int compare; char *src; - struct string_list *renames1, *renames2, *renames2Dst; + struct string_list *renames1, *renames2Dst; struct rename *ren1 = NULL, *ren2 = NULL; const char *branch1, *branch2; const char *ren1_src, *ren1_dst; if (i >= a_renames->nr) { - compare = 1; ren2 = b_renames->items[j++].util; } else if (j >= b_renames->nr) { - compare = -1; ren1 = a_renames->items[i++].util; } else { - compare = strcmp(a_renames->items[i].string, - b_renames->items[j].string); + int compare = strcmp(a_renames->items[i].string, + b_renames->items[j].string); if (compare <= 0) ren1 = a_renames->items[i++].util; if (compare >= 0) @@ -826,14 +823,12 @@ static int process_renames(struct merge_options *o, /* TODO: refactor, so that 1/2 are not needed */ if (ren1) { renames1 = a_renames; - renames2 = b_renames; renames2Dst = &b_by_dst; branch1 = o->branch1; branch2 = o->branch2; } else { struct rename *tmp; renames1 = b_renames; - renames2 = a_renames; renames2Dst = &a_by_dst; branch1 = o->branch2; branch2 = o->branch1; diff --git a/parse-options.c b/parse-options.c index 4c5d09dd25..cf71bcffd2 100644 --- a/parse-options.c +++ b/parse-options.c @@ -244,6 +244,9 @@ void parse_options_start(struct parse_opt_ctx_t *ctx, ctx->out = argv; ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); ctx->flags = flags; + if ((flags & PARSE_OPT_KEEP_UNKNOWN) && + (flags & PARSE_OPT_STOP_AT_NON_OPTION)) + die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); } static int usage_with_options_internal(const char * const *, @@ -253,6 +256,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, const char * const usagestr[]) { + int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); + /* we must reset ->opt, unknown short option leave it dangling */ ctx->opt = NULL; @@ -268,18 +273,18 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (arg[1] != '-') { ctx->opt = arg + 1; - if (*ctx->opt == 'h') + if (internal_help && *ctx->opt == 'h') return parse_options_usage(usagestr, options); switch (parse_short_opt(ctx, options)) { case -1: return parse_options_usage(usagestr, options); case -2: - return PARSE_OPT_UNKNOWN; + goto unknown; } if (ctx->opt) check_typos(arg + 1, options); while (ctx->opt) { - if (*ctx->opt == 'h') + if (internal_help && *ctx->opt == 'h') return parse_options_usage(usagestr, options); switch (parse_short_opt(ctx, options)) { case -1: @@ -292,7 +297,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, */ ctx->argv[0] = xstrdup(ctx->opt - 1); *(char *)ctx->argv[0] = '-'; - return PARSE_OPT_UNKNOWN; + goto unknown; } } continue; @@ -306,16 +311,22 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, break; } - if (!strcmp(arg + 2, "help-all")) + if (internal_help && !strcmp(arg + 2, "help-all")) return usage_with_options_internal(usagestr, options, 1); - if (!strcmp(arg + 2, "help")) + if (internal_help && !strcmp(arg + 2, "help")) return parse_options_usage(usagestr, options); switch (parse_long_opt(ctx, arg + 2, options)) { case -1: return parse_options_usage(usagestr, options); case -2: - return PARSE_OPT_UNKNOWN; + goto unknown; } + continue; +unknown: + if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) + return PARSE_OPT_UNKNOWN; + ctx->out[ctx->cpidx++] = ctx->argv[0]; + ctx->opt = NULL; } return PARSE_OPT_DONE; } @@ -356,6 +367,9 @@ int parse_options(int argc, const char **argv, const struct option *options, int usage_with_options_internal(const char * const *usagestr, const struct option *opts, int full) { + if (!usagestr) + return PARSE_OPT_HELP; + fprintf(stderr, "usage: %s\n", *usagestr++); while (*usagestr && **usagestr) fprintf(stderr, " or: %s\n", *usagestr++); diff --git a/parse-options.h b/parse-options.h index 912290549b..f8ef1db128 100644 --- a/parse-options.h +++ b/parse-options.h @@ -21,6 +21,8 @@ enum parse_opt_flags { PARSE_OPT_KEEP_DASHDASH = 1, PARSE_OPT_STOP_AT_NON_OPTION = 2, PARSE_OPT_KEEP_ARGV0 = 4, + PARSE_OPT_KEEP_UNKNOWN = 8, + PARSE_OPT_NO_INTERNAL_HELP = 16, }; enum parse_opt_option_flags { @@ -10,6 +10,15 @@ static char *user_format; +static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat) +{ + free(user_format); + user_format = xstrdup(cp); + if (is_tformat) + rev->use_terminator = 1; + rev->commit_format = CMIT_FMT_USERFORMAT; +} + void get_commit_format(const char *arg, struct rev_info *rev) { int i; @@ -33,12 +42,7 @@ void get_commit_format(const char *arg, struct rev_info *rev) return; } if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) { - const char *cp = strchr(arg, ':') + 1; - free(user_format); - user_format = xstrdup(cp); - if (arg[0] == 't') - rev->use_terminator = 1; - rev->commit_format = CMIT_FMT_USERFORMAT; + save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't'); return; } for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { @@ -50,6 +54,10 @@ void get_commit_format(const char *arg, struct rev_info *rev) return; } } + if (strchr(arg, '%')) { + save_user_format(rev, arg, 1); + return; + } die("invalid --pretty format: %s", arg); } @@ -127,7 +135,6 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, int namelen; unsigned long time; int tz; - const char *filler = " "; if (fmt == CMIT_FMT_ONELINE) return; @@ -146,7 +153,6 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, while (line < name_tail && isspace(name_tail[-1])) name_tail--; display_name_length = name_tail - line; - filler = ""; strbuf_addstr(sb, "From: "); add_rfc2047(sb, line, display_name_length, encoding); strbuf_add(sb, name_tail, namelen - display_name_length); @@ -154,7 +160,7 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, } else { strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); + " ", namelen, line); } switch (fmt) { case CMIT_FMT_MEDIUM: @@ -567,16 +573,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, return end - placeholder + 1; } if (!prefixcmp(placeholder + 1, "red")) { - strbuf_addstr(sb, "\033[31m"); + strbuf_addstr(sb, GIT_COLOR_RED); return 4; } else if (!prefixcmp(placeholder + 1, "green")) { - strbuf_addstr(sb, "\033[32m"); + strbuf_addstr(sb, GIT_COLOR_GREEN); return 6; } else if (!prefixcmp(placeholder + 1, "blue")) { - strbuf_addstr(sb, "\033[34m"); + strbuf_addstr(sb, GIT_COLOR_BLUE); return 5; } else if (!prefixcmp(placeholder + 1, "reset")) { - strbuf_addstr(sb, "\033[m"); + strbuf_addstr(sb, GIT_COLOR_RESET); return 6; } else return 0; diff --git a/read-cache.c b/read-cache.c index 940ec76fdf..3f587110cb 100644 --- a/read-cache.c +++ b/read-cache.c @@ -67,8 +67,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n */ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) { - ce->ce_ctime = st->st_ctime; - ce->ce_mtime = st->st_mtime; + ce->ce_ctime.sec = (unsigned int)st->st_ctime; + ce->ce_mtime.sec = (unsigned int)st->st_mtime; + ce->ce_ctime.nsec = ST_CTIME_NSEC(*st); + ce->ce_mtime.nsec = ST_MTIME_NSEC(*st); ce->ce_dev = st->st_dev; ce->ce_ino = st->st_ino; ce->ce_uid = st->st_uid; @@ -196,11 +198,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) default: die("internal error: ce_mode is %o", ce->ce_mode); } - if (ce->ce_mtime != (unsigned int) st->st_mtime) + if (ce->ce_mtime.sec != (unsigned int)st->st_mtime) changed |= MTIME_CHANGED; - if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime) + if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime) changed |= CTIME_CHANGED; +#ifdef USE_NSEC + if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st)) + changed |= MTIME_CHANGED; + if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st)) + changed |= CTIME_CHANGED; +#endif + if (ce->ce_uid != (unsigned int) st->st_uid || ce->ce_gid != (unsigned int) st->st_gid) changed |= OWNER_CHANGED; @@ -232,8 +241,16 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce) { return (!S_ISGITLINK(ce->ce_mode) && - istate->timestamp && - ((unsigned int)istate->timestamp) <= ce->ce_mtime); + istate->timestamp.sec && +#ifdef USE_NSEC + /* nanosecond timestamped files can also be racy! */ + (istate->timestamp.sec < ce->ce_mtime.sec || + (istate->timestamp.sec == ce->ce_mtime.sec && + istate->timestamp.nsec <= ce->ce_mtime.nsec)) +#else + istate->timestamp.sec <= ce->ce_mtime.sec +#endif + ); } int ie_match_stat(const struct index_state *istate, @@ -443,6 +460,26 @@ int remove_index_entry_at(struct index_state *istate, int pos) return 1; } +/* + * Remove all cache ententries marked for removal, that is where + * CE_REMOVE is set in ce_flags. This is much more effective than + * calling remove_index_entry_at() for each entry to be removed. + */ +void remove_marked_cache_entries(struct index_state *istate) +{ + struct cache_entry **ce_array = istate->cache; + unsigned int i, j; + + for (i = j = 0; i < istate->cache_nr; i++) { + if (ce_array[i]->ce_flags & CE_REMOVE) + remove_name_hash(ce_array[i]); + else + ce_array[j++] = ce_array[i]; + } + istate->cache_changed = 1; + istate->cache_nr = j; +} + int remove_file_from_index(struct index_state *istate, const char *path) { int pos = index_name_pos(istate, path, strlen(path)); @@ -1139,8 +1176,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en size_t len; const char *name; - ce->ce_ctime = ntohl(ondisk->ctime.sec); - ce->ce_mtime = ntohl(ondisk->mtime.sec); + ce->ce_ctime.sec = ntohl(ondisk->ctime.sec); + ce->ce_mtime.sec = ntohl(ondisk->mtime.sec); + ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec); + ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec); ce->ce_dev = ntohl(ondisk->dev); ce->ce_ino = ntohl(ondisk->ino); ce->ce_mode = ntohl(ondisk->mode); @@ -1206,7 +1245,8 @@ int read_index_from(struct index_state *istate, const char *path) return istate->cache_nr; errno = ENOENT; - istate->timestamp = 0; + istate->timestamp.sec = 0; + istate->timestamp.nsec = 0; fd = open(path, O_RDONLY); if (fd < 0) { if (errno == ENOENT) @@ -1258,7 +1298,9 @@ int read_index_from(struct index_state *istate, const char *path) src_offset += ondisk_ce_size(ce); dst_offset += ce_size(ce); } - istate->timestamp = st.st_mtime; + istate->timestamp.sec = st.st_mtime; + istate->timestamp.nsec = ST_MTIME_NSEC(st); + while (src_offset <= mmap_size - 20 - 8) { /* After an array of active_nr index entries, * there can be arbitrary number of extended @@ -1288,14 +1330,15 @@ unmap: int is_index_unborn(struct index_state *istate) { - return (!istate->cache_nr && !istate->alloc && !istate->timestamp); + return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec); } int discard_index(struct index_state *istate) { istate->cache_nr = 0; istate->cache_changed = 0; - istate->timestamp = 0; + istate->timestamp.sec = 0; + istate->timestamp.nsec = 0; istate->name_hash_initialized = 0; free_hash(&istate->name_hash); cache_tree_free(&(istate->cache_tree)); @@ -1441,10 +1484,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) struct ondisk_cache_entry *ondisk = xcalloc(1, size); char *name; - ondisk->ctime.sec = htonl(ce->ce_ctime); - ondisk->ctime.nsec = 0; - ondisk->mtime.sec = htonl(ce->ce_mtime); - ondisk->mtime.nsec = 0; + ondisk->ctime.sec = htonl(ce->ce_ctime.sec); + ondisk->mtime.sec = htonl(ce->ce_mtime.sec); + ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec); + ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec); ondisk->dev = htonl(ce->ce_dev); ondisk->ino = htonl(ce->ce_ino); ondisk->mode = htonl(ce->ce_mode); @@ -1466,13 +1509,14 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) return ce_write(c, fd, ondisk, size); } -int write_index(const struct index_state *istate, int newfd) +int write_index(struct index_state *istate, int newfd) { git_SHA_CTX c; struct cache_header hdr; int i, err, removed, extended; struct cache_entry **cache = istate->cache; int entries = istate->cache_nr; + struct stat st; for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) @@ -1516,7 +1560,12 @@ int write_index(const struct index_state *istate, int newfd) if (err) return -1; } - return ce_flush(&c, newfd); + + if (ce_flush(&c, newfd) || fstat(newfd, &st)) + return -1; + istate->timestamp.sec = (unsigned int)st.st_mtime; + istate->timestamp.nsec = ST_MTIME_NSEC(st); + return 0; } /* diff --git a/reflog-walk.c b/reflog-walk.c index f751fdc8d8..fd065f4e1a 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -242,7 +242,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) } void show_reflog_message(struct reflog_walk_info* info, int oneline, - int relative_date) + enum date_mode dmode) { if (info && info->last_commit_reflog) { struct commit_reflog *commit_reflog = info->last_commit_reflog; @@ -251,8 +251,10 @@ void show_reflog_message(struct reflog_walk_info* info, int oneline, info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; if (oneline) { printf("%s@{", commit_reflog->reflogs->ref); - if (commit_reflog->flag || relative_date) - printf("%s", show_date(info->timestamp, 0, 1)); + if (commit_reflog->flag || dmode) + printf("%s", show_date(info->timestamp, + info->tz, + dmode)); else printf("%d", commit_reflog->reflogs->nr - 2 - commit_reflog->recno); @@ -260,10 +262,10 @@ void show_reflog_message(struct reflog_walk_info* info, int oneline, } else { printf("Reflog: %s@{", commit_reflog->reflogs->ref); - if (commit_reflog->flag || relative_date) + if (commit_reflog->flag || dmode) printf("%s", show_date(info->timestamp, info->tz, - relative_date)); + dmode)); else printf("%d", commit_reflog->reflogs->nr - 2 - commit_reflog->recno); diff --git a/reflog-walk.h b/reflog-walk.h index 7ca1438f4d..74c90964bd 100644 --- a/reflog-walk.h +++ b/reflog-walk.h @@ -1,11 +1,14 @@ #ifndef REFLOG_WALK_H #define REFLOG_WALK_H +#include "cache.h" + extern void init_reflog_walk(struct reflog_walk_info** info); extern int add_reflog_for_walk(struct reflog_walk_info *info, struct commit *commit, const char *name); extern void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit); -extern void show_reflog_message(struct reflog_walk_info *info, int, int); +extern void show_reflog_message(struct reflog_walk_info *info, int, + enum date_mode); #endif @@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch) int check_ref_format(const char *ref) { int ch, level, bad_type; + int ret = CHECK_REF_FORMAT_OK; const char *cp = ref; level = 0; @@ -709,18 +710,18 @@ int check_ref_format(const char *ref) return CHECK_REF_FORMAT_ERROR; bad_type = bad_ref_char(ch); if (bad_type) { - return (bad_type == 2 && !*cp) - ? CHECK_REF_FORMAT_WILDCARD - : CHECK_REF_FORMAT_ERROR; + if (bad_type == 2 && (!*cp || *cp == '/') && + ret == CHECK_REF_FORMAT_OK) + ret = CHECK_REF_FORMAT_WILDCARD; + else + return CHECK_REF_FORMAT_ERROR; } /* scan the rest of the path component */ while ((ch = *cp++) != 0) { bad_type = bad_ref_char(ch); if (bad_type) { - return (bad_type == 2 && !*cp) - ? CHECK_REF_FORMAT_WILDCARD - : CHECK_REF_FORMAT_ERROR; + return CHECK_REF_FORMAT_ERROR; } if (ch == '/') break; @@ -731,11 +732,21 @@ int check_ref_format(const char *ref) if (!ch) { if (level < 2) return CHECK_REF_FORMAT_ONELEVEL; - return CHECK_REF_FORMAT_OK; + return ret; } } } +const char *prettify_ref(const struct ref *ref) +{ + const char *name = ref->name; + return name + ( + !prefixcmp(name, "refs/heads/") ? 11 : + !prefixcmp(name, "refs/tags/") ? 10 : + !prefixcmp(name, "refs/remotes/") ? 13 : + 0); +} + const char *ref_rev_parse_rules[] = { "%.*s", "refs/%.*s", @@ -995,7 +1006,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt) err = unlink(git_path("logs/%s", lock->ref_name)); if (err && errno != ENOENT) - fprintf(stderr, "warning: unlink(%s) failed: %s", + warning("unlink(%s) failed: %s", git_path("logs/%s", lock->ref_name), strerror(errno)); invalidate_cached_refs(); unlock_ref(lock); @@ -1437,8 +1448,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * if (get_sha1_hex(rec + 41, sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { - fprintf(stderr, - "warning: Log %s has gap after %s.\n", + warning("Log %s has gap after %s.", logfile, show_date(date, tz, DATE_RFC2822)); } } @@ -1450,8 +1460,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * if (get_sha1_hex(rec + 41, logged_sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { - fprintf(stderr, - "warning: Log %s unexpectedly ended on %s.\n", + warning("Log %s unexpectedly ended on %s.", logfile, show_date(date, tz, DATE_RFC2822)); } } @@ -1628,10 +1637,10 @@ int update_ref(const char *action, const char *refname, return 0; } -struct ref *find_ref_by_name(struct ref *list, const char *name) +struct ref *find_ref_by_name(const struct ref *list, const char *name) { for ( ; list; list = list->next) if (!strcmp(list->name, name)) - return list; + return (struct ref *)list; return NULL; } @@ -79,6 +79,8 @@ extern int for_each_reflog(each_ref_fn, void *); #define CHECK_REF_FORMAT_WILDCARD (-3) extern int check_ref_format(const char *target); +extern const char *prettify_ref(const struct ref *ref); + /** rename ref, return 0 on success **/ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg); @@ -5,13 +5,14 @@ #include "diff.h" #include "revision.h" #include "dir.h" +#include "tag.h" static struct refspec s_tag_refspec = { 0, 1, 0, - "refs/tags/", - "refs/tags/" + "refs/tags/*", + "refs/tags/*" }; const struct refspec *tag_refspec = &s_tag_refspec; @@ -454,16 +455,11 @@ static void read_config(void) */ static int verify_refname(char *name, int is_glob) { - int result, len = -1; + int result; - if (is_glob) { - len = strlen(name); - assert(name[len - 1] == '/'); - name[len - 1] = '\0'; - } result = check_ref_format(name); - if (is_glob) - name[len - 1] = '/'; + if (is_glob && result == CHECK_REF_FORMAT_WILDCARD) + result = CHECK_REF_FORMAT_OK; return result; } @@ -498,7 +494,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp int is_glob; const char *lhs, *rhs; - llen = is_glob = 0; + is_glob = 0; lhs = refspec[i]; if (*lhs == '+') { @@ -519,16 +515,15 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (rhs) { size_t rlen = strlen(++rhs); - is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); - rs[i].dst = xstrndup(rhs, rlen - is_glob); + is_glob = (1 <= rlen && strchr(rhs, '*')); + rs[i].dst = xstrndup(rhs, rlen); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); - if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) { + if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; - llen--; } else if (rhs && is_glob) { goto invalid; } @@ -728,6 +723,41 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } +static int match_name_with_pattern(const char *key, const char *name, + const char *value, char **result) +{ + const char *kstar = strchr(key, '*'); + size_t klen; + size_t ksuffixlen; + size_t namelen; + int ret; + if (!kstar) + die("Key '%s' of pattern had no '*'", key); + klen = kstar - key; + ksuffixlen = strlen(kstar + 1); + namelen = strlen(name); + ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen && + !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen); + if (ret && value) { + const char *vstar = strchr(value, '*'); + size_t vlen; + size_t vsuffixlen; + if (!vstar) + die("Value '%s' of pattern has no '*'", value); + vlen = vstar - value; + vsuffixlen = strlen(vstar + 1); + *result = xmalloc(vlen + vsuffixlen + + strlen(name) - + klen - ksuffixlen + 1); + strncpy(*result, value, vlen); + strncpy(*result + vlen, + name + klen, namelen - klen - ksuffixlen); + strcpy(*result + vlen + namelen - klen - ksuffixlen, + vstar + 1); + } + return ret; +} + int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; @@ -751,13 +781,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (!prefixcmp(needle, key)) { - *result = xmalloc(strlen(value) + - strlen(needle) - - strlen(key) + 1); - strcpy(*result, value); - strcpy(*result + strlen(value), - needle + strlen(key)); + if (match_name_with_pattern(key, needle, value, result)) { refspec->force = fetch->force; return 0; } @@ -787,10 +811,18 @@ struct ref *alloc_ref(const char *name) static struct ref *copy_ref(const struct ref *ref) { - struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); - memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); - ret->next = NULL; - return ret; + struct ref *cpy; + size_t len; + if (!ref) + return NULL; + len = strlen(ref->name); + cpy = xmalloc(sizeof(struct ref) + len + 1); + memcpy(cpy, ref, sizeof(struct ref) + len + 1); + cpy->next = NULL; + cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL; + cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL; + cpy->peer_ref = copy_ref(ref->peer_ref); + return cpy; } struct ref *copy_ref_list(const struct ref *ref) @@ -809,6 +841,7 @@ static void free_ref(struct ref *ref) { if (!ref) return; + free_ref(ref->peer_ref); free(ref->remote_status); free(ref->symref); free(ref); @@ -819,7 +852,6 @@ void free_refs(struct ref *ref) struct ref *next; while (ref) { next = ref->next; - free(ref->peer_ref); free_ref(ref); ref = next; } @@ -936,6 +968,7 @@ static int match_explicit(struct ref *src, struct ref *dst, struct refspec *rs) { struct ref *matched_src, *matched_dst; + int copy_src; const char *dst_value = rs->dst; char *dst_guess; @@ -946,6 +979,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { case 1: + copy_src = 1; break; case 0: /* The source could be in the get_sha1() format @@ -955,6 +989,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = try_explicit_object_name(rs->src); if (!matched_src) return error("src refspec %s does not match any.", rs->src); + copy_src = 0; break; default: return error("src refspec %s matches more than one.", rs->src); @@ -1000,7 +1035,7 @@ static int match_explicit(struct ref *src, struct ref *dst, return error("dst ref %s receives from more than one src.", matched_dst->name); else { - matched_dst->peer_ref = matched_src; + matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src; matched_dst->force = rs->force; } return 0; @@ -1029,7 +1064,8 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) + if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name, + NULL, NULL)) return rs + i; } if (matching_refs != -1) @@ -1049,6 +1085,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + int errs; static const char *default_refspec[] = { ":", 0 }; if (!nr_refspec) { @@ -1056,8 +1093,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, refspec = default_refspec; } rs = parse_push_refspec(nr_refspec, (const char **) refspec); - if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) - return -1; + errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec); /* pick the remainder */ for ( ; src; src = src->next) { @@ -1083,11 +1119,9 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, } else { const char *dst_side = pat->dst ? pat->dst : pat->src; - dst_name = xmalloc(strlen(dst_side) + - strlen(src->name) - - strlen(pat->src) + 2); - strcpy(dst_name, dst_side); - strcat(dst_name, src->name + strlen(pat->src)); + if (!match_name_with_pattern(pat->src, src->name, + dst_side, &dst_name)) + die("Didn't think it matches any more"); } dst_peer = find_ref_by_name(dst, dst_name); if (dst_peer) { @@ -1108,11 +1142,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } - dst_peer->peer_ref = src; + dst_peer->peer_ref = copy_ref(src); dst_peer->force = pat->force; free_name: free(dst_name); } + if (errs) + return -1; return 0; } @@ -1163,19 +1199,17 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *ret = NULL; struct ref **tail = &ret; - int remote_prefix_len = strlen(refspec->src); - int local_prefix_len = strlen(refspec->dst); + char *expn_name; for (ref = remote_refs; ref; ref = ref->next) { if (strchr(ref->name, '^')) continue; /* a dereference item */ - if (!prefixcmp(ref->name, refspec->src)) { - const char *match; + if (match_name_with_pattern(refspec->src, ref->name, + refspec->dst, &expn_name)) { struct ref *cpy = copy_ref(ref); - match = ref->name + remote_prefix_len; - cpy->peer_ref = alloc_ref_with_prefix(refspec->dst, - local_prefix_len, match); + cpy->peer_ref = alloc_ref(expn_name); + free(expn_name); if (refspec->force) cpy->peer_ref->force = 1; *tail = cpy; @@ -1278,6 +1312,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) return 1; } +static void unmark_and_free(struct commit_list *list, unsigned int mark) +{ + while (list) { + struct commit_list *temp = list; + temp->item->object.flags &= ~mark; + list = temp->next; + free(temp); + } +} + +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1) +{ + struct object *o; + struct commit *old, *new; + struct commit_list *list, *used; + int found = 0; + + /* Both new and old must be commit-ish and new is descendant of + * old. Otherwise we require --force. + */ + o = deref_tag(parse_object(old_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + old = (struct commit *) o; + + o = deref_tag(parse_object(new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + new = (struct commit *) o; + + if (parse_commit(new) < 0) + return 0; + + used = list = NULL; + commit_list_insert(new, &list); + while (list) { + new = pop_most_recent_commit(&list, TMP_MARK); + commit_list_insert(new, &used); + if (new == old) { + found = 1; + break; + } + } + unmark_and_free(list, TMP_MARK); + unmark_and_free(used, TMP_MARK); + return found; +} + /* * Return true if there is anything to report, otherwise false. */ @@ -1319,9 +1401,10 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) if (theirs == ours) return 0; - /* Run "rev-list --left-right ours...theirs" internally... */ + /* Run "rev-list --no-merges --left-right ours...theirs" internally... */ rev_argc = 0; rev_argv[rev_argc++] = NULL; + rev_argv[rev_argc++] = "--no-merges"; rev_argv[rev_argc++] = "--left-right"; rev_argv[rev_argc++] = symmetric; rev_argv[rev_argc++] = "--"; @@ -1385,3 +1468,68 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base, num_ours, num_theirs); return 1; } + +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + struct ref ***local_tail = cb_data; + struct ref *ref; + int len; + + /* we already know it starts with refs/ to get here */ + if (check_ref_format(refname + 5)) + return 0; + + len = strlen(refname) + 1; + ref = xcalloc(1, sizeof(*ref) + len); + hashcpy(ref->new_sha1, sha1); + memcpy(ref->name, refname, len); + **local_tail = ref; + *local_tail = &ref->next; + return 0; +} + +struct ref *get_local_heads(void) +{ + struct ref *local_refs, **local_tail = &local_refs; + for_each_ref(one_local_ref, &local_tail); + return local_refs; +} + +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all) +{ + const struct ref *r; + struct ref *list = NULL; + struct ref **tail = &list; + + if (!head) + return NULL; + + /* + * Some transports support directly peeking at + * where HEAD points; if that is the case, then + * we don't have to guess. + */ + if (head->symref) + return copy_ref(find_ref_by_name(refs, head->symref)); + + /* If refs/heads/master could be right, it is. */ + if (!all) { + r = find_ref_by_name(refs, "refs/heads/master"); + if (r && !hashcmp(r->old_sha1, head->old_sha1)) + return copy_ref(r); + } + + /* Look for another ref that points there */ + for (r = refs; r; r = r->next) { + if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) { + *tail = copy_ref(r); + tail = &((*tail)->next); + if (!all) + break; + } + } + + return list; +} @@ -74,6 +74,7 @@ int check_ref_type(const struct ref *ref, int flags); void free_refs(struct ref *ref); int resolve_remote_symref(struct ref *ref, struct ref *list); +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1); /* * Removes and frees any duplicate refs in the map. @@ -137,4 +138,15 @@ enum match_refs_flags { int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); int format_tracking_info(struct branch *branch, struct strbuf *sb); +struct ref *get_local_heads(void); +/* + * Find refs from a list which are likely to be pointed to by the given HEAD + * ref. If 'all' is false, returns the most likely ref; otherwise, returns a + * list of all candidate refs. If no match is found (or 'head' is NULL), + * returns NULL. All returns are newly allocated and should be freed. + */ +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all); + #endif @@ -12,15 +12,15 @@ static int rerere_autoupdate; static char *merge_rr_path; -static const char *rr_path(const char *name, const char *file) +const char *rerere_path(const char *hex, const char *file) { - return git_path("rr-cache/%s/%s", name, file); + return git_path("rr-cache/%s/%s", hex, file); } -static int has_resolution(const char *name) +int has_rerere_resolution(const char *hex) { struct stat st; - return !stat(rr_path(name, "postimage"), &st); + return !stat(rerere_path(hex, "postimage"), &st); } static void read_rr(struct string_list *rr) @@ -208,12 +208,12 @@ static int merge(const char *name, const char *path) mmbuffer_t result = {NULL, 0}; xpparam_t xpp = {XDF_NEED_MINIMAL}; - if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0) + if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0) return 1; - if (read_mmfile(&cur, rr_path(name, "thisimage")) || - read_mmfile(&base, rr_path(name, "preimage")) || - read_mmfile(&other, rr_path(name, "postimage"))) + if (read_mmfile(&cur, rerere_path(name, "thisimage")) || + read_mmfile(&base, rerere_path(name, "preimage")) || + read_mmfile(&other, rerere_path(name, "postimage"))) return 1; ret = xdl_merge(&base, &cur, "", &other, "", &xpp, XDL_MERGE_ZEALOUS, &result); @@ -291,7 +291,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) string_list_insert(path, rr)->util = hex; if (mkdir(git_path("rr-cache/%s", hex), 0755)) continue; - handle_file(path, NULL, rr_path(hex, "preimage")); + handle_file(path, NULL, rerere_path(hex, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } } @@ -307,7 +307,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) const char *path = rr->items[i].string; const char *name = (const char *)rr->items[i].util; - if (has_resolution(name)) { + if (has_rerere_resolution(name)) { if (!merge(name, path)) { if (rerere_autoupdate) string_list_insert(path, &update); @@ -326,7 +326,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) continue; fprintf(stderr, "Recorded resolution for '%s'.\n", path); - copy_file(rr_path(name, "postimage"), path, 0666); + copy_file(rerere_path(name, "postimage"), path, 0666); mark_resolved: rr->items[i].util = NULL; } @@ -5,5 +5,7 @@ extern int setup_rerere(struct string_list *); extern int rerere(void); +extern const char *rerere_path(const char *hex, const char *file); +extern int has_rerere_resolution(const char *hex); #endif diff --git a/revision.c b/revision.c index 286e416b75..b6215cc72c 100644 --- a/revision.c +++ b/revision.c @@ -994,16 +994,6 @@ static void add_message_grep(struct rev_info *revs, const char *pattern) add_grep(revs, pattern, GREP_PATTERN_BODY); } -static void add_ignore_packed(struct rev_info *revs, const char *name) -{ - int num = ++revs->num_ignore_packed; - - revs->ignore_packed = xrealloc(revs->ignore_packed, - sizeof(const char *) * (num + 1)); - revs->ignore_packed[num-1] = name; - revs->ignore_packed[num] = NULL; -} - static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, int *unkc, const char **unkv) { @@ -1116,12 +1106,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->edge_hint = 1; } else if (!strcmp(arg, "--unpacked")) { revs->unpacked = 1; - free(revs->ignore_packed); - revs->ignore_packed = NULL; - revs->num_ignore_packed = 0; } else if (!prefixcmp(arg, "--unpacked=")) { - revs->unpacked = 1; - add_ignore_packed(revs, arg+11); + die("--unpacked=<packfile> no longer supported."); } else if (!strcmp(arg, "-r")) { revs->diff = 1; DIFF_OPT_SET(&revs->diffopt, RECURSIVE); @@ -1144,9 +1130,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--pretty")) { revs->verbose_header = 1; get_commit_format(arg+8, revs); - } else if (!prefixcmp(arg, "--pretty=")) { + } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) { revs->verbose_header = 1; get_commit_format(arg+9, revs); + } else if (!strcmp(arg, "--oneline")) { + revs->verbose_header = 1; + get_commit_format("oneline", revs); + revs->abbrev_commit = 1; } else if (!strcmp(arg, "--graph")) { revs->topo_order = 1; revs->rewrite_parents = 1; @@ -1685,7 +1675,7 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) { if (commit->object.flags & SHOWN) return commit_ignore; - if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed)) + if (revs->unpacked && has_sha1_pack(commit->object.sha1)) return commit_ignore; if (revs->show_all) return commit_show; diff --git a/revision.h b/revision.h index 7cf848771b..6e98b71b49 100644 --- a/revision.h +++ b/revision.h @@ -49,7 +49,7 @@ struct rev_info { blob_objects:1, edge_hint:1, limited:1, - unpacked:1, /* see also ignore_packed below */ + unpacked:1, boundary:2, left_right:1, rewrite_parents:1, @@ -80,16 +80,13 @@ struct rev_info { missing_newline:1; enum date_mode date_mode; - const char **ignore_packed; /* pretend objects in these are unpacked */ - int num_ignore_packed; - unsigned int abbrev; enum cmit_fmt commit_format; struct log_info *loginfo; int nr, total; const char *mime_boundary; char *message_id; - const char *ref_message_id; + struct string_list *ref_message_ids; const char *add_signoff; const char *extra_headers; const char *log_reencode; diff --git a/send-pack.h b/send-pack.h index 8ff1dc3539..83d76c7e35 100644 --- a/send-pack.h +++ b/send-pack.h @@ -2,9 +2,7 @@ #define SEND_PACK_H struct send_pack_args { - const char *receivepack; unsigned verbose:1, - send_all:1, send_mirror:1, force_update:1, use_thin_pack:1, @@ -12,7 +10,7 @@ struct send_pack_args { }; int send_pack(struct send_pack_args *args, - const char *dest, struct remote *remote, - int nr_heads, const char **heads); + int fd[], struct child_process *conn, + struct ref *remote_refs, struct extra_have_objects *extra_have); #endif diff --git a/sha1_file.c b/sha1_file.c index a07aa4e5c4..37e833b77d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1919,25 +1919,7 @@ off_t find_pack_entry_one(const unsigned char *sha1, return 0; } -int matches_pack_name(struct packed_git *p, const char *name) -{ - const char *last_c, *c; - - if (!strcmp(p->pack_name, name)) - return 1; - - for (c = p->pack_name, last_c = c; *c;) - if (*c == '/') - last_c = ++c; - else - ++c; - if (!strcmp(last_c, name)) - return 1; - - return 0; -} - -static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed) +static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) { static struct packed_git *last_found = (void *)1; struct packed_git *p; @@ -1949,15 +1931,6 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons p = (last_found == (void *)1) ? packed_git : last_found; do { - if (ignore_packed) { - const char **ig; - for (ig = ignore_packed; *ig; ig++) - if (matches_pack_name(p, *ig)) - break; - if (*ig) - goto next; - } - if (p->num_bad_objects) { unsigned i; for (i = 0; i < p->num_bad_objects; i++) @@ -2038,7 +2011,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) struct pack_entry e; int status; - if (!find_pack_entry(sha1, &e, NULL)) { + if (!find_pack_entry(sha1, &e)) { /* Most likely it's a loose object. */ status = sha1_loose_object_info(sha1, sizep); if (status >= 0) @@ -2046,7 +2019,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) /* Not a loose object; someone else may have just packed it. */ reprepare_packed_git(); - if (!find_pack_entry(sha1, &e, NULL)) + if (!find_pack_entry(sha1, &e)) return status; } @@ -2065,7 +2038,7 @@ static void *read_packed_sha1(const unsigned char *sha1, struct pack_entry e; void *data; - if (!find_pack_entry(sha1, &e, NULL)) + if (!find_pack_entry(sha1, &e)) return NULL; data = cache_or_unpack_entry(e.p, e.offset, size, type, 1); if (!data) { @@ -2464,17 +2437,17 @@ int has_pack_file(const unsigned char *sha1) return 1; } -int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed) +int has_sha1_pack(const unsigned char *sha1) { struct pack_entry e; - return find_pack_entry(sha1, &e, ignore_packed); + return find_pack_entry(sha1, &e); } int has_sha1_file(const unsigned char *sha1) { struct pack_entry e; - if (find_pack_entry(sha1, &e, NULL)) + if (find_pack_entry(sha1, &e)) return 1; return has_loose_object(sha1); } diff --git a/sideband.c b/sideband.c index cca3360546..899b1ff366 100644 --- a/sideband.c +++ b/sideband.c @@ -19,7 +19,7 @@ #define FIX_SIZE 10 /* large enough for any of the above */ -int recv_sideband(const char *me, int in_stream, int out, int err) +int recv_sideband(const char *me, int in_stream, int out) { unsigned pf = strlen(PREFIX); unsigned sf; @@ -41,8 +41,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err) if (len == 0) break; if (len < 1) { - len = sprintf(buf, "%s: protocol error: no band designator\n", me); - safe_write(err, buf, len); + fprintf(stderr, "%s: protocol error: no band designator\n", me); return SIDEBAND_PROTOCOL_ERROR; } band = buf[pf] & 0xff; @@ -50,8 +49,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err) switch (band) { case 3: buf[pf] = ' '; - buf[pf+1+len] = '\n'; - safe_write(err, buf, pf+1+len+1); + buf[pf+1+len] = '\0'; + fprintf(stderr, "%s\n", buf); return SIDEBAND_REMOTE_ERROR; case 2: buf[pf] = ' '; @@ -95,12 +94,12 @@ int recv_sideband(const char *me, int in_stream, int out, int err) memcpy(save, b + brk, sf); b[brk + sf - 1] = b[brk - 1]; memcpy(b + brk - 1, suffix, sf); - safe_write(err, b, brk + sf); + fprintf(stderr, "%.*s", brk + sf, b); memcpy(b + brk, save, sf); len -= brk; } else { int l = brk ? brk : len; - safe_write(err, b, l); + fprintf(stderr, "%.*s", l, b); len -= l; } @@ -112,10 +111,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err) safe_write(out, buf + pf+1, len); continue; default: - len = sprintf(buf, - "%s: protocol error: bad band #%d\n", - me, band); - safe_write(err, buf, len); + fprintf(stderr, "%s: protocol error: bad band #%d\n", + me, band); return SIDEBAND_PROTOCOL_ERROR; } } diff --git a/sideband.h b/sideband.h index a84b6917c7..d72db35d1e 100644 --- a/sideband.h +++ b/sideband.h @@ -7,7 +7,7 @@ #define DEFAULT_PACKET_MAX 1000 #define LARGE_PACKET_MAX 65520 -int recv_sideband(const char *me, int in_stream, int out, int err); +int recv_sideband(const char *me, int in_stream, int out); ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); #endif @@ -139,14 +139,11 @@ void strbuf_list_free(struct strbuf **sbs) int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) { - int cmp; - if (a->len < b->len) { - cmp = memcmp(a->buf, b->buf, a->len); - return cmp ? cmp : -1; - } else { - cmp = memcmp(a->buf, b->buf, b->len); - return cmp ? cmp : a->len != b->len; - } + int len = a->len < b->len ? a->len: b->len; + int cmp = memcmp(a->buf, b->buf, len); + if (cmp) + return cmp; + return a->len < b->len ? -1: a->len != b->len; } void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, diff --git a/string-list.c b/string-list.c index 15e14cf47a..1ac536e638 100644 --- a/string-list.c +++ b/string-list.c @@ -92,6 +92,16 @@ struct string_list_item *string_list_lookup(const char *string, struct string_li return list->items + i; } +int for_each_string_list(string_list_each_func_t fn, + struct string_list *list, void *cb_data) +{ + int i, ret = 0; + for (i = 0; i < list->nr; i++) + if ((ret = fn(&list->items[i], cb_data))) + break; + return ret; +} + void string_list_clear(struct string_list *list, int free_util) { if (list->items) { diff --git a/string-list.h b/string-list.h index d32ba05202..14bbc477de 100644 --- a/string-list.h +++ b/string-list.h @@ -20,6 +20,11 @@ void string_list_clear(struct string_list *list, int free_util); typedef void (*string_list_clear_func_t)(void *p, const char *str); void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc); +/* Use this function to iterate over each item */ +typedef int (*string_list_each_func_t)(struct string_list_item *, void *); +int for_each_string_list(string_list_each_func_t, + struct string_list *list, void *cb_data); + /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); int string_list_find_insert_index(const struct string_list *list, const char *string, diff --git a/symlinks.c b/symlinks.c index f262b7c44b..1d6b35b858 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,51 +1,54 @@ #include "cache.h" -static struct cache_def { - char path[PATH_MAX + 1]; - int len; - int flags; - int track_flags; - int prefix_len_stat_func; -} cache; - /* * Returns the length (on a path component basis) of the longest - * common prefix match of 'name' and the cached path string. + * common prefix match of 'name_a' and 'name_b'. */ -static inline int longest_match_lstat_cache(int len, const char *name, - int *previous_slash) +static int longest_path_match(const char *name_a, int len_a, + const char *name_b, int len_b, + int *previous_slash) { 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] == '/') { + max_len = len_a < len_b ? len_a : len_b; + while (i < max_len && name_a[i] == name_b[i]) { + if (name_a[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)) { + /* + * Is 'name_b' a substring of 'name_a', the other way around, + * or is 'name_a' and 'name_b' the exact same string? + */ + if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') || + (len_a < len_b && name_b[len_a] == '/') || + (len_a == len_b))) { match_len_prev = match_len; - match_len = len; + match_len = i; } *previous_slash = match_len_prev; return match_len; } -static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func) +static struct cache_def { + char path[PATH_MAX + 1]; + int len; + int flags; + int track_flags; + int prefix_len_stat_func; +} cache; + +static inline void reset_lstat_cache(void) { cache.path[0] = '\0'; cache.len = 0; cache.flags = 0; - cache.track_flags = track_flags; - cache.prefix_len_stat_func = prefix_len_stat_func; + /* + * The track_flags and prefix_len_stat_func members is only + * set by the safeguard rule inside lstat_cache() + */ } #define FL_DIR (1 << 0) @@ -67,7 +70,7 @@ static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func) * 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, +static int lstat_cache(const char *name, int len, int track_flags, int prefix_len_stat_func) { int match_len, last_slash, last_slash_dir, previous_slash; @@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name, 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. + * As a safeguard rule 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); + reset_lstat_cache(); + cache.track_flags = track_flags; + cache.prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; } else { /* @@ -89,7 +94,8 @@ static int lstat_cache(int len, const char *name, * the 2 "excluding" path types. */ match_len = last_slash = - longest_match_lstat_cache(len, name, &previous_slash); + longest_path_match(name, len, cache.path, cache.len, + &previous_slash); match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); if (match_flags && match_len == cache.len) return match_flags; @@ -153,7 +159,7 @@ static int lstat_cache(int len, const char *name, cache.path[last_slash] = '\0'; cache.len = last_slash; cache.flags = save_flags; - } else if (track_flags & FL_DIR && + } else if ((track_flags & FL_DIR) && last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { /* * We have a separate test for the directory case, @@ -170,7 +176,7 @@ static int lstat_cache(int len, const char *name, cache.len = last_slash_dir; cache.flags = FL_DIR; } else { - reset_lstat_cache(track_flags, prefix_len_stat_func); + reset_lstat_cache(); } return ret_flags; } @@ -179,19 +185,19 @@ static int lstat_cache(int len, const char *name, * Invalidate the given 'name' from the cache, if 'name' matches * completely with the cache. */ -void invalidate_lstat_cache(int len, const char *name) +void invalidate_lstat_cache(const char *name, int len) { int match_len, previous_slash; - match_len = longest_match_lstat_cache(len, name, &previous_slash); + match_len = longest_path_match(name, len, cache.path, cache.len, + &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); + reset_lstat_cache(); } } @@ -200,7 +206,7 @@ void invalidate_lstat_cache(int len, const char *name) */ void clear_lstat_cache(void) { - reset_lstat_cache(0, 0); + reset_lstat_cache(); } #define USE_ONLY_LSTAT 0 @@ -208,9 +214,9 @@ void clear_lstat_cache(void) /* * Return non-zero if path 'name' has a leading symlink component */ -int has_symlink_leading_path(int len, const char *name) +int has_symlink_leading_path(const char *name, int len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; } @@ -219,9 +225,9 @@ int has_symlink_leading_path(int len, const char *name) * 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) +int has_symlink_or_noent_leading_path(const char *name, int len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & (FL_SYMLINK|FL_NOENT); } @@ -233,9 +239,68 @@ int has_symlink_or_noent_leading_path(int len, const char *name) * '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) +int has_dirs_only_path(const char *name, int len, int prefix_len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; } + +static struct removal_def { + char path[PATH_MAX]; + int len; +} removal; + +static void do_remove_scheduled_dirs(int new_len) +{ + while (removal.len > new_len) { + removal.path[removal.len] = '\0'; + if (rmdir(removal.path)) + break; + do { + removal.len--; + } while (removal.len > new_len && + removal.path[removal.len] != '/'); + } + removal.len = new_len; + return; +} + +void schedule_dir_for_removal(const char *name, int len) +{ + int match_len, last_slash, i, previous_slash; + + match_len = last_slash = i = + longest_path_match(name, len, removal.path, removal.len, + &previous_slash); + /* Find last slash inside 'name' */ + while (i < len) { + if (name[i] == '/') + last_slash = i; + i++; + } + + /* + * If we are about to go down the directory tree, we check if + * we must first go upwards the tree, such that we then can + * remove possible empty directories as we go upwards. + */ + if (match_len < last_slash && match_len < removal.len) + do_remove_scheduled_dirs(match_len); + /* + * If we go deeper down the directory tree, we only need to + * save the new path components as we go down. + */ + if (match_len < last_slash) { + memcpy(&removal.path[match_len], &name[match_len], + last_slash - match_len); + removal.len = last_slash; + } + return; +} + +void remove_scheduled_dirs(void) +{ + do_remove_scheduled_dirs(0); + return; +} diff --git a/t/Makefile b/t/Makefile index 9149373032..bf816fc850 100644 --- a/t/Makefile +++ b/t/Makefile @@ -38,4 +38,7 @@ full-svn-test: $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8 -.PHONY: pre-clean $(T) aggregate-results clean +valgrind: + GIT_TEST_OPTS=--valgrind $(MAKE) + +.PHONY: pre-clean $(T) aggregate-results clean valgrind @@ -39,7 +39,8 @@ this: * passed all 3 test(s) You can pass --verbose (or -v), --debug (or -d), and --immediate -(or -i) command line argument to the test. +(or -i) command line argument to the test, or by setting GIT_TEST_OPTS +appropriately before running "make". --verbose:: This makes the test more verbose. Specifically, the @@ -58,6 +59,21 @@ You can pass --verbose (or -v), --debug (or -d), and --immediate This causes additional long-running tests to be run (where available), for more exhaustive testing. +--valgrind:: + Execute all Git binaries with valgrind and exit with status + 126 on errors (just like regular tests, this will only stop + the test script when running under -i). Valgrind errors + go to stderr, so you might want to pass the -v option, too. + + Since it makes no sense to run the tests with --valgrind and + not see any output, this option implies --verbose. For + convenience, it also implies --tee. + +--tee:: + In addition to printing the test output to the terminal, + write it to files named 't/test-results/$TEST_NAME.out'. + As the names depend on the tests' file names, it is safe to + run the tests with this option in parallel. Skipping Tests -------------- diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 67c431d4eb..cdd7ccdd2a 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -5,9 +5,8 @@ git_svn_id=git""-svn-id if test -n "$NO_SVN_TESTS" then - test_expect_success 'skipping git svn tests, NO_SVN_TESTS defined' : + say 'skipping git svn tests, NO_SVN_TESTS defined' test_done - exit fi GIT_DIR=$PWD/.git @@ -17,9 +16,8 @@ SVN_TREE=$GIT_SVN_DIR/svn-tree svn >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git svn tests, svn not found' : + say 'skipping git svn tests, svn not found' test_done - exit fi svnrepo=$PWD/svnrepo @@ -41,9 +39,8 @@ then else err='Perl SVN libraries not found or unusable, skipping test' fi - test_expect_success "$err" : + say "$err" test_done - exit fi rawsvnrepo="$svnrepo" @@ -144,7 +141,6 @@ require_svnserve () { then say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)' test_done - exit fi } diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 86cdebc727..cde659d14a 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -8,7 +8,6 @@ then say "skipping test, network testing disabled by default" say "(define GIT_TEST_HTTPD to enable)" test_done - exit fi HTTPD_PARA="" @@ -36,7 +35,6 @@ if ! test -x "$LIB_HTTPD_PATH" then say "skipping test, no web server found at '$LIB_HTTPD_PATH'" test_done - exit fi HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \ @@ -50,7 +48,6 @@ then then say "skipping test, at least Apache version 2 is required" test_done - exit fi LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH" @@ -94,13 +91,18 @@ prepare_httpd() { } start_httpd() { - prepare_httpd + prepare_httpd >&3 2>&4 trap 'stop_httpd; die' EXIT "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \ -f "$TEST_PATH/apache.conf" $HTTPD_PARA \ - -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start + -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \ + >&3 2>&4 + if ! test $? = 0; then + say "skipping test, web server setup failed" + test_done + fi } stop_httpd() { diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index af6e5e1d6a..21aa42f1c6 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -1,15 +1,13 @@ ServerName dummy +LockFile accept.lock PidFile httpd.pid DocumentRoot www LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog access.log common ErrorLog error.log - -<IfDefine Darwin> +<IfModule !mod_log_config.c> LoadModule log_config_module modules/mod_log_config.so - LockFile accept.lock - PidFile httpd.pid -</IfDefine> +</IfModule> <IfDefine SSL> LoadModule ssl_module modules/mod_ssl.so diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 70df15cbd8..f4ca4fc85c 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -57,6 +57,21 @@ test_expect_failure 'pretend we have a known breakage' ' test_expect_failure 'pretend we have fixed a known breakage' ' : ' +test_set_prereq HAVEIT +haveit=no +test_expect_success HAVEIT 'test runs if prerequisite is satisfied' ' + test_have_prereq HAVEIT && + haveit=yes +' +donthaveit=yes +test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' ' + donthaveit=no +' +if test $haveit$donthaveit != yesyes +then + say "bug in test framework: prerequisite tags do not work reliably" + exit 1 +fi ################################################################ # Basics of the basics @@ -100,12 +115,31 @@ test_expect_success \ 'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904' # Various types of objects +# Some filesystems do not support symblic links; on such systems +# some expected values are different mkdir path2 path3 path3/subp3 -for p in path0 path2/file2 path3/file3 path3/subp3/file3 +paths='path0 path2/file2 path3/file3 path3/subp3/file3' +for p in $paths do echo "hello $p" >$p - ln -s "hello $p" ${p}sym done +if test_have_prereq SYMLINKS +then + for p in $paths + do + ln -s "hello $p" ${p}sym + done + expectfilter=cat + expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b + expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3 + expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2 +else + expectfilter='grep -v sym' + expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46 + expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325 + expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f +fi + test_expect_success \ 'adding various types of objects with git update-index --add.' \ 'find path* ! -type d -print | xargs git update-index --add' @@ -115,7 +149,7 @@ test_expect_success \ 'showing stage with git ls-files --stage' \ 'git ls-files --stage >current' -cat >expected <<\EOF +$expectfilter >expected <<\EOF 100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0 path0sym 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0 path2/file2 @@ -127,14 +161,14 @@ cat >expected <<\EOF EOF test_expect_success \ 'validate git ls-files output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' test_expect_success \ 'writing tree out with git write-tree.' \ 'tree=$(git write-tree)' test_expect_success \ 'validate object ID for a known tree.' \ - 'test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b' + 'test "$tree" = "$expectedtree"' test_expect_success \ 'showing tree with git ls-tree' \ @@ -145,16 +179,16 @@ cat >expected <<\EOF 040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2 040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3 EOF -test_expect_success \ +test_expect_success SYMLINKS \ 'git ls-tree output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' # This changed in ls-tree pathspec change -- recursive does # not show tree nodes anymore. test_expect_success \ 'showing tree with git ls-tree -r' \ 'git ls-tree -r $tree >current' -cat >expected <<\EOF +$expectfilter >expected <<\EOF 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym 100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2 @@ -166,7 +200,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree -r output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' # But with -r -t we can have both. test_expect_success \ @@ -185,23 +219,23 @@ cat >expected <<\EOF 100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3 120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym EOF -test_expect_success \ +test_expect_success SYMLINKS \ 'git ls-tree -r output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' test_expect_success \ 'writing partial tree out with git write-tree --prefix.' \ 'ptree=$(git write-tree --prefix=path3)' test_expect_success \ 'validate object ID for a known tree.' \ - 'test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3' + 'test "$ptree" = "$expectedptree1"' test_expect_success \ 'writing partial tree out with git write-tree --prefix.' \ 'ptree=$(git write-tree --prefix=path3/subp3)' test_expect_success \ 'validate object ID for a known tree.' \ - 'test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2' + 'test "$ptree" = "$expectedptree2"' cat >badobjects <<EOF 100644 blob 1000000000000000000000000000000000000000 dir/file1 @@ -234,7 +268,7 @@ test_expect_success \ newtree=$(git write-tree) && test "$newtree" = "$tree"' -cat >expected <<\EOF +$expectfilter >expected <<\EOF :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M path0 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M path0sym :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M path2/file2 @@ -257,7 +291,7 @@ test_expect_success \ 'git diff-files >current && cmp -s current /dev/null' ################################################################ -P=087704a96baf1c2d1c869a8b084481e121c88b5b +P=$expectedtree test_expect_success \ 'git commit-tree records the correct tree in a commit.' \ 'commit0=$(echo NO | git commit-tree $P) && @@ -293,7 +327,7 @@ test_expect_success 'update-index D/F conflict' ' test $numpath0 = 1 ' -test_expect_success 'absolute path works as expected' ' +test_expect_success SYMLINKS 'absolute path works as expected' ' mkdir first && ln -s ../.git first/.git && mkdir second && diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh index 63e1217e71..2342ac5788 100755 --- a/t/t0004-unwritable.sh +++ b/t/t0004-unwritable.sh @@ -15,7 +15,7 @@ test_expect_success setup ' ' -test_expect_success 'write-tree should notice unwritable repository' ' +test_expect_success POSIXPERM 'write-tree should notice unwritable repository' ' ( chmod a-w .git/objects .git/objects/?? && @@ -27,7 +27,7 @@ test_expect_success 'write-tree should notice unwritable repository' ' ' -test_expect_success 'commit should notice unwritable repository' ' +test_expect_success POSIXPERM 'commit should notice unwritable repository' ' ( chmod a-w .git/objects .git/objects/?? && @@ -39,7 +39,7 @@ test_expect_success 'commit should notice unwritable repository' ' ' -test_expect_success 'update-index should notice unwritable repository' ' +test_expect_success POSIXPERM 'update-index should notice unwritable repository' ' ( echo 6O >file && @@ -52,7 +52,7 @@ test_expect_success 'update-index should notice unwritable repository' ' ' -test_expect_success 'add should notice unwritable repository' ' +test_expect_success POSIXPERM 'add should notice unwritable repository' ' ( echo b >file && diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index 1be7446d8d..4e72b53140 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -429,6 +429,37 @@ test_expect_success 'in-tree .gitattributes (4)' ' } ' +test_expect_success 'checkout with existing .gitattributes' ' + + git config core.autocrlf true && + git config --unset core.safecrlf && + echo ".file2 -crlfQ" | q_to_cr >> .gitattributes && + git add .gitattributes && + git commit -m initial && + echo ".file -crlfQ" | q_to_cr >> .gitattributes && + echo "contents" > .file && + git add .gitattributes .file && + git commit -m second && + + git checkout master~1 && + git checkout master && + test "$(git diff-files --raw)" = "" + +' + +test_expect_success 'checkout when deleting .gitattributes' ' + + git rm .gitattributes && + echo "contentsQ" | q_to_cr > .file2 && + git add .file2 && + git commit -m third + + git checkout master~1 && + git checkout master && + remove_cr .file2 >/dev/null + +' + test_expect_success 'invalid .gitattributes (must not crash)' ' echo "three +crlf" >>.gitattributes && diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh index e5330395fc..c7d0324374 100755 --- a/t/t0024-crlf-archive.sh +++ b/t/t0024-crlf-archive.sh @@ -28,12 +28,12 @@ test_expect_success 'tar archive' ' "$UNZIP" -v >/dev/null 2>&1 if [ $? -eq 127 ]; then - echo "Skipping ZIP test, because unzip was not found" - test_done - exit + say "Skipping ZIP test, because unzip was not found" +else + test_set_prereq UNZIP fi -test_expect_success 'zip archive' ' +test_expect_success UNZIP 'zip archive' ' git archive --format=zip HEAD >test.zip && diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 7edf49db3c..89282ccf7a 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -8,7 +8,9 @@ auml=`printf '\xc3\xa4'` aumlcdiar=`printf '\x61\xcc\x88'` case_insensitive= -test_expect_success 'see if we expect ' ' +unibad= +no_symlinks= +test_expect_success 'see what we expect' ' test_case=test_expect_success test_unicode=test_expect_success @@ -19,7 +21,6 @@ test_expect_success 'see if we expect ' ' then test_case=test_expect_failure case_insensitive=t - say "will test on a case insensitive filesystem" fi && rm -fr junk && mkdir junk && @@ -27,13 +28,26 @@ test_expect_success 'see if we expect ' ' case "$(cd junk && echo *)" in "$aumlcdiar") test_unicode=test_expect_failure - say "will test on a unicode corrupting filesystem" + unibad=t ;; *) ;; esac && - rm -fr junk + rm -fr junk && + { + ln -s x y 2> /dev/null && + test -h y 2> /dev/null || + no_symlinks=1 + rm -f y + } ' +test "$case_insensitive" && + say "will test on a case insensitive filesystem" +test "$unibad" && + say "will test on a unicode corrupting filesystem" +test "$no_symlinks" && + say "will test on a filesystem lacking symbolic links" + if test "$case_insensitive" then test_expect_success "detection of case insensitive filesystem during repo init" ' @@ -48,6 +62,21 @@ test_expect_success "detection of case insensitive filesystem during repo init" ' fi +if test "$no_symlinks" +then +test_expect_success "detection of filesystem w/o symlink support during repo init" ' + + v=$(git config --bool core.symlinks) && + test "$v" = false +' +else +test_expect_success "detection of filesystem w/o symlink support during repo init" ' + + test_must_fail git config --bool core.symlinks || + test "$(git config --bool core.symlinks)" = true +' +fi + test_expect_success "setup case tests" ' git config core.ignorecase true && diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh index b29c37a5a4..0c6ff567a1 100755 --- a/t/t0055-beyond-symlinks.sh +++ b/t/t0055-beyond-symlinks.sh @@ -4,7 +4,7 @@ test_description='update-index and add refuse to add beyond symlinks' . ./test-lib.sh -test_expect_success setup ' +test_expect_success SYMLINKS setup ' >a && mkdir b && ln -s b c && @@ -12,12 +12,12 @@ test_expect_success setup ' git update-index --add a b/d ' -test_expect_success 'update-index --add beyond symlinks' ' +test_expect_success SYMLINKS 'update-index --add beyond symlinks' ' test_must_fail git update-index --add c/d && ! ( git ls-files | grep c/d ) ' -test_expect_success 'add beyond symlinks' ' +test_expect_success SYMLINKS 'add beyond symlinks' ' test_must_fail git add c/d && ! ( git ls-files | grep c/d ) ' diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 8336114f98..53cf1f8dc4 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -7,41 +7,91 @@ test_description='Test various path utilities' . ./test-lib.sh -norm_abs() { - test_expect_success "normalize absolute: $1 => $2" \ +norm_path() { + test_expect_success $3 "normalize path: $1 => $2" \ "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'" } +# On Windows, we are using MSYS's bash, which mangles the paths. +# Absolute paths are anchored at the MSYS installation directory, +# which means that the path / accounts for this many characters: +rootoff=$(test-path-utils normalize_path_copy / | wc -c) +# Account for the trailing LF: +if test $rootoff = 2; then + rootoff= # we are on Unix +else + rootoff=$(($rootoff-1)) +fi + ancestor() { - test_expect_success "longest ancestor: $1 $2 => $3" \ - "test \"\$(test-path-utils longest_ancestor_length '$1' '$2')\" = '$3'" + # We do some math with the expected ancestor length. + expected=$3 + if test -n "$rootoff" && test "x$expected" != x-1; then + expected=$(($expected+$rootoff)) + fi + test_expect_success "longest ancestor: $1 $2 => $expected" \ + "actual=\$(test-path-utils longest_ancestor_length '$1' '$2') && + test \"\$actual\" = '$expected'" } -norm_abs "" "" -norm_abs / / -norm_abs // / -norm_abs /// / -norm_abs /. / -norm_abs /./ / -norm_abs /./.. ++failed++ -norm_abs /../. ++failed++ -norm_abs /./../.// ++failed++ -norm_abs /dir/.. / -norm_abs /dir/sub/../.. / -norm_abs /dir/sub/../../.. ++failed++ -norm_abs /dir /dir -norm_abs /dir// /dir/ -norm_abs /./dir /dir -norm_abs /dir/. /dir/ -norm_abs /dir///./ /dir/ -norm_abs /dir//sub/.. /dir/ -norm_abs /dir/sub/../ /dir/ -norm_abs //dir/sub/../. /dir/ -norm_abs /dir/s1/../s2/ /dir/s2/ -norm_abs /d1/s1///s2/..//../s3/ /d1/s3/ -norm_abs /d1/s1//../s2/../../d2 /d2 -norm_abs /d1/.../d2 /d1/.../d2 -norm_abs /d1/..././../d2 /d1/d2 +# Absolute path tests must be skipped on Windows because due to path mangling +# the test program never sees a POSIX-style absolute path +case $(uname -s) in +*MINGW*) + ;; +*) + test_set_prereq POSIX + ;; +esac + +norm_path "" "" +norm_path . "" +norm_path ./ "" +norm_path ./. "" +norm_path ./.. ++failed++ +norm_path ../. ++failed++ +norm_path ./../.// ++failed++ +norm_path dir/.. "" +norm_path dir/sub/../.. "" +norm_path dir/sub/../../.. ++failed++ +norm_path dir dir +norm_path dir// dir/ +norm_path ./dir dir +norm_path dir/. dir/ +norm_path dir///./ dir/ +norm_path dir//sub/.. dir/ +norm_path dir/sub/../ dir/ +norm_path dir/sub/../. dir/ +norm_path dir/s1/../s2/ dir/s2/ +norm_path d1/s1///s2/..//../s3/ d1/s3/ +norm_path d1/s1//../s2/../../d2 d2 +norm_path d1/.../d2 d1/.../d2 +norm_path d1/..././../d2 d1/d2 + +norm_path / / POSIX +norm_path // / POSIX +norm_path /// / POSIX +norm_path /. / POSIX +norm_path /./ / POSIX +norm_path /./.. ++failed++ POSIX +norm_path /../. ++failed++ POSIX +norm_path /./../.// ++failed++ POSIX +norm_path /dir/.. / POSIX +norm_path /dir/sub/../.. / POSIX +norm_path /dir/sub/../../.. ++failed++ POSIX +norm_path /dir /dir POSIX +norm_path /dir// /dir/ POSIX +norm_path /./dir /dir POSIX +norm_path /dir/. /dir/ POSIX +norm_path /dir///./ /dir/ POSIX +norm_path /dir//sub/.. /dir/ POSIX +norm_path /dir/sub/../ /dir/ POSIX +norm_path //dir/sub/../. /dir/ POSIX +norm_path /dir/s1/../s2/ /dir/s2/ POSIX +norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX +norm_path /d1/s1//../s2/../../d2 /d2 POSIX +norm_path /d1/.../d2 /d1/.../d2 POSIX +norm_path /d1/..././../d2 /d1/d2 POSIX ancestor / "" -1 ancestor / / -1 @@ -80,10 +130,10 @@ ancestor /foo/bar /:/foo:/bar/ 4 ancestor /foo/bar /foo:/:/bar/ 4 ancestor /foo/bar /:/bar/:/fo 0 ancestor /foo/bar /:/bar/ 0 -ancestor /foo/bar :://foo/. 4 -ancestor /foo/bar :://foo/.:: 4 -ancestor /foo/bar //foo/./::/bar 4 -ancestor /foo/bar ::/bar -1 +ancestor /foo/bar .:/foo/. 4 +ancestor /foo/bar .:/foo/.:.: 4 +ancestor /foo/bar /foo/./:.:/bar 4 +ancestor /foo/bar .:/bar -1 test_expect_success 'strip_path_suffix' ' test c:/msysgit = $(test-path-utils strip_path_suffix \ diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh index 570d3729bd..f19b4a2a4a 100755 --- a/t/t1004-read-tree-m-u-wf.sh +++ b/t/t1004-read-tree-m-u-wf.sh @@ -157,7 +157,7 @@ test_expect_success '3-way not overwriting local changes (their side)' ' ' -test_expect_success 'funny symlink in work tree' ' +test_expect_success SYMLINKS 'funny symlink in work tree' ' git reset --hard && git checkout -b sym-b side-b && @@ -177,7 +177,7 @@ test_expect_success 'funny symlink in work tree' ' ' -test_expect_success 'funny symlink in work tree, un-unlink-able' ' +test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' ' rm -fr a b && git reset --hard && @@ -189,7 +189,7 @@ test_expect_success 'funny symlink in work tree, un-unlink-able' ' ' # clean-up from the above test -chmod a+w a +chmod a+w a 2>/dev/null rm -fr a b test_expect_success 'D/F setup' ' diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index fc386ba033..210e594f6f 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -126,7 +126,7 @@ test_expect_success 'no file/rev ambiguity check inside a bare repo' ' cd foo.git && git show -s HEAD ' -test_expect_success 'detection should not be fooled by a symlink' ' +test_expect_success SYMLINKS 'detection should not be fooled by a symlink' ' cd "$HERE" && rm -fr foo.git && git clone -s .git another && diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh index 7f7fc36734..c4414ff576 100755 --- a/t/t1100-commit-tree-options.sh +++ b/t/t1100-commit-tree-options.sh @@ -40,6 +40,6 @@ test_expect_success \ test_expect_success \ 'compare commit' \ - 'diff expected commit' + 'test_cmp expected commit' test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 3c06842d99..43ea283242 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -118,7 +118,14 @@ EOF test_expect_success 'multiple unset is correct' 'cmp .git/config expect' -mv .git/config2 .git/config +cp .git/config2 .git/config + +test_expect_success '--replace-all missing value' ' + test_must_fail git config --replace-all beta.haha && + test_cmp .git/config2 .git/config +' + +rm .git/config2 test_expect_success '--replace-all' \ 'git config --replace-all beta.haha gamma' @@ -726,7 +733,7 @@ echo >>result test_expect_success '--null --get-regexp' 'cmp result expect' -test_expect_success 'symlinked configuration' ' +test_expect_success SYMLINKS 'symlinked configuration' ' ln -s notyet myconfig && GIT_CONFIG=myconfig git config test.frotz nitfol && diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index 653362ba22..dc4485409d 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -26,7 +26,7 @@ modebits () { for u in 002 022 do - test_expect_success "shared=1 does not clear bits preset by umask $u" ' + test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" ' mkdir sub && ( cd sub && umask $u && @@ -54,7 +54,7 @@ test_expect_success 'shared=all' ' test 2 = $(git config core.sharedrepository) ' -test_expect_success 'update-server-info honors core.sharedRepository' ' +test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' ' : > a1 && git add a1 && test_tick && @@ -85,7 +85,7 @@ do git config core.sharedrepository "$u" && umask 0277 && - test_expect_success "shared = $u ($y) ro" ' + test_expect_success POSIXPERM "shared = $u ($y) ro" ' rm -f .git/info/refs && git update-server-info && @@ -97,7 +97,7 @@ do ' umask 077 && - test_expect_success "shared = $u ($x) rw" ' + test_expect_success POSIXPERM "shared = $u ($x) rw" ' rm -f .git/info/refs && git update-server-info && @@ -111,7 +111,7 @@ do done -test_expect_success 'git reflog expire honors core.sharedRepository' ' +test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' ' git config core.sharedRepository group && git reflog expire --all && actual="$(ls -l .git/logs/refs/heads/master)" && diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index bd589268fc..54ba3df95f 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -137,7 +137,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 EOF test_expect_success \ "verifying $m's log" \ - "diff expect .git/logs/$m" + "test_cmp expect .git/logs/$m" rm -rf .git/$m .git/logs expect test_expect_success \ @@ -168,7 +168,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000 EOF test_expect_success \ "verifying $m's log" \ - 'diff expect .git/logs/$m' + 'test_cmp expect .git/logs/$m' rm -f .git/$m .git/logs/$m expect git update-ref $m $D @@ -272,7 +272,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 c EOF test_expect_success \ 'git commit logged updates' \ - "diff expect .git/logs/$m" + "test_cmp expect .git/logs/$m" unset h_TEST h_OTHER h_FIXED h_MERGED test_expect_success \ diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 5b24f05573..80af6b9b7e 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -70,9 +70,7 @@ test_expect_success setup ' E=`git rev-parse --verify HEAD:A/B/E` && check_fsck && - chmod +x C && - ( test "`git config --bool core.filemode`" != false || - echo executable >>C ) && + test_chmod +x C && git add C && test_tick && git commit -m dragon && L=`git rev-parse --verify HEAD` && diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh new file mode 100755 index 0000000000..c18ed8edf9 --- /dev/null +++ b/t/t1411-reflog-show.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +test_description='Test reflog display routines' +. ./test-lib.sh + +test_expect_success 'setup' ' + echo content >file && + git add file && + test_tick && + git commit -m one +' + +cat >expect <<'EOF' +Reflog: HEAD@{0} (C O Mitter <committer@example.com>) +Reflog message: commit (initial): one +EOF +test_expect_success 'log -g shows reflog headers' ' + git log -g -1 >tmp && + grep ^Reflog <tmp >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +e46513e HEAD@{0}: commit (initial): one +EOF +test_expect_success 'oneline reflog format' ' + git log -g -1 --oneline >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>) +Reflog message: commit (initial): one +EOF +test_expect_success 'using @{now} syntax shows reflog date (multiline)' ' + git log -g -1 HEAD@{now} >tmp && + grep ^Reflog <tmp >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one +EOF +test_expect_success 'using @{now} syntax shows reflog date (oneline)' ' + git log -g -1 --oneline HEAD@{now} >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>) +Reflog message: commit (initial): one +EOF +test_expect_success 'using --date= shows reflog date (multiline)' ' + git log -g -1 --date=raw >tmp && + grep ^Reflog <tmp >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +e46513e HEAD@{1112911993 -0700}: commit (initial): one +EOF +test_expect_success 'using --date= shows reflog date (oneline)' ' + git log -g -1 --oneline --date=raw >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 4597af0eb6..a22632f483 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -28,4 +28,71 @@ test_expect_success 'loose objects borrowed from alternate are not missing' ' ) ' +# Corruption tests follow. Make sure to remove all traces of the +# specific corruption you test afterwards, lest a later test trip over +# it. + +test_expect_success 'object with bad sha1' ' + sha=$(echo blob | git hash-object -w --stdin) && + echo $sha && + old=$(echo $sha | sed "s+^..+&/+") && + new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff && + sha="$(dirname $new)$(basename $new)" + mv .git/objects/$old .git/objects/$new && + git update-index --add --cacheinfo 100644 $sha foo && + tree=$(git write-tree) && + cmt=$(echo bogus | git commit-tree $tree) && + git update-ref refs/heads/bogus $cmt && + (git fsck 2>out; true) && + grep "$sha.*corrupt" out && + rm -f .git/objects/$new && + git update-ref -d refs/heads/bogus && + git read-tree -u --reset HEAD +' + +test_expect_success 'branch pointing to non-commit' ' + git rev-parse HEAD^{tree} > .git/refs/heads/invalid && + git fsck 2>out && + grep "not a commit" out && + git update-ref -d refs/heads/invalid +' + +cat > invalid-tag <<EOF +object ffffffffffffffffffffffffffffffffffffffff +type commit +tag invalid +tagger T A Gger <tagger@example.com> 1234567890 -0000 + +This is an invalid tag. +EOF + +test_expect_failure 'tag pointing to nonexistent' ' + tag=$(git hash-object -w --stdin < invalid-tag) && + echo $tag > .git/refs/tags/invalid && + git fsck --tags 2>out && + cat out && + grep "could not load tagged object" out && + rm .git/refs/tags/invalid +' + +cat > wrong-tag <<EOF +object $(echo blob | git hash-object -w --stdin) +type commit +tag wrong +tagger T A Gger <tagger@example.com> 1234567890 -0000 + +This is an invalid tag. +EOF + +test_expect_failure 'tag pointing to something else than its type' ' + tag=$(git hash-object -w --stdin < wrong-tag) && + echo $tag > .git/refs/tags/wrong && + git fsck --tags 2>out && + cat out && + grep "some sane error message" out && + rm .git/refs/tags/wrong +' + + + test_done diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh index e377d48902..df5ad8c686 100755 --- a/t/t1504-ceiling-dirs.sh +++ b/t/t1504-ceiling-dirs.sh @@ -13,7 +13,7 @@ test_fail() { "git rev-parse --show-prefix" } -TRASH_ROOT="$(pwd)" +TRASH_ROOT="$PWD" ROOT_PARENT=$(dirname "$TRASH_ROOT") diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh index ef007532b1..98aa73e823 100755 --- a/t/t2001-checkout-cache-clash.sh +++ b/t/t2001-checkout-cache-clash.sh @@ -59,10 +59,10 @@ test_expect_success \ 'git read-tree -m $tree1 && git checkout-index -f -a' test_debug 'show_files $tree1' -ln -s path0 path1 -test_expect_success \ +test_expect_success SYMLINKS \ 'git update-index --add a symlink.' \ - 'git update-index --add path1' + 'ln -s path0 path1 && + git update-index --add path1' test_expect_success \ 'writing tree out with git write-tree' \ 'tree3=$(git write-tree)' diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh index 71894b3743..02a4fc5d36 100755 --- a/t/t2003-checkout-cache-mkdir.sh +++ b/t/t2003-checkout-cache-mkdir.sh @@ -19,7 +19,7 @@ test_expect_success \ echo rezrov >path1/file1 && git update-index --add path0 path1/file1' -test_expect_success \ +test_expect_success SYMLINKS \ 'have symlink in place where dir is expected.' \ 'rm -fr path0 path1 && mkdir path2 && @@ -59,7 +59,7 @@ test_expect_success \ test ! -f path1/file1' # Linus fix #1 -test_expect_success \ +test_expect_success SYMLINKS \ 'use --prefix=tmp/orary/ where tmp is a symlink' \ 'rm -fr path0 path1 path2 tmp* && mkdir tmp1 tmp1/orary && @@ -71,7 +71,7 @@ test_expect_success \ test -h tmp' # Linus fix #2 -test_expect_success \ +test_expect_success SYMLINKS \ 'use --prefix=tmp/orary- where tmp is a symlink' \ 'rm -fr path0 path1 path2 tmp* && mkdir tmp1 && @@ -82,7 +82,7 @@ test_expect_success \ test -h tmp' # Linus fix #3 -test_expect_success \ +test_expect_success SYMLINKS \ 'use --prefix=tmp- where tmp-path1 is a symlink' \ 'rm -fr path0 path1 path2 tmp* && mkdir tmp1 && diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh index 39133b8c7a..36cca14d95 100755 --- a/t/t2004-checkout-cache-temp.sh +++ b/t/t2004-checkout-cache-temp.sh @@ -194,7 +194,7 @@ test_expect_success \ test $(cat ../$s1) = tree1asubdir/path5) )' -test_expect_success \ +test_expect_success SYMLINKS \ 'checkout --temp symlink' ' rm -f path* .merge_* out .git/index && ln -s b a && diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh index 0526fce163..20f33436d0 100755 --- a/t/t2007-checkout-symlink.sh +++ b/t/t2007-checkout-symlink.sh @@ -6,6 +6,12 @@ test_description='git checkout to switch between branches with symlink<->dir' . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say "symbolic links not supported - skipping tests" + test_done +fi + test_expect_success setup ' mkdir frotz && diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh index 6ef2dcfd8a..2df3fdde8b 100755 --- a/t/t2100-update-cache-badpath.sh +++ b/t/t2100-update-cache-badpath.sh @@ -26,7 +26,12 @@ All of the attempts should fail. mkdir path2 path3 date >path0 -ln -s xyzzy path1 +if test_have_prereq SYMLINKS +then + ln -s xyzzy path1 +else + date > path1 +fi date >path2/file2 date >path3/file3 @@ -38,7 +43,12 @@ rm -fr path? mkdir path0 path1 date >path2 -ln -s frotz path3 +if test_have_prereq SYMLINKS +then + ln -s frotz path3 +else + date > path3 +fi date >path0/file0 date >path1/file1 diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index b2ddf5ace3..912075063b 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -80,7 +80,7 @@ test_expect_success 'change gets noticed' ' ' -test_expect_success 'replace a file with a symlink' ' +test_expect_success SYMLINKS 'replace a file with a symlink' ' rm foo && ln -s top foo && @@ -150,7 +150,7 @@ test_expect_success 'add -u resolves unmerged paths' ' echo 2 >path3 && echo 2 >path5 && git add -u && - git ls-files -s "path?" >actual && + git ls-files -s path1 path2 path3 path4 path5 path6 >actual && { echo "100644 $three 0 path1" echo "100644 $one 1 path3" diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh index d24c7d9e5f..2e8f702452 100755 --- a/t/t2201-add-update-typechange.sh +++ b/t/t2201-add-update-typechange.sh @@ -11,7 +11,13 @@ test_expect_success setup ' _empty=$(git hash-object --stdin <xyzzy) && >yomin && >caskly && - ln -s frotz nitfol && + if test_have_prereq SYMLINKS; then + ln -s frotz nitfol && + T_letter=T + else + printf %s frotz > nitfol && + T_letter=M + fi && mkdir rezrov && >rezrov/bozbar && git add caskly xyzzy yomin nitfol rezrov/bozbar && @@ -29,7 +35,11 @@ test_expect_success modify ' >nitfol && # rezrov/bozbar disappears rm -fr rezrov && - ln -s xyzzy rezrov && + if test_have_prereq SYMLINKS; then + ln -s xyzzy rezrov + else + printf %s xyzzy > rezrov + fi && # xyzzy disappears (not a submodule) mkdir xyzzy && echo gnusto >xyzzy/bozbar && @@ -71,7 +81,7 @@ test_expect_success modify ' s/blob/000000/ } / nitfol/{ - s/ nitfol/ $_z40 T&/ + s/ nitfol/ $_z40 $T_letter&/ s/blob/100644/ } / rezrov.bozbar/{ diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh index 293dc353b1..3b01ad2e4d 100755 --- a/t/t2300-cd-to-toplevel.sh +++ b/t/t2300-cd-to-toplevel.sh @@ -5,7 +5,7 @@ test_description='cd_to_toplevel' . ./test-lib.sh test_cd_to_toplevel () { - test_expect_success "$2" ' + test_expect_success $3 "$2" ' ( cd '"'$1'"' && . git-sh-setup && @@ -24,14 +24,14 @@ test_cd_to_toplevel repo 'at physical root' test_cd_to_toplevel repo/sub/dir 'at physical subdir' -ln -s repo symrepo -test_cd_to_toplevel symrepo 'at symbolic root' +ln -s repo symrepo 2>/dev/null +test_cd_to_toplevel symrepo 'at symbolic root' SYMLINKS -ln -s repo/sub/dir subdir-link -test_cd_to_toplevel subdir-link 'at symbolic subdir' +ln -s repo/sub/dir subdir-link 2>/dev/null +test_cd_to_toplevel subdir-link 'at symbolic subdir' SYMLINKS cd repo -ln -s sub/dir internal-link -test_cd_to_toplevel internal-link 'at internal symbolic subdir' +ln -s sub/dir internal-link 2>/dev/null +test_cd_to_toplevel internal-link 'at internal symbolic subdir' SYMLINKS test_done diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index bc0a351392..86291e8399 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -13,12 +13,18 @@ filesystem. path2/file2 - a file in a directory path3-junk - a file to confuse things path3/file3 - a file in a directory + path4 - an empty directory ' . ./test-lib.sh date >path0 -ln -s xyzzy path1 -mkdir path2 path3 +if test_have_prereq SYMLINKS +then + ln -s xyzzy path1 +else + date > path1 +fi +mkdir path2 path3 path4 date >path2/file2 date >path2-junk date >path3/file3 @@ -28,6 +34,7 @@ git update-index --add path3-junk path3/file3 cat >expected1 <<EOF expected1 expected2 +expected3 output path0 path1 @@ -35,6 +42,8 @@ path2-junk path2/file2 EOF sed -e 's|path2/file2|path2/|' <expected1 >expected2 +cat <expected2 >expected3 +echo path4/ >>expected2 test_expect_success \ 'git ls-files --others to show output.' \ @@ -42,7 +51,7 @@ test_expect_success \ test_expect_success \ 'git ls-files --others should pick up symlinks.' \ - 'diff output expected1' + 'test_cmp expected1 output' test_expect_success \ 'git ls-files --others --directory to show output.' \ @@ -51,6 +60,14 @@ test_expect_success \ test_expect_success \ 'git ls-files --others --directory should not get confused.' \ - 'diff output expected2' + 'test_cmp expected2 output' + +test_expect_success \ + 'git ls-files --others --directory --no-empty-directory to show output.' \ + 'git ls-files --others --directory --no-empty-directory >output' + +test_expect_success \ + '--no-empty-directory hides empty directory' \ + 'test_cmp expected3 output' test_done diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh index ec14040637..95671c2053 100755 --- a/t/t3010-ls-files-killed-modified.sh +++ b/t/t3010-ls-files-killed-modified.sh @@ -38,7 +38,12 @@ modified without reporting path9 and path10. . ./test-lib.sh date >path0 -ln -s xyzzy path1 +if test_have_prereq SYMLINKS +then + ln -s xyzzy path1 +else + date > path1 +fi mkdir path2 path3 date >path2/file2 date >path3/file3 @@ -52,8 +57,14 @@ test_expect_success \ rm -fr path? ;# leave path10 alone date >path2 -ln -s frotz path3 -ln -s nitfol path5 +if test_have_prereq SYMLINKS +then + ln -s frotz path3 + ln -s nitfol path5 +else + date > path3 + date > path5 +fi mkdir path0 path1 path6 date >path0/file0 date >path1/file1 @@ -75,7 +86,7 @@ EOF test_expect_success \ 'validate git ls-files -k output.' \ - 'diff .output .expected' + 'test_cmp .expected .output' test_expect_success \ 'git ls-files -m to show modified files.' \ @@ -91,6 +102,6 @@ EOF test_expect_success \ 'validate git ls-files -m output.' \ - 'diff .output .expected' + 'test_cmp .expected .output' test_done diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index 6e6a2542a2..ee60d03fe8 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -22,9 +22,21 @@ test_expect_success \ 'setup' \ 'mkdir path2 path2/baz && echo Hi >path0 && - ln -s path0 path1 && + if test_have_prereq SYMLINKS + then + ln -s path0 path1 && + ln -s ../path1 path2/bazbo + make_expected () { + cat >expected + } + else + printf path0 > path1 && + printf ../path1 > path2/bazbo + make_expected () { + sed -e "s/120000 /100644 /" >expected + } + fi && echo Lo >path2/foo && - ln -s ../path1 path2/bazbo && echo Mi >path2/baz/b && find path? \( -type f -o -type l \) -print | xargs git update-index --add && @@ -41,7 +53,7 @@ test_output () { test_expect_success \ 'ls-tree plain' \ 'git ls-tree $tree >current && - cat >expected <<\EOF && + make_expected <<\EOF && 100644 blob X path0 120000 blob X path1 040000 tree X path2 @@ -51,7 +63,7 @@ EOF test_expect_success \ 'ls-tree recursive' \ 'git ls-tree -r $tree >current && - cat >expected <<\EOF && + make_expected <<\EOF && 100644 blob X path0 120000 blob X path1 100644 blob X path2/baz/b @@ -63,7 +75,7 @@ EOF test_expect_success \ 'ls-tree recursive with -t' \ 'git ls-tree -r -t $tree >current && - cat >expected <<\EOF && + make_expected <<\EOF && 100644 blob X path0 120000 blob X path1 040000 tree X path2 @@ -77,7 +89,7 @@ EOF test_expect_success \ 'ls-tree recursive with -d' \ 'git ls-tree -r -d $tree >current && - cat >expected <<\EOF && + make_expected <<\EOF && 040000 tree X path2 040000 tree X path2/baz EOF @@ -86,7 +98,7 @@ EOF test_expect_success \ 'ls-tree filtered with path' \ 'git ls-tree $tree path >current && - cat >expected <<\EOF && + make_expected <<\EOF && EOF test_output' @@ -96,7 +108,7 @@ EOF test_expect_success \ 'ls-tree filtered with path1 path0' \ 'git ls-tree $tree path1 path0 >current && - cat >expected <<\EOF && + make_expected <<\EOF && 100644 blob X path0 120000 blob X path1 EOF @@ -105,7 +117,7 @@ EOF test_expect_success \ 'ls-tree filtered with path0/' \ 'git ls-tree $tree path0/ >current && - cat >expected <<\EOF && + make_expected <<\EOF && EOF test_output' @@ -114,7 +126,7 @@ EOF test_expect_success \ 'ls-tree filtered with path2' \ 'git ls-tree $tree path2 >current && - cat >expected <<\EOF && + make_expected <<\EOF && 040000 tree X path2 EOF test_output' @@ -123,7 +135,7 @@ EOF test_expect_success \ 'ls-tree filtered with path2/' \ 'git ls-tree $tree path2/ >current && - cat >expected <<\EOF && + make_expected <<\EOF && 040000 tree X path2/baz 120000 blob X path2/bazbo 100644 blob X path2/foo @@ -135,7 +147,7 @@ EOF test_expect_success \ 'ls-tree filtered with path2/baz' \ 'git ls-tree $tree path2/baz >current && - cat >expected <<\EOF && + make_expected <<\EOF && 040000 tree X path2/baz EOF test_output' @@ -143,14 +155,14 @@ EOF test_expect_success \ 'ls-tree filtered with path2/bak' \ 'git ls-tree $tree path2/bak >current && - cat >expected <<\EOF && + make_expected <<\EOF && EOF test_output' test_expect_success \ 'ls-tree -t filtered with path2/bak' \ 'git ls-tree -t $tree path2/bak >current && - cat >expected <<\EOF && + make_expected <<\EOF && 040000 tree X path2 EOF test_output' diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 1b1e9ece57..d59a9b4aef 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -121,7 +121,7 @@ test_expect_success 'renaming a symref is not allowed' \ ! test -f .git/refs/heads/master3 ' -test_expect_success \ +test_expect_success SYMLINKS \ 'git branch -m u v should fail when the reflog for u is a symlink' ' git branch -l u && mv .git/logs/refs/heads/u real-u && diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh new file mode 100755 index 0000000000..809d1c4ed4 --- /dev/null +++ b/t/t3203-branch-output.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +test_description='git branch display tests' +. ./test-lib.sh + +test_expect_success 'make commits' ' + echo content >file && + git add file && + git commit -m one && + echo content >>file && + git commit -a -m two +' + +test_expect_success 'make branches' ' + git branch branch-one + git branch branch-two HEAD^ +' + +test_expect_success 'make remote branches' ' + git update-ref refs/remotes/origin/branch-one branch-one + git update-ref refs/remotes/origin/branch-two branch-two + git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one +' + +cat >expect <<'EOF' + branch-one + branch-two +* master +EOF +test_expect_success 'git branch shows local branches' ' + git branch >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' + origin/HEAD -> origin/branch-one + origin/branch-one + origin/branch-two +EOF +test_expect_success 'git branch -r shows remote branches' ' + git branch -r >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' + branch-one + branch-two +* master + remotes/origin/HEAD -> origin/branch-one + remotes/origin/branch-one + remotes/origin/branch-two +EOF +test_expect_success 'git branch -a shows local and remote branches' ' + git branch -a >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +two +one +two +EOF +test_expect_success 'git branch -v shows branch summaries' ' + git branch -v >tmp && + awk "{print \$NF}" <tmp >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +* (no branch) + branch-one + branch-two + master +EOF +test_expect_success 'git branch shows detached HEAD properly' ' + git checkout HEAD^0 && + git branch >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index be7ae5a004..6e391a3702 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -83,9 +83,9 @@ test_expect_success 'rebase a single mode change' ' git checkout -b modechange HEAD^ && echo 1 > X && git add X && - chmod a+x A && + test_chmod +x A && test_tick && - git commit -m modechange A X && + git commit -m modechange && GIT_TRACE=1 git rebase master ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 603b003edf..c32ff6682b 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -459,4 +459,15 @@ test_expect_success 'submodule rebase -i' ' FAKE_LINES="1 squash 2 3" git rebase -i A ' +test_expect_success 'avoid unnecessary reset' ' + git checkout master && + test-chmtime =123456789 file3 && + git update-index --refresh && + HEAD=$(git rev-parse HEAD) && + git rebase -i HEAD~4 && + test $HEAD = $(git rev-parse HEAD) && + MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') && + test 123456789 = $MTIME +' + test_done diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh index 5391080943..85fc7c4af8 100755 --- a/t/t3406-rebase-message.sh +++ b/t/t3406-rebase-message.sh @@ -22,7 +22,8 @@ test_expect_success setup ' git checkout topic && quick_one A && quick_one B && - quick_one Z + quick_one Z && + git tag start ' @@ -41,4 +42,24 @@ test_expect_success 'rebase -m' ' ' +test_expect_success 'rebase --stat' ' + git reset --hard start + git rebase --stat master >diffstat.txt && + grep "^ fileX | *1 +$" diffstat.txt +' + +test_expect_success 'rebase w/config rebase.stat' ' + git reset --hard start + git config rebase.stat true && + git rebase master >diffstat.txt && + grep "^ fileX | *1 +$" diffstat.txt +' + +test_expect_success 'rebase -n overrides config rebase.stat config' ' + git reset --hard start + git config rebase.stat true && + git rebase -n master >diffstat.txt && + ! grep "^ fileX | *1 +$" diffstat.txt +' + test_done diff --git a/t/t3409-rebase-hook.sh b/t/t3413-rebase-hook.sh index 098b75507b..098b75507b 100755 --- a/t/t3409-rebase-hook.sh +++ b/t/t3413-rebase-hook.sh diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 95542e9cfe..76b1bb4545 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -12,30 +12,37 @@ test_expect_success \ 'Initialize test directory' \ "touch -- foo bar baz 'space embedded' -q && git add -- foo bar baz 'space embedded' -q && - git commit -m 'add normal files' && - test_tabs=y && - if touch -- 'tab embedded' 'newline -embedded' - then + git commit -m 'add normal files'" + +if touch -- 'tab embedded' 'newline +embedded' 2>/dev/null +then + test_set_prereq FUNNYNAMES +else + say 'Your filesystem does not allow tabs in filenames.' +fi + +test_expect_success FUNNYNAMES 'add files with funny names' " git add -- 'tab embedded' 'newline embedded' && git commit -m 'add files with tabs and newlines' - else - say 'Your filesystem does not allow tabs in filenames.' - test_tabs=n - fi" +" +# Determine rm behavior # Later we will try removing an unremovable path to make sure # git rm barfs, but if the test is run as root that cannot be # arranged. -test_expect_success \ - 'Determine rm behavior' \ - ': >test-file - chmod a-w . - rm -f test-file - test -f test-file && test_failed_remove=y - chmod 775 . - rm -f test-file' +: >test-file +chmod a-w . +rm -f test-file 2>/dev/null +if test -f test-file +then + test_set_prereq RO_DIR +else + say 'skipping removal failure test (perhaps running as root?)' +fi +chmod 775 . +rm -f test-file test_expect_success \ 'Pre-check that foo exists and is in index before git rm foo' \ @@ -100,20 +107,16 @@ test_expect_success \ 'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \ 'git rm -- -q' -test "$test_tabs" = y && test_expect_success \ +test_expect_success FUNNYNAMES \ "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \ "git rm -f 'space embedded' 'tab embedded' 'newline embedded'" -if test "$test_failed_remove" = y; then -chmod a-w . -test_expect_success \ - 'Test that "git rm -f" fails if its rm fails' \ - 'test_must_fail git rm -f baz' -chmod 775 . -else - test_expect_success 'skipping removal failure (perhaps running as root?)' : -fi +test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' ' + chmod a-w . && + test_must_fail git rm -f baz && + chmod 775 . +' test_expect_success \ 'When the rm in "git rm -f" fails, it should not remove the file from the index' \ diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 9f6454d2ff..050de42ef4 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -30,7 +30,7 @@ test_expect_success \ *) echo fail; git ls-files --stage xfoo1; (exit 1);; esac' -test_expect_success 'git add: filemode=0 should not get confused by symlink' ' +test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' ' rm -f xfoo1 && ln -s foo xfoo1 && git add xfoo1 && @@ -51,7 +51,7 @@ test_expect_success \ *) echo fail; git ls-files --stage xfoo2; (exit 1);; esac' -test_expect_success 'git add: filemode=0 should not get confused by symlink' ' +test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' ' rm -f xfoo2 && ln -s foo xfoo2 && git update-index --add xfoo2 && @@ -61,7 +61,7 @@ test_expect_success 'git add: filemode=0 should not get confused by symlink' ' esac ' -test_expect_success \ +test_expect_success SYMLINKS \ 'git update-index --add: Test that executable bit is not used...' \ 'git config core.filemode 0 && ln -s xfoo2 xfoo3 && @@ -179,7 +179,7 @@ test_expect_success 'git add --refresh' ' test -z "`git diff-index HEAD -- foo`" ' -test_expect_success 'git add should fail atomically upon an unreadable file' ' +test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' ' git reset --hard && date >foo1 && date >foo2 && @@ -190,7 +190,7 @@ test_expect_success 'git add should fail atomically upon an unreadable file' ' rm -f foo2 -test_expect_success 'git add --ignore-errors' ' +test_expect_success POSIXPERM 'git add --ignore-errors' ' git reset --hard && date >foo1 && date >foo2 && @@ -201,7 +201,7 @@ test_expect_success 'git add --ignore-errors' ' rm -f foo2 -test_expect_success 'git add (add.ignore-errors)' ' +test_expect_success POSIXPERM 'git add (add.ignore-errors)' ' git config add.ignore-errors 1 && git reset --hard && date >foo1 && @@ -212,7 +212,7 @@ test_expect_success 'git add (add.ignore-errors)' ' ' rm -f foo2 -test_expect_success 'git add (add.ignore-errors = false)' ' +test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' ' git config add.ignore-errors 0 && git reset --hard && date >foo1 && @@ -222,7 +222,7 @@ test_expect_success 'git add (add.ignore-errors = false)' ' ! ( git ls-files foo1 | grep foo1 ) ' -test_expect_success 'git add '\''fo\[ou\]bar'\'' ignores foobar' ' +test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" ' git reset --hard && touch fo\[ou\]bar foobar && git add '\''fo\[ou\]bar'\'' && diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index e95663d8e6..fe017839c4 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -135,10 +135,12 @@ test_expect_success 'real edit works' ' if test "$(git config --bool core.filemode)" = false then - say 'skipping filemode tests (filesystem does not properly support modes)' + say 'skipping filemode tests (filesystem does not properly support modes)' else + test_set_prereq FILEMODE +fi -test_expect_success 'patch does not affect mode' ' +test_expect_success FILEMODE 'patch does not affect mode' ' git reset --hard && echo content >>file && chmod +x file && @@ -147,7 +149,7 @@ test_expect_success 'patch does not affect mode' ' git diff file | grep "new mode" ' -test_expect_success 'stage mode but not hunk' ' +test_expect_success FILEMODE 'stage mode but not hunk' ' git reset --hard && echo content >>file && chmod +x file && @@ -156,7 +158,6 @@ test_expect_success 'stage mode but not hunk' ' git diff file | grep "+content" ' -fi # end of tests disabled when filemode is not usable test_done diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index cc3681f161..18695ce821 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -258,4 +258,12 @@ test_expect_success \ git diff-tree -r -R $tree_A $tree_B >.test-b && cmp -s .test-a .test-b' +test_expect_success \ + 'diff can read from stdin' \ + 'test_must_fail git diff --no-index -- MN - < NN | + grep -v "^index" | sed "s#/-#/NN#" >.test-a && + test_must_fail git diff --no-index -- MN NN | + grep -v "^index" >.test-b && + test_cmp .test-a .test-b' + test_done diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh index b35af9b42d..a4da1196a9 100755 --- a/t/t4004-diff-rename-symlink.sh +++ b/t/t4004-diff-rename-symlink.sh @@ -12,6 +12,12 @@ by an edit for them. . ./test-lib.sh . "$TEST_DIRECTORY"/diff-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + test_expect_success \ 'prepare reference tree' \ 'echo xyzzy | tr -d '\\\\'012 >yomin && diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index 4e92fce1d0..8c1b81e248 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -15,21 +15,10 @@ test_expect_success \ tree=`git write-tree` && echo $tree' -if [ "$(git config --get core.filemode)" = false ] -then - say 'filemode disabled on the filesystem, using update-index --chmod=+x' - test_expect_success \ - 'git update-index --chmod=+x' \ - 'git update-index rezrov && - git update-index --chmod=+x rezrov && - git diff-index $tree >current' -else - test_expect_success \ - 'chmod' \ - 'chmod +x rezrov && - git update-index rezrov && - git diff-index $tree >current' -fi +test_expect_success \ + 'chmod' \ + 'test_chmod +x rezrov && + git diff-index $tree >current' _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 7e343a9cd1..e19ca65885 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -99,7 +99,7 @@ test_expect_success \ 'validate result of -B -M (#4)' \ 'compare_diff_raw expected current' -test_expect_success \ +test_expect_success SYMLINKS \ 'make file0 into something completely different' \ 'rm -f file0 && ln -s frotz file0 && @@ -114,7 +114,7 @@ cat >expected <<\EOF :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1 EOF -test_expect_success \ +test_expect_success SYMLINKS \ 'validate result of -B (#5)' \ 'compare_diff_raw expected current' @@ -129,7 +129,7 @@ cat >expected <<\EOF :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1 EOF -test_expect_success \ +test_expect_success SYMLINKS \ 'validate result of -B -M (#6)' \ 'compare_diff_raw expected current' @@ -144,7 +144,7 @@ cat >expected <<\EOF :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M file1 EOF -test_expect_success \ +test_expect_success SYMLINKS \ 'validate result of -M (#7)' \ 'compare_diff_raw expected current' diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh index 9055c8b318..d7e327cc5b 100755 --- a/t/t4011-diff-symlink.sh +++ b/t/t4011-diff-symlink.sh @@ -9,6 +9,12 @@ test_description='Test diff of symlinks. . ./test-lib.sh . "$TEST_DIRECTORY"/diff-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + cat > expected << EOF diff --git a/frotz b/frotz new file mode 120000 diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index 3cf5b5c4ea..f64aa48d24 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -87,7 +87,7 @@ nul_to_q() { test_expect_success 'diff --no-index with binary creation' ' echo Q | q_to_nul >binary && - (:# hide error code from diff, which just indicates differences + (: hide error code from diff, which just indicates differences git diff --binary --no-index /dev/null binary >current || true ) && diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 9c709022ef..426e64e828 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -101,8 +101,7 @@ do '' | '#'*) continue ;; esac test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'` - cnt=`expr $test_count + 1` - pfx=`printf "%04d" $cnt` + pfx=`printf "%04d" $test_count` expect="$TEST_DIRECTORY/t4013/diff.$test" actual="$pfx-diff.$test" @@ -207,6 +206,10 @@ log --root -c --patch-with-stat --summary master log --root --cc --patch-with-stat --summary master log -SF master log -SF -p master +log --decorate --all + +rev-list --parents HEAD +rev-list --children HEAD whatchanged master whatchanged -p master @@ -268,6 +271,7 @@ diff --no-index --name-status dir2 dir diff --no-index --name-status -- dir2 dir diff --no-index dir dir3 diff master master^ side +diff --dirstat master~1 master~2 EOF test_done diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2 new file mode 100644 index 0000000000..b672e1ca63 --- /dev/null +++ b/t/t4013/diff.diff_--dirstat_master~1_master~2 @@ -0,0 +1,3 @@ +$ git diff --dirstat master~1 master~2 + 40.0% dir/ +$ diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all new file mode 100644 index 0000000000..12da8ac07d --- /dev/null +++ b/t/t4013/diff.log_--decorate_--all @@ -0,0 +1,34 @@ +$ git log --decorate --all +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master) +Merge: 9a6d494 c7a2ab9 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:04:00 2006 +0000 + + Merge branch 'side' + +commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side) +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:03:00 2006 +0000 + + Side + +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:02:00 2006 +0000 + + Third + +commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:01:00 2006 +0000 + + Second + + This is the second commit. + +commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial) +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:00:00 2006 +0000 + + Initial +$ diff --git a/t/t4013/diff.rev-list_--children_HEAD b/t/t4013/diff.rev-list_--children_HEAD new file mode 100644 index 0000000000..e7f17d5aa0 --- /dev/null +++ b/t/t4013/diff.rev-list_--children_HEAD @@ -0,0 +1,7 @@ +$ git rev-list --children HEAD +59d314ad6f356dd08601a4cd5e530381da3e3c64 +c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 59d314ad6f356dd08601a4cd5e530381da3e3c64 +9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 59d314ad6f356dd08601a4cd5e530381da3e3c64 +1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +444ac553ac7612cc88969031b02b3767fb8a353a 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a +$ diff --git a/t/t4013/diff.rev-list_--parents_HEAD b/t/t4013/diff.rev-list_--parents_HEAD new file mode 100644 index 0000000000..65d2a80208 --- /dev/null +++ b/t/t4013/diff.rev-list_--parents_HEAD @@ -0,0 +1,7 @@ +$ git rev-list --parents HEAD +59d314ad6f356dd08601a4cd5e530381da3e3c64 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a +c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 444ac553ac7612cc88969031b02b3767fb8a353a +9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 444ac553ac7612cc88969031b02b3767fb8a353a +444ac553ac7612cc88969031b02b3767fb8a353a +$ diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index f045898fe3..11061ddd5b 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -16,9 +16,7 @@ test_expect_success setup ' git checkout -b side && for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file && - chmod +x elif && - git update-index file elif && - git update-index --chmod=+x elif && + test_chmod +x elif && git commit -m "Side changes #1" && for i in D E F; do echo "$i"; done >>file && @@ -130,6 +128,21 @@ test_expect_success 'additional command line cc' ' grep "^ *S. E. Cipient <scipient@example.com>$" patch5 ' +test_expect_success 'command line headers' ' + + git config --unset-all format.headers && + git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch6 && + grep "^Cc: R. E. Cipient <rcipient@example.com>$" patch6 +' + +test_expect_success 'configuration headers and command line headers' ' + + git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" && + git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch7 && + grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch7 && + grep "^ *S. E. Cipient <scipient@example.com>$" patch7 +' + test_expect_success 'multiple files' ' rm -rf patches/ && @@ -138,56 +151,243 @@ test_expect_success 'multiple files' ' ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch ' -test_expect_success 'thread' ' +check_threading () { + expect="$1" && + shift && + (git format-patch --stdout "$@"; echo $? > status.out) | + # Prints everything between the Message-ID and In-Reply-To, + # and replaces all Message-ID-lookalikes by a sequence number + perl -ne ' + if (/^(message-id|references|in-reply-to)/i) { + $printing = 1; + } elsif (/^\S/) { + $printing = 0; + } + if ($printing) { + $h{$1}=$i++ if (/<([^>]+)>/ and !exists $h{$1}); + for $k (keys %h) {s/$k/$h{$k}/}; + print; + } + print "---\n" if /^From /i; + ' > actual && + test 0 = "$(cat status.out)" && + test_cmp "$expect" actual +} + +cat >> expect.no-threading <<EOF +--- +--- +--- +EOF - rm -rf patches/ && +test_expect_success 'no threading' ' git checkout side && - git format-patch --thread -o patches/ master && - FIRST_MID=$(grep "Message-Id:" patches/0001-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") && - for i in patches/0002-* patches/0003-* - do - grep "References: $FIRST_MID" $i && - grep "In-Reply-To: $FIRST_MID" $i || break - done + check_threading expect.no-threading master ' -test_expect_success 'thread in-reply-to' ' +cat > expect.thread <<EOF +--- +Message-Id: <0> +--- +Message-Id: <1> +In-Reply-To: <0> +References: <0> +--- +Message-Id: <2> +In-Reply-To: <0> +References: <0> +EOF - rm -rf patches/ && - git checkout side && - git format-patch --in-reply-to="<test.message>" --thread -o patches/ master && - FIRST_MID="<test.message>" && - for i in patches/* - do - grep "References: $FIRST_MID" $i && - grep "In-Reply-To: $FIRST_MID" $i || break - done +test_expect_success 'thread' ' + check_threading expect.thread --thread master ' -test_expect_success 'thread cover-letter' ' +cat > expect.in-reply-to <<EOF +--- +Message-Id: <0> +In-Reply-To: <1> +References: <1> +--- +Message-Id: <2> +In-Reply-To: <1> +References: <1> +--- +Message-Id: <3> +In-Reply-To: <1> +References: <1> +EOF - rm -rf patches/ && - git checkout side && - git format-patch --cover-letter --thread -o patches/ master && - FIRST_MID=$(grep "Message-Id:" patches/0000-* | sed "s/^[^<]*\(<[^>]*>\).*$/\1/") && - for i in patches/0001-* patches/0002-* patches/0003-* - do - grep "References: $FIRST_MID" $i && - grep "In-Reply-To: $FIRST_MID" $i || break - done +test_expect_success 'thread in-reply-to' ' + check_threading expect.in-reply-to --in-reply-to="<test.message>" \ + --thread master ' +cat > expect.cover-letter <<EOF +--- +Message-Id: <0> +--- +Message-Id: <1> +In-Reply-To: <0> +References: <0> +--- +Message-Id: <2> +In-Reply-To: <0> +References: <0> +--- +Message-Id: <3> +In-Reply-To: <0> +References: <0> +EOF + +test_expect_success 'thread cover-letter' ' + check_threading expect.cover-letter --cover-letter --thread master +' + +cat > expect.cl-irt <<EOF +--- +Message-Id: <0> +In-Reply-To: <1> +References: <1> +--- +Message-Id: <2> +In-Reply-To: <0> +References: <1> + <0> +--- +Message-Id: <3> +In-Reply-To: <0> +References: <1> + <0> +--- +Message-Id: <4> +In-Reply-To: <0> +References: <1> + <0> +EOF + test_expect_success 'thread cover-letter in-reply-to' ' + check_threading expect.cl-irt --cover-letter \ + --in-reply-to="<test.message>" --thread master +' - rm -rf patches/ && - git checkout side && - git format-patch --cover-letter --in-reply-to="<test.message>" --thread -o patches/ master && - FIRST_MID="<test.message>" && - for i in patches/* - do - grep "References: $FIRST_MID" $i && - grep "In-Reply-To: $FIRST_MID" $i || break - done +test_expect_success 'thread explicit shallow' ' + check_threading expect.cl-irt --cover-letter \ + --in-reply-to="<test.message>" --thread=shallow master +' + +cat > expect.deep <<EOF +--- +Message-Id: <0> +--- +Message-Id: <1> +In-Reply-To: <0> +References: <0> +--- +Message-Id: <2> +In-Reply-To: <1> +References: <0> + <1> +EOF + +test_expect_success 'thread deep' ' + check_threading expect.deep --thread=deep master +' + +cat > expect.deep-irt <<EOF +--- +Message-Id: <0> +In-Reply-To: <1> +References: <1> +--- +Message-Id: <2> +In-Reply-To: <0> +References: <1> + <0> +--- +Message-Id: <3> +In-Reply-To: <2> +References: <1> + <0> + <2> +EOF + +test_expect_success 'thread deep in-reply-to' ' + check_threading expect.deep-irt --thread=deep \ + --in-reply-to="<test.message>" master +' + +cat > expect.deep-cl <<EOF +--- +Message-Id: <0> +--- +Message-Id: <1> +In-Reply-To: <0> +References: <0> +--- +Message-Id: <2> +In-Reply-To: <1> +References: <0> + <1> +--- +Message-Id: <3> +In-Reply-To: <2> +References: <0> + <1> + <2> +EOF + +test_expect_success 'thread deep cover-letter' ' + check_threading expect.deep-cl --cover-letter --thread=deep master +' + +cat > expect.deep-cl-irt <<EOF +--- +Message-Id: <0> +In-Reply-To: <1> +References: <1> +--- +Message-Id: <2> +In-Reply-To: <0> +References: <1> + <0> +--- +Message-Id: <3> +In-Reply-To: <2> +References: <1> + <0> + <2> +--- +Message-Id: <4> +In-Reply-To: <3> +References: <1> + <0> + <2> + <3> +EOF + +test_expect_success 'thread deep cover-letter in-reply-to' ' + check_threading expect.deep-cl-irt --cover-letter \ + --in-reply-to="<test.message>" --thread=deep master +' + +test_expect_success 'thread via config' ' + git config format.thread true && + check_threading expect.thread master +' + +test_expect_success 'thread deep via config' ' + git config format.thread deep && + check_threading expect.deep master +' + +test_expect_success 'thread config + override' ' + git config format.thread deep && + check_threading expect.thread --thread master +' + +test_expect_success 'thread config + --no-thread' ' + git config format.thread deep && + check_threading expect.no-threading --no-thread master ' test_expect_success 'excessive subject' ' diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index f8c99f1a98..0720001281 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -152,4 +152,12 @@ test_expect_success 'external diff with autocrlf = true' ' test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c) ' +test_expect_success 'diff --cached' ' + git add file && + git update-index --assume-unchanged file && + echo second >file && + git diff --cached >actual && + test_cmp ../t4020/diff.NUL actual +' + test_done diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 297ddb5a25..9bdf6596d8 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -4,6 +4,12 @@ test_description='typechange rename detection' . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + test_expect_success setup ' rm -f foo bar && diff --git a/t/t4017-quiet.sh b/t/t4035-diff-quiet.sh index e747e84227..e747e84227 100755 --- a/t/t4017-quiet.sh +++ b/t/t4035-diff-quiet.sh diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh index ba43f18549..ba43f18549 100755 --- a/t/t4021-format-patch-signer-mime.sh +++ b/t/t4036-format-patch-signer-mime.sh diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh index d42abff1ad..1597965241 100755 --- a/t/t4102-apply-rename.sh +++ b/t/t4102-apply-rename.sh @@ -31,14 +31,16 @@ test_expect_success setup \ test_expect_success apply \ 'git apply --index --stat --summary --apply test-patch' -if [ "$(git config --get core.filemode)" = false ] +if test "$(git config --bool core.filemode)" = false then say 'filemode disabled on the filesystem' else - test_expect_success validate \ - 'test -f bar && ls -l bar | grep "^-..x......"' + test_set_prereq FILEMODE fi +test_expect_success FILEMODE validate \ + 'test -f bar && ls -l bar | grep "^-..x......"' + test_expect_success 'apply reverse' \ 'git apply -R --index --stat --summary --apply test-patch && test "$(cat foo)" = "This is foo"' diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh index 0f185caa44..99ec13dd53 100755 --- a/t/t4114-apply-typechange.sh +++ b/t/t4114-apply-typechange.sh @@ -9,6 +9,12 @@ test_description='git apply should not get confused with type changes. . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + test_expect_success 'setup repository and commits' ' echo "hello world" > foo && echo "hi planet" > bar && diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh index 9ace578f17..b852e58980 100755 --- a/t/t4115-apply-symlink.sh +++ b/t/t4115-apply-symlink.sh @@ -9,6 +9,12 @@ test_description='git apply symlinks and partial files . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + test_expect_success setup ' ln -s path1/path2/path3/path4/path5 link1 && diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh index 841773f75f..0d3c1d5dd5 100755 --- a/t/t4122-apply-symlink-inside.sh +++ b/t/t4122-apply-symlink-inside.sh @@ -3,6 +3,12 @@ test_description='apply to deeper directory without getting fooled with symlink' . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + lecho () { for l_ do diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index adfcbb5a3b..fc7af04931 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -4,6 +4,13 @@ test_description='applying patch with mode bits' . ./test-lib.sh +if test "$(git config --bool core.filemode)" = false +then + say 'filemode disabled on the filesystem' +else + test_set_prereq FILEMODE +fi + test_expect_success setup ' echo original >file && git add file && @@ -16,14 +23,14 @@ test_expect_success setup ' git diff --stat -p >patch-1.txt ' -test_expect_success 'same mode (no index)' ' +test_expect_success FILEMODE 'same mode (no index)' ' git reset --hard && chmod +x file && git apply patch-0.txt && test -x file ' -test_expect_success 'same mode (with index)' ' +test_expect_success FILEMODE 'same mode (with index)' ' git reset --hard && chmod +x file && git add file && @@ -32,7 +39,7 @@ test_expect_success 'same mode (with index)' ' git diff --exit-code ' -test_expect_success 'same mode (index only)' ' +test_expect_success FILEMODE 'same mode (index only)' ' git reset --hard && chmod +x file && git add file && @@ -40,20 +47,20 @@ test_expect_success 'same mode (index only)' ' git ls-files -s file | grep "^100755" ' -test_expect_success 'mode update (no index)' ' +test_expect_success FILEMODE 'mode update (no index)' ' git reset --hard && git apply patch-1.txt && test -x file ' -test_expect_success 'mode update (with index)' ' +test_expect_success FILEMODE 'mode update (with index)' ' git reset --hard && git apply --index patch-1.txt && test -x file && git diff --exit-code ' -test_expect_success 'mode update (index only)' ' +test_expect_success FILEMODE 'mode update (index only)' ' git reset --hard && git apply --cached patch-1.txt && git ls-files -s file | grep "^100755" diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 7b976ee36d..b98619035c 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -37,6 +37,46 @@ test_expect_success setup ' ' +printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial" > expect +test_expect_success 'pretty' ' + + git log --pretty="format:%s" > actual && + test_cmp expect actual +' + +printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial\n" > expect +test_expect_success 'pretty (tformat)' ' + + git log --pretty="tformat:%s" > actual && + test_cmp expect actual +' + +test_expect_success 'pretty (shortcut)' ' + + git log --pretty="%s" > actual && + test_cmp expect actual +' + +test_expect_success 'format' ' + + git log --format="%s" > actual && + test_cmp expect actual +' + +cat > expect << EOF +804a787 sixth +394ef78 fifth +5d31159 fourth +2fbe8c0 third +f7dab8e second +3a2fdcb initial +EOF +test_expect_success 'oneline' ' + + git log --oneline > actual && + test_cmp expect actual +' + test_expect_success 'diff-filter=A' ' actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) && @@ -134,5 +174,153 @@ test_expect_success 'log --grep -i' ' test_cmp expect actual ' +cat > expect <<EOF +* Second +* sixth +* fifth +* fourth +* third +* second +* initial +EOF + +test_expect_success 'simple log --graph' ' + git log --graph --pretty=tformat:%s >actual && + test_cmp expect actual +' + +test_expect_success 'set up merge history' ' + git checkout -b side HEAD~4 && + test_commit side-1 1 1 && + test_commit side-2 2 2 && + git checkout master && + git merge side +' + +cat > expect <<\EOF +* Merge branch 'side' +|\ +| * side-2 +| * side-1 +* | Second +* | sixth +* | fifth +* | fourth +|/ +* third +* second +* initial +EOF + +test_expect_success 'log --graph with merge' ' + git log --graph --date-order --pretty=tformat:%s | + sed "s/ *$//" >actual && + test_cmp expect actual +' + +cat > expect <<\EOF +* commit master +|\ Merge: A B +| | Author: A U Thor <author@example.com> +| | +| | Merge branch 'side' +| | +| * commit side +| | Author: A U Thor <author@example.com> +| | +| | side-2 +| | +| * commit tags/side-1 +| | Author: A U Thor <author@example.com> +| | +| | side-1 +| | +* | commit master~1 +| | Author: A U Thor <author@example.com> +| | +| | Second +| | +* | commit master~2 +| | Author: A U Thor <author@example.com> +| | +| | sixth +| | +* | commit master~3 +| | Author: A U Thor <author@example.com> +| | +| | fifth +| | +* | commit master~4 +|/ Author: A U Thor <author@example.com> +| +| fourth +| +* commit tags/side-1~1 +| Author: A U Thor <author@example.com> +| +| third +| +* commit tags/side-1~2 +| Author: A U Thor <author@example.com> +| +| second +| +* commit tags/side-1~3 + Author: A U Thor <author@example.com> + + initial +EOF + +test_expect_success 'log --graph with full output' ' + git log --graph --date-order --pretty=short | + git name-rev --name-only --stdin | + sed "s/Merge:.*/Merge: A B/;s/ *$//" >actual && + test_cmp expect actual +' + +test_expect_success 'set up more tangled history' ' + git checkout -b tangle HEAD~6 && + test_commit tangle-a tangle-a a && + git merge master~3 && + git merge side~1 && + git checkout master && + git merge tangle +' + +cat > expect <<\EOF +* Merge branch 'tangle' +|\ +| * Merge branch 'side' (early part) into tangle +| |\ +| * \ Merge branch 'master' (early part) into tangle +| |\ \ +| * | | tangle-a +* | | | Merge branch 'side' +|\ \ \ \ +| * | | | side-2 +| | | |/ +| | |/| +| |/| | +| * | | side-1 +* | | | Second +* | | | sixth +| | |/ +| |/| +|/| | +* | | fifth +* | | fourth +|/ / +* | third +|/ +* second +* initial +EOF + +test_expect_success 'log --graph with merge' ' + git log --graph --date-order --pretty=tformat:%s | + sed "s/ *$//" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh new file mode 100755 index 0000000000..04f7bae850 --- /dev/null +++ b/t/t4204-patch-id.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +test_description='git patch-id' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit initial foo a && + test_commit first foo b && + git checkout -b same HEAD^ && + test_commit same-msg foo b && + git checkout -b notsame HEAD^ && + test_commit notsame-msg foo c +' + +test_expect_success 'patch-id output is well-formed' ' + git log -p -1 | git patch-id > output && + grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output +' + +get_patch_id () { + git log -p -1 "$1" | git patch-id | + sed "s# .*##" > patch-id_"$1" +} + +test_expect_success 'patch-id detects equality' ' + get_patch_id master && + get_patch_id same && + test_cmp patch-id_master patch-id_same +' + +test_expect_success 'patch-id detects inequality' ' + get_patch_id master && + get_patch_id notsame && + ! test_cmp patch-id_master patch-id_notsame +' + +test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index c942c8be85..7641e0dd46 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -37,7 +37,11 @@ test_expect_success \ cp /bin/sh a/bin && printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 && printf "A not substituted O" >a/substfile2 && - ln -s a a/l1 && + if test_have_prereq SYMLINKS; then + ln -s a a/l1 + else + printf %s a > a/l1 + fi && (p=long_path_to_a_file && cd a && for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && echo text >file_with_long_path) && @@ -76,7 +80,7 @@ test_expect_success \ test_expect_success \ 'git archive vs. git tar-tree' \ - 'diff b.tar b2.tar' + 'test_cmp b.tar b2.tar' test_expect_success \ 'git archive in a bare repo' \ @@ -86,18 +90,22 @@ test_expect_success \ 'git archive vs. the same in a bare repo' \ 'test_cmp b.tar b3.tar' +test_expect_success 'git archive with --output' \ + 'git archive --output=b4.tar HEAD && + test_cmp b.tar b4.tar' + test_expect_success \ 'validate file modification time' \ 'mkdir extract && "$TAR" xf b.tar -C extract a/a && test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime && echo "1117231200" >expected.mtime && - diff expected.mtime b.mtime' + test_cmp expected.mtime b.mtime' test_expect_success \ 'git get-tar-commit-id' \ 'git get-tar-commit-id <b.tar >b.commitid && - diff .git/$(git symbolic-ref HEAD) b.commitid' + test_cmp .git/$(git symbolic-ref HEAD) b.commitid' test_expect_success \ 'extract tar archive' \ @@ -106,7 +114,7 @@ test_expect_success \ test_expect_success \ 'validate filenames' \ '(cd b/a && find .) | sort >b.lst && - diff a.lst b.lst' + test_cmp a.lst b.lst' test_expect_success \ 'validate file contents' \ @@ -123,7 +131,7 @@ test_expect_success \ test_expect_success \ 'validate filenames with prefix' \ '(cd c/prefix/a && find .) | sort >c.lst && - diff a.lst c.lst' + test_cmp a.lst c.lst' test_expect_success \ 'validate file contents with prefix' \ @@ -144,8 +152,8 @@ test_expect_success \ 'validate substfile contents' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ >f/a/substfile1.expected && - diff f/a/substfile1.expected f/a/substfile1 && - diff a/substfile2 f/a/substfile2 + test_cmp f/a/substfile1.expected f/a/substfile1 && + test_cmp a/substfile2 f/a/substfile2 ' test_expect_success \ @@ -156,8 +164,8 @@ test_expect_success \ 'validate substfile contents from archive with prefix' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ >g/prefix/a/substfile1.expected && - diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 && - diff a/substfile2 g/prefix/a/substfile2 + test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 && + test_cmp a/substfile2 g/prefix/a/substfile2 ' test_expect_success \ @@ -172,23 +180,27 @@ test_expect_success \ 'git archive --format=zip vs. the same in a bare repo' \ 'test_cmp d.zip d1.zip' +test_expect_success 'git archive --format=zip with --output' \ + 'git archive --format=zip --output=d2.zip HEAD && + test_cmp d.zip d2.zip' + $UNZIP -v >/dev/null 2>&1 if [ $? -eq 127 ]; then - echo "Skipping ZIP tests, because unzip was not found" - test_done - exit + say "Skipping ZIP tests, because unzip was not found" +else + test_set_prereq UNZIP fi -test_expect_success \ +test_expect_success UNZIP \ 'extract ZIP archive' \ '(mkdir d && cd d && $UNZIP ../d.zip)' -test_expect_success \ +test_expect_success UNZIP \ 'validate filenames' \ '(cd d/a && find .) | sort >d.lst && - diff a.lst d.lst' + test_cmp a.lst d.lst' -test_expect_success \ +test_expect_success UNZIP \ 'validate file contents' \ 'diff -r a d/a' @@ -196,16 +208,16 @@ test_expect_success \ 'git archive --format=zip with prefix' \ 'git archive --format=zip --prefix=prefix/ HEAD >e.zip' -test_expect_success \ +test_expect_success UNZIP \ 'extract ZIP archive with prefix' \ '(mkdir e && cd e && $UNZIP ../e.zip)' -test_expect_success \ +test_expect_success UNZIP \ 'validate filenames with prefix' \ '(cd e/prefix/a && find .) | sort >e.lst && - diff a.lst e.lst' + test_cmp a.lst e.lst' -test_expect_success \ +test_expect_success UNZIP \ 'validate file contents with prefix' \ 'diff -r a e/prefix/a' diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index ccfc64c6ee..e2aa254eae 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -13,11 +13,10 @@ TRASH=`pwd` test_expect_success \ 'setup' \ 'rm -f .git/index* - for i in a b c - do - dd if=/dev/zero bs=4k count=1 | perl -pe "y/\\000/$i/" >$i && - git update-index --add $i || return 1 - done && + perl -e "print \"a\" x 4096;" > a && + perl -e "print \"b\" x 4096;" > b && + perl -e "print \"c\" x 4096;" > c && + git update-index --add a b c && cat c >d && echo foo >>d && git update-index --add d && tree=`git write-tree` && commit=`git commit-tree $tree </dev/null` && { @@ -221,7 +220,7 @@ test_expect_success \ test_expect_success \ 'verify-pack catches a corrupted pack signature' \ 'cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 && + echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 && if git verify-pack test-3.idx then false else :; @@ -230,7 +229,7 @@ test_expect_success \ test_expect_success \ 'verify-pack catches a corrupted pack version' \ 'cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 && + echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 && if git verify-pack test-3.idx then false else :; @@ -239,7 +238,7 @@ test_expect_success \ test_expect_success \ 'verify-pack catches a corrupted type/size of the 1st packed object data' \ 'cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 && + echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 && if git verify-pack test-3.idx then false else :; @@ -250,7 +249,7 @@ test_expect_success \ 'l=`wc -c <test-3.idx` && l=`expr $l - 20` && cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l && + printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l && if git verify-pack test-3.pack then false else :; diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index e6f70d474f..4360e77d31 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -69,32 +69,27 @@ test_expect_success \ 'index v2: force some 64-bit offsets with pack-objects' \ 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)' -have_64bits= if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) || ! (echo "$msg" | grep "pack too large .* off_t") then - have_64bits=t + test_set_prereq OFF64_T else say "skipping tests concerning 64-bit offsets" fi -test "$have_64bits" && -test_expect_success \ +test_expect_success OFF64_T \ 'index v2: verify a pack with some 64-bit offsets' \ 'git verify-pack -v "test-3-${pack3}.pack"' -test "$have_64bits" && -test_expect_success \ +test_expect_success OFF64_T \ '64-bit offsets: should be different from previous index v2 results' \ '! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"' -test "$have_64bits" && -test_expect_success \ +test_expect_success OFF64_T \ 'index v2: force some 64-bit offsets with index-pack' \ 'git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"' -test "$have_64bits" && -test_expect_success \ +test_expect_success OFF64_T \ '64-bit offsets: index-pack result should match pack-objects one' \ 'cmp "test-3-${pack3}.idx" "3.idx"' @@ -208,7 +203,7 @@ test_expect_success \ obj=`git hash-object file_001` && nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` && chmod +w ".git/objects/pack/pack-${pack1}.idx" && - dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \ + printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \ bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + $nr * 4)) && ( while read obj do git cat-file -p $obj >/dev/null || exit 1 diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh index d4e30fc43c..5132d41309 100755 --- a/t/t5303-pack-corruption-resilience.sh +++ b/t/t5303-pack-corruption-resilience.sh @@ -55,6 +55,8 @@ do_corrupt_object() { test_must_fail git verify-pack ${pack}.pack } +printf '\0' > zero + test_expect_success \ 'initial setup validation' \ 'create_test_files && @@ -66,7 +68,7 @@ test_expect_success \ test_expect_success \ 'create corruption in header of first object' \ - 'do_corrupt_object $blob_1 0 < /dev/zero && + 'do_corrupt_object $blob_1 0 < zero && test_must_fail git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -125,7 +127,7 @@ test_expect_success \ 'create corruption in header of first delta' \ 'create_new_pack && git prune-packed && - do_corrupt_object $blob_2 0 < /dev/zero && + do_corrupt_object $blob_2 0 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -180,7 +182,7 @@ test_expect_success \ 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \ 'create_new_pack && git prune-packed && - do_corrupt_object $blob_2 2 < /dev/zero && + do_corrupt_object $blob_2 2 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -207,7 +209,7 @@ test_expect_success \ 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \ 'create_new_pack --delta-base-offset && git prune-packed && - do_corrupt_object $blob_2 2 < /dev/zero && + do_corrupt_object $blob_2 2 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -259,7 +261,7 @@ test_expect_success \ test_expect_success \ '... and a redundant pack allows for full recovery too' \ - 'do_corrupt_object $blob_2 2 < /dev/zero && + 'do_corrupt_object $blob_2 2 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null && diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index 9b2e1a94c5..5858b868ed 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -71,4 +71,18 @@ test_expect_success 'post-checkout receives the right args when not switching br test $old = $new -a $flag = 0 ' +if test "$(git config --bool core.filemode)" = true; then +mkdir -p templates/hooks +cat >templates/hooks/post-checkout <<'EOF' +#!/bin/sh +echo $@ > $GIT_DIR/post-checkout.args +EOF +chmod +x templates/hooks/post-checkout + +test_expect_success 'post-checkout hook is triggered by clone' ' + git clone --template=templates . clone3 && + test -f clone3/.git/post-checkout.args +' +fi + test_done diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh index 4074e23ffa..d5db75d826 100755 --- a/t/t5503-tagfollow.sh +++ b/t/t5503-tagfollow.sh @@ -4,6 +4,12 @@ test_description='test automatic tag following' . ./test-lib.sh +case $(uname -s) in +*MINGW*) + say "GIT_DEBUG_SEND_PACK not supported - skipping tests" + test_done +esac + # End state of the repository: # # T - tag1 S - tag2 diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index eb637184a0..5ec668d6d8 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -28,7 +28,7 @@ tokens_match () { } check_remote_track () { - actual=$(git remote show "$1" | sed -e '1,/Tracked/d') && + actual=$(git remote show "$1" | sed -ne 's|^ \(.*\) tracked$|\1|p') shift && tokens_match "$*" "$actual" } @@ -136,47 +136,73 @@ EOF cat > test/expect << EOF * remote origin URL: $(pwd)/one - Remote branch merged with 'git pull' while on branch master + HEAD branch: master + Remote branches: + master new (next fetch will store in remotes/origin) + side tracked + Local branches configured for 'git pull': + ahead merges with remote master + master merges with remote master + octopus merges with remote topic-a + and with remote topic-b + and with remote topic-c + rebase rebases onto remote master + Local refs configured for 'git push': + master pushes to master (local out of date) + master pushes to upstream (create) +* remote two + URL: ../two + HEAD branch (remote HEAD is ambiguous, may be one of the following): + another master - New remote branch (next fetch will store in remotes/origin) - master - Tracked remote branches - side - master - Local branches pushed with 'git push' - master:upstream - +refs/tags/lastbackup + Local refs configured for 'git push': + ahead forces to master (fast forwardable) + master pushes to another (up to date) EOF test_expect_success 'show' ' (cd test && - git config --add remote.origin.fetch \ - refs/heads/master:refs/heads/upstream && + git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream && git fetch && + git checkout -b ahead origin/master && + echo 1 >> file && + test_tick && + git commit -m update file && + git checkout master && + git branch --track octopus origin/master && + git branch --track rebase origin/master && git branch -d -r origin/master && + git config --add remote.two.url ../two && + git config branch.rebase.rebase true && + git config branch.octopus.merge "topic-a topic-b topic-c" && (cd ../one && echo 1 > file && test_tick && git commit -m update file) && - git config remote.origin.push \ - refs/heads/master:refs/heads/upstream && - git config --add remote.origin.push \ - +refs/tags/lastbackup && - git remote show origin > output && + git config --add remote.origin.push : && + git config --add remote.origin.push refs/heads/master:refs/heads/upstream && + git config --add remote.origin.push +refs/tags/lastbackup && + git config --add remote.two.push +refs/heads/ahead:refs/heads/master && + git config --add remote.two.push refs/heads/master:refs/heads/another && + git remote show origin two > output && + git branch -d rebase octopus && test_cmp expect output) ' cat > test/expect << EOF * remote origin URL: $(pwd)/one - Remote branch merged with 'git pull' while on branch master - master - Tracked remote branches + HEAD branch: (not queried) + Remote branches: (status not queried) master side - Local branches pushed with 'git push' - master:upstream - +refs/tags/lastbackup + Local branches configured for 'git pull': + ahead merges with remote master + master merges with remote master + Local refs configured for 'git push' (status not queried): + (matching) pushes to (matching) + refs/heads/master pushes to refs/heads/upstream + refs/tags/lastbackup forces to refs/tags/lastbackup EOF test_expect_success 'show -n' ' @@ -197,6 +223,46 @@ test_expect_success 'prune' ' test_must_fail git rev-parse refs/remotes/origin/side) ' +test_expect_success 'set-head --delete' ' + (cd test && + git symbolic-ref refs/remotes/origin/HEAD && + git remote set-head --delete origin && + test_must_fail git symbolic-ref refs/remotes/origin/HEAD) +' + +test_expect_success 'set-head --auto' ' + (cd test && + git remote set-head --auto origin && + echo refs/remotes/origin/master >expect && + git symbolic-ref refs/remotes/origin/HEAD >output && + test_cmp expect output + ) +' + +cat >test/expect <<EOF +error: Multiple remote HEAD branches. Please choose one explicitly with: + git remote set-head two another + git remote set-head two master +EOF + +test_expect_success 'set-head --auto fails w/multiple HEADs' ' + (cd test && + test_must_fail git remote set-head --auto two >output 2>&1 && + test_cmp expect output) +' + +cat >test/expect <<EOF +refs/remotes/origin/side2 +EOF + +test_expect_success 'set-head explicit' ' + (cd test && + git remote set-head origin side2 && + git symbolic-ref refs/remotes/origin/HEAD >output && + git remote set-head origin master && + test_cmp expect output) +' + cat > test/expect << EOF Pruning origin URL: $(pwd)/one @@ -343,7 +409,7 @@ test_expect_success '"remote show" does not show symbolic refs' ' git clone one three && (cd three && git remote show origin > output && - ! grep HEAD < output && + ! grep "^ *HEAD$" < output && ! grep -i stale < output) ' diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh index 22ba380034..c28932216b 100755 --- a/t/t5511-refspec.sh +++ b/t/t5511-refspec.sh @@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me' test_refspec push ':refs/remotes/frotz/delete me' invalid test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid +test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid +test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid + +test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid +test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid + +test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid +test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid + +test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*' +test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*' + test_done diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index 1f4608d8ba..dbb927dec8 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -129,8 +129,7 @@ do '' | '#'*) continue ;; esac test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'` - cnt=`expr $test_count + 1` - pfx=`printf "%04d" $cnt` + pfx=`printf "%04d" $test_count` expect_f="$TEST_DIRECTORY/t5515/fetch.$test" actual_f="$pfx-fetch.$test" expect_r="$TEST_DIRECTORY/t5515/refs.$test" diff --git a/t/t5521-pull-symlink.sh b/t/t5522-pull-symlink.sh index 5672b51e2e..86bbd7d024 100755 --- a/t/t5521-pull-symlink.sh +++ b/t/t5522-pull-symlink.sh @@ -4,6 +4,12 @@ test_description='pulling from symlinked subdir' . ./test-lib.sh +if ! test_have_prereq SYMLINKS +then + say 'Symbolic links not supported, skipping tests.' + test_done +fi + # The scenario we are building: # # trash\ directory/ diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 10e5fd0d5a..5fe479e1c2 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -11,22 +11,16 @@ This test runs various sanity checks on http-push.' ROOT_PATH="$PWD" LIB_HTTPD_DAV=t +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'} if git http-push > /dev/null 2>&1 || [ $? -eq 128 ] then say "skipping test, USE_CURL_MULTI is not defined" test_done - exit fi . "$TEST_DIRECTORY"/lib-httpd.sh - -if ! start_httpd >&3 2>&4 -then - say "skipping test, web server setup failed" - test_done - exit -fi +start_httpd test_expect_success 'setup remote repository' ' cd "$ROOT_PATH" && diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh new file mode 100755 index 0000000000..05b1b62cb6 --- /dev/null +++ b/t/t5550-http-fetch.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +test_description='test fetching over http' +. ./test-lib.sh + +if test -n "$NO_CURL"; then + say 'skipping test, git built without http support' + test_done +fi + +. "$TEST_DIRECTORY"/lib-httpd.sh +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'} +start_httpd + +test_expect_success 'setup repository' ' + echo content >file && + git add file && + git commit -m one +' + +test_expect_success 'create http-accessible bare repository' ' + mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git --bare init && + echo "exec git update-server-info" >hooks/post-update && + chmod +x hooks/post-update + ) && + git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git push public master:master +' + +test_expect_success 'clone http repository' ' + git clone $HTTPD_URL/repo.git clone && + test_cmp file clone/file +' + +test_expect_success 'fetch changes via http' ' + echo content >>file && + git commit -a -m two && + git push public + (cd clone && git pull) && + test_cmp file clone/file +' + +test_expect_success 'http remote detects correct HEAD' ' + git push public master:other && + (cd clone && + git remote set-head origin -d && + git remote set-head origin -a && + git symbolic-ref refs/remotes/origin/HEAD > output && + echo refs/remotes/origin/master > expect && + test_cmp expect output + ) +' + +stop_httpd +test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 44793f2eee..2335d8bc85 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -159,4 +159,19 @@ test_expect_success 'clone a void' ' test_cmp target-6/.git/config target-7/.git/config ' +test_expect_success 'clone respects global branch.autosetuprebase' ' + ( + HOME=$(pwd) && + export HOME && + test_config="$HOME/.gitconfig" && + unset GIT_CONFIG_NOGLOBAL && + git config -f "$test_config" branch.autosetuprebase remote && + rm -fr dst && + git clone src dst && + cd dst && + actual="z$(git config branch.master.rebase)" && + test ztrue = $actual + ) +' + test_done diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh index 82b1d1e2b3..deffdaee49 100755 --- a/t/t5602-clone-remote-exec.sh +++ b/t/t5602-clone-remote-exec.sh @@ -18,8 +18,8 @@ test_expect_success 'clone calls git upload-pack unqualified with no -u option' ' test_expect_success 'clone calls specified git upload-pack with -u option' ' - GIT_SSH=./not_ssh git clone -u /something/bin/git-upload-pack localhost:/path/to/repo junk - echo "localhost /something/bin/git-upload-pack '\''/path/to/repo'\''" >expected + GIT_SSH=./not_ssh git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk + echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected test_cmp expected not_ssh_output ' diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh new file mode 100755 index 0000000000..9f52154cac --- /dev/null +++ b/t/t5705-clone-2gb.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +test_description='Test cloning a repository larger than 2 gigabyte' +. ./test-lib.sh + +test -z "$GIT_TEST_CLONE_2GB" && +say "Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" && +test_done && +exit + +test_expect_success 'setup' ' + + git config pack.compression 0 && + git config pack.depth 0 && + blobsize=$((20*1024*1024)) && + blobcount=$((2*1024*1024*1024/$blobsize+1)) && + i=1 && + (while test $i -le $blobcount + do + printf "Generating blob $i/$blobcount\r" >&2 && + printf "blob\nmark :$i\ndata $blobsize\n" && + #test-genrandom $i $blobsize && + printf "%-${blobsize}s" $i && + echo "M 100644 :$i $i" >> commit + i=$(($i+1)) || + echo $? > exit-status + done && + echo "commit refs/heads/master" && + echo "author A U Thor <author@email.com> 123456789 +0000" && + echo "committer C O Mitter <committer@email.com> 123456789 +0000" && + echo "data 5" && + echo ">2gb" && + cat commit) | + git fast-import && + test ! -f exit-status + +' + +test_expect_success 'clone' ' + + git clone --bare --no-hardlinks . clone + +' + +test_done diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh index 8073e0c3ef..8a3304fb0b 100755 --- a/t/t6031-merge-recursive.sh +++ b/t/t6031-merge-recursive.sh @@ -3,8 +3,10 @@ test_description='merge-recursive: handle file mode' . ./test-lib.sh -# Note that we follow "chmod +x F" with "update-index --chmod=+x F" to -# help filesystems that do not have the executable bit. +if ! test "$(git config --bool core.filemode)" = false +then + test_set_prereq FILEMODE +fi test_expect_success 'mode change in one branch: keep changed version' ' : >file1 && @@ -15,11 +17,14 @@ test_expect_success 'mode change in one branch: keep changed version' ' git add dummy && git commit -m a && git checkout -b b1 master && - chmod +x file1 && - git update-index --chmod=+x file1 && + test_chmod +x file1 && git commit -m b1 && git checkout a1 && git merge-recursive master -- a1 b1 && + git ls-files -s file1 | grep ^100755 +' + +test_expect_success FILEMODE 'verify executable bit on file' ' test -x file1 ' @@ -28,8 +33,7 @@ test_expect_success 'mode change in both branches: expect conflict' ' git checkout -b a2 master && : >file2 && H=$(git hash-object file2) && - chmod +x file2 && - git update-index --add --chmod=+x file2 && + test_chmod +x file2 && git commit -m a2 && git checkout -b b2 master && : >file2 && @@ -46,6 +50,10 @@ test_expect_success 'mode change in both branches: expect conflict' ' echo "100644 $H 3 file2" ) >expect && test_cmp actual expect && + git ls-files -s file2 | grep ^100755 +' + +test_expect_success FILEMODE 'verify executable bit on file' ' test -x file2 ' diff --git a/t/t6023-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh index 65be95fbaa..65be95fbaa 100755 --- a/t/t6023-merge-rename-nocruft.sh +++ b/t/t6034-merge-rename-nocruft.sh diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 8f5a06f7dd..2049ab6cf8 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -83,13 +83,13 @@ test_expect_success 'merge-msg test #1' ' ' cat >expected <<EOF -Merge branch 'left' of $TEST_DIRECTORY/$test +Merge branch 'left' of $(pwd) EOF test_expect_success 'merge-msg test #2' ' git checkout master && - git fetch "$TEST_DIRECTORY/$test" left && + git fetch "$(pwd)" left && git fmt-merge-msg <.git/FETCH_HEAD >actual && test_cmp expected actual diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 8fb3a56838..10b8f8c44b 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -206,7 +206,7 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' ' rm -f dirty dirty2 -test_expect_success 'git mv should overwrite symlink to a file' ' +test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' ' rm -fr .git && git init && @@ -225,7 +225,7 @@ test_expect_success 'git mv should overwrite symlink to a file' ' rm -f moved symlink -test_expect_success 'git mv should overwrite file with a symlink' ' +test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' ' rm -fr .git && git init && diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 69501e2711..73dbc4360b 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -185,8 +185,9 @@ cba EOF test_expect_success \ 'listing tags with substring as pattern must print those matching' ' - git tag -l "*a*" > actual && - test_cmp expect actual + rm *a* && + git tag -l "*a*" > current && + test_cmp expect current ' cat >expect <<EOF @@ -580,28 +581,38 @@ test_expect_success \ ' # subsequent tests require gpg; check if it is available -gpg --version >/dev/null +gpg --version >/dev/null 2>/dev/null if [ $? -eq 127 ]; then - echo "gpg not found - skipping tag signing and verification tests" - test_done - exit + say "gpg not found - skipping tag signing and verification tests" +else + # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19 + # the gpg version 1.0.6 didn't parse trust packets correctly, so for + # that version, creation of signed tags using the generated key fails. + case "$(gpg --version)" in + 'gpg (GnuPG) 1.0.6'*) + say "Skipping signed tag tests, because a bug in 1.0.6 version" + ;; + *) + test_set_prereq GPG + ;; + esac fi # trying to verify annotated non-signed tags: -test_expect_success \ +test_expect_success GPG \ 'trying to verify an annotated non-signed tag should fail' ' tag_exists annotated-tag && test_must_fail git tag -v annotated-tag ' -test_expect_success \ +test_expect_success GPG \ 'trying to verify a file-annotated non-signed tag should fail' ' tag_exists file-annotated-tag && test_must_fail git tag -v file-annotated-tag ' -test_expect_success \ +test_expect_success GPG \ 'trying to verify two annotated non-signed tags should fail' ' tag_exists annotated-tag file-annotated-tag && test_must_fail git tag -v annotated-tag file-annotated-tag @@ -609,17 +620,6 @@ test_expect_success \ # creating and verifying signed tags: -# As said here: http://www.gnupg.org/documentation/faqs.html#q6.19 -# the gpg version 1.0.6 didn't parse trust packets correctly, so for -# that version, creation of signed tags using the generated key fails. -case "$(gpg --version)" in -'gpg (GnuPG) 1.0.6'*) - echo "Skipping signed tag tests, because a bug in 1.0.6 version" - test_done - exit - ;; -esac - # key generation info: gpg --homedir t/t7004 --gen-key # Type DSA and Elgamal, size 2048 bits, no expiration date. # Name and email: C O Mitter <committer@example.com> @@ -633,7 +633,7 @@ export GNUPGHOME get_tag_header signed-tag $commit commit $time >expect echo 'A signed tag message' >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success 'creating a signed tag with -m message should succeed' ' +test_expect_success GPG 'creating a signed tag with -m message should succeed' ' git tag -s -m "A signed tag message" signed-tag && get_tag_msg signed-tag >actual && test_cmp expect actual @@ -642,7 +642,7 @@ test_expect_success 'creating a signed tag with -m message should succeed' ' get_tag_header u-signed-tag $commit commit $time >expect echo 'Another message' >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success 'sign with a given key id' ' +test_expect_success GPG 'sign with a given key id' ' git tag -u committer@example.com -m "Another message" u-signed-tag && get_tag_msg u-signed-tag >actual && @@ -650,14 +650,14 @@ test_expect_success 'sign with a given key id' ' ' -test_expect_success 'sign with an unknown id (1)' ' +test_expect_success GPG 'sign with an unknown id (1)' ' test_must_fail git tag -u author@example.com \ -m "Another message" o-signed-tag ' -test_expect_success 'sign with an unknown id (2)' ' +test_expect_success GPG 'sign with an unknown id (2)' ' test_must_fail git tag -u DEADBEEF -m "Another message" o-signed-tag @@ -674,7 +674,7 @@ chmod +x fakeeditor get_tag_header implied-sign $commit commit $time >expect ./fakeeditor >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success '-u implies signed tag' ' +test_expect_success GPG '-u implies signed tag' ' GIT_EDITOR=./fakeeditor git tag -u CDDE430D implied-sign && get_tag_msg implied-sign >actual && test_cmp expect actual @@ -687,7 +687,7 @@ EOF get_tag_header file-signed-tag $commit commit $time >expect cat sigmsgfile >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with -F messagefile should succeed' ' git tag -s -F sigmsgfile file-signed-tag && get_tag_msg file-signed-tag >actual && @@ -701,7 +701,7 @@ EOF get_tag_header stdin-signed-tag $commit commit $time >expect cat siginputmsg >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success 'creating a signed tag with -F - should succeed' ' +test_expect_success GPG 'creating a signed tag with -F - should succeed' ' git tag -s -F - stdin-signed-tag <siginputmsg && get_tag_msg stdin-signed-tag >actual && test_cmp expect actual @@ -710,13 +710,13 @@ test_expect_success 'creating a signed tag with -F - should succeed' ' get_tag_header implied-annotate $commit commit $time >expect ./fakeeditor >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success '-s implies annotated tag' ' +test_expect_success GPG '-s implies annotated tag' ' GIT_EDITOR=./fakeeditor git tag -s implied-annotate && get_tag_msg implied-annotate >actual && test_cmp expect actual ' -test_expect_success \ +test_expect_success GPG \ 'trying to create a signed tag with non-existing -F file should fail' ' ! test -f nonexistingfile && ! tag_exists nosigtag && @@ -724,13 +724,13 @@ test_expect_success \ ! tag_exists nosigtag ' -test_expect_success 'verifying a signed tag should succeed' \ +test_expect_success GPG 'verifying a signed tag should succeed' \ 'git tag -v signed-tag' -test_expect_success 'verifying two signed tags in one command should succeed' \ +test_expect_success GPG 'verifying two signed tags in one command should succeed' \ 'git tag -v signed-tag file-signed-tag' -test_expect_success \ +test_expect_success GPG \ 'verifying many signed and non-signed tags should fail' ' test_must_fail git tag -v signed-tag annotated-tag && test_must_fail git tag -v file-annotated-tag file-signed-tag && @@ -739,7 +739,7 @@ test_expect_success \ test_must_fail git tag -v signed-tag annotated-tag file-signed-tag ' -test_expect_success 'verifying a forged tag should fail' ' +test_expect_success GPG 'verifying a forged tag should fail' ' forged=$(git cat-file tag signed-tag | sed -e "s/signed-tag/forged-tag/" | git mktag) && @@ -751,7 +751,7 @@ test_expect_success 'verifying a forged tag should fail' ' get_tag_header empty-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with an empty -m message should succeed' ' git tag -s -m "" empty-signed-tag && get_tag_msg empty-signed-tag >actual && @@ -762,7 +762,7 @@ test_expect_success \ >sigemptyfile get_tag_header emptyfile-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with an empty -F messagefile should succeed' ' git tag -s -F sigemptyfile emptyfile-signed-tag && get_tag_msg emptyfile-signed-tag >actual && @@ -785,7 +785,7 @@ Trailing spaces Trailing blank lines EOF echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'extra blanks in the message for a signed tag should be removed' ' git tag -s -F sigblanksfile blanks-signed-tag && get_tag_msg blanks-signed-tag >actual && @@ -795,7 +795,7 @@ test_expect_success \ get_tag_header blank-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with a blank -m message should succeed' ' git tag -s -m " " blank-signed-tag && get_tag_msg blank-signed-tag >actual && @@ -808,7 +808,7 @@ echo '' >>sigblankfile echo ' ' >>sigblankfile get_tag_header blankfile-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with blank -F file with spaces should succeed' ' git tag -s -F sigblankfile blankfile-signed-tag && get_tag_msg blankfile-signed-tag >actual && @@ -819,7 +819,7 @@ test_expect_success \ printf ' ' >sigblanknonlfile get_tag_header blanknonlfile-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with spaces and no newline should succeed' ' git tag -s -F sigblanknonlfile blanknonlfile-signed-tag && get_tag_msg blanknonlfile-signed-tag >actual && @@ -856,7 +856,7 @@ Another line. Last line. EOF echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with a -F file with #comments should succeed' ' git tag -s -F sigcommentsfile comments-signed-tag && get_tag_msg comments-signed-tag >actual && @@ -866,7 +866,7 @@ test_expect_success \ get_tag_header comment-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with #commented -m message should succeed' ' git tag -s -m "#comment" comment-signed-tag && get_tag_msg comment-signed-tag >actual && @@ -879,7 +879,7 @@ echo '' >>sigcommentfile echo '####' >>sigcommentfile get_tag_header commentfile-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with #commented -F messagefile should succeed' ' git tag -s -F sigcommentfile commentfile-signed-tag && get_tag_msg commentfile-signed-tag >actual && @@ -890,7 +890,7 @@ test_expect_success \ printf '#comment' >sigcommentnonlfile get_tag_header commentnonlfile-signed-tag $commit commit $time >expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag with a #comment and no newline should succeed' ' git tag -s -F sigcommentnonlfile commentnonlfile-signed-tag && get_tag_msg commentnonlfile-signed-tag >actual && @@ -900,7 +900,7 @@ test_expect_success \ # listing messages for signed tags: -test_expect_success \ +test_expect_success GPG \ 'listing the one-line message of a signed tag should succeed' ' git tag -s -m "A message line signed" stag-one-line && @@ -925,7 +925,7 @@ test_expect_success \ test_cmp expect actual ' -test_expect_success \ +test_expect_success GPG \ 'listing the zero-lines message of a signed tag should succeed' ' git tag -s -m "" stag-zero-lines && @@ -953,7 +953,7 @@ test_expect_success \ echo 'stag line one' >sigtagmsg echo 'stag line two' >>sigtagmsg echo 'stag line three' >>sigtagmsg -test_expect_success \ +test_expect_success GPG \ 'listing many message lines of a signed tag should succeed' ' git tag -s -F sigtagmsg stag-lines && @@ -998,12 +998,12 @@ test_expect_success \ tree=$(git rev-parse HEAD^{tree}) blob=$(git rev-parse HEAD:foo) -tag=$(git rev-parse signed-tag) +tag=$(git rev-parse signed-tag 2>/dev/null) get_tag_header tree-signed-tag $tree tree $time >expect echo "A message for a tree" >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag pointing to a tree should succeed' ' git tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} && get_tag_msg tree-signed-tag >actual && @@ -1013,7 +1013,7 @@ test_expect_success \ get_tag_header blob-signed-tag $blob blob $time >expect echo "A message for a blob" >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag pointing to a blob should succeed' ' git tag -s -m "A message for a blob" blob-signed-tag HEAD:foo && get_tag_msg blob-signed-tag >actual && @@ -1023,7 +1023,7 @@ test_expect_success \ get_tag_header tag-signed-tag $tag tag $time >expect echo "A message for another tag" >>expect echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success \ +test_expect_success GPG \ 'creating a signed tag pointing to another tag should succeed' ' git tag -s -m "A message for another tag" tag-signed-tag signed-tag && get_tag_msg tag-signed-tag >actual && @@ -1032,7 +1032,7 @@ test_expect_success \ # try to sign with bad user.signingkey git config user.signingkey BobTheMouse -test_expect_success \ +test_expect_success GPG \ 'git tag -s fails if gpg is misconfigured' \ 'test_must_fail git tag -s -m tail tag-gpg-failure' git config --unset user.signingkey @@ -1040,7 +1040,7 @@ git config --unset user.signingkey # try to verify without gpg: rm -rf gpghome -test_expect_success \ +test_expect_success GPG \ 'verify signed tag fails when public key is not present' \ 'test_must_fail git tag -v signed-tag' diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 2d919d69ef..b647957d75 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -7,6 +7,7 @@ test_description='GIT_EDITOR, core.editor, and stuff' for i in GIT_EDITOR core_editor EDITOR VISUAL vi do cat >e-$i.sh <<-EOF + #!$SHELL_PATH echo "Edited by $i" >"\$1" EOF chmod +x e-$i.sh @@ -87,30 +88,26 @@ do ' done +if ! echo 'echo space > "$1"' > "e space.sh" +then + say "Skipping; FS does not support spaces in filenames" + test_done +fi + test_expect_success 'editor with a space' ' - if echo "echo space > \"\$1\"" > "e space.sh" - then - chmod a+x "e space.sh" && - GIT_EDITOR="./e\ space.sh" git commit --amend && - test space = "$(git show -s --pretty=format:%s)" - else - say "Skipping; FS does not support spaces in filenames" - fi + chmod a+x "e space.sh" && + GIT_EDITOR="./e\ space.sh" git commit --amend && + test space = "$(git show -s --pretty=format:%s)" ' unset GIT_EDITOR test_expect_success 'core.editor with a space' ' - if test -f "e space.sh" - then - git config core.editor \"./e\ space.sh\" && - git commit --amend && - test space = "$(git show -s --pretty=format:%s)" - else - say "Skipping; FS does not support spaces in filenames" - fi + git config core.editor \"./e\ space.sh\" && + git commit --amend && + test space = "$(git show -s --pretty=format:%s)" ' diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 1636fac2a4..929d5d4d3b 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -373,9 +373,9 @@ test_expect_success 'removal failure' ' mkdir foo && touch foo/bar && - exec <foo/bar && - chmod 0 foo && - test_must_fail git clean -f -d + (exec <foo/bar && + chmod 0 foo && + test_must_fail git clean -f -d) ' chmod 755 foo diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index ad42c78d7c..56cd866019 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -234,7 +234,7 @@ cat >.git/FAKE_EDITOR <<EOF # kill -TERM command added below. EOF -test_expect_success 'a SIGTERM should break locks' ' +test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' ' echo >>negative && ! "$SHELL_PATH" -c '\'' echo kill -TERM $$ >> .git/FAKE_EDITOR diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh index b069095995..8528f64c8d 100755 --- a/t/t7503-pre-commit-hook.sh +++ b/t/t7503-pre-commit-hook.sh @@ -69,7 +69,7 @@ test_expect_success '--no-verify with failing hook' ' ' chmod -x "$HOOK" -test_expect_success 'with non-executable hook' ' +test_expect_success POSIXPERM 'with non-executable hook' ' echo "content" >> file && git add file && @@ -77,7 +77,7 @@ test_expect_success 'with non-executable hook' ' ' -test_expect_success '--no-verify with non-executable hook' ' +test_expect_success POSIXPERM '--no-verify with non-executable hook' ' echo "more content" >> file && git add file && diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh index 47680e6df4..1f53ea8090 100755 --- a/t/t7504-commit-msg-hook.sh +++ b/t/t7504-commit-msg-hook.sh @@ -136,7 +136,7 @@ test_expect_success '--no-verify with failing hook (editor)' ' ' chmod -x "$HOOK" -test_expect_success 'with non-executable hook' ' +test_expect_success POSIXPERM 'with non-executable hook' ' echo "content" >> file && git add file && @@ -144,7 +144,7 @@ test_expect_success 'with non-executable hook' ' ' -test_expect_success 'with non-executable hook (editor)' ' +test_expect_success POSIXPERM 'with non-executable hook (editor)' ' echo "content again" >> file && git add file && @@ -153,7 +153,7 @@ test_expect_success 'with non-executable hook (editor)' ' ' -test_expect_success '--no-verify with non-executable hook' ' +test_expect_success POSIXPERM '--no-verify with non-executable hook' ' echo "more content" >> file && git add file && @@ -161,7 +161,7 @@ test_expect_success '--no-verify with non-executable hook' ' ' -test_expect_success '--no-verify with non-executable hook (editor)' ' +test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' ' echo "even more content" >> file && git add file && diff --git a/t/t7502-status.sh b/t/t7508-status.sh index 93f875f500..93f875f500 100755 --- a/t/t7502-status.sh +++ b/t/t7508-status.sh diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index f5682d66db..6b29bff782 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -88,5 +88,66 @@ test_expect_failure 'packed obs in alt ODB are repacked when local repo has pack done ' +test_expect_success 'packed obs in alternate ODB kept pack are repacked' ' + # swap the .keep so the commit object is in the pack with .keep + for p in alt_objects/pack/*.pack + do + base_name=$(basename $p .pack) + if test -f alt_objects/pack/$base_name.keep + then + rm alt_objects/pack/$base_name.keep + else + touch alt_objects/pack/$base_name.keep + fi + done + git repack -a -d && + myidx=$(ls -1 .git/objects/pack/*.idx) && + test -f "$myidx" && + for p in alt_objects/pack/*.idx; do + git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p" + done | while read sha1 rest; do + if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then + echo "Missing object in local pack: $sha1" + return 1 + fi + done +' + +test_expect_success 'packed unreachable obs in alternate ODB are not loosened' ' + rm -f alt_objects/pack/*.keep && + mv .git/objects/pack/* alt_objects/pack/ && + csha1=$(git rev-parse HEAD^{commit}) && + git reset --hard HEAD^ && + sleep 1 && + git reflog expire --expire=now --expire-unreachable=now --all && + # The pack-objects call on the next line is equivalent to + # git repack -A -d without the call to prune-packed + git pack-objects --honor-pack-keep --non-empty --all --reflog \ + --unpack-unreachable </dev/null pack && + rm -f .git/objects/pack/* && + mv pack-* .git/objects/pack/ && + test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx | + egrep "^$csha1 " | sort | uniq | wc -l) && + echo > .git/objects/info/alternates && + test_must_fail git show $csha1 +' + +test_expect_success 'local packed unreachable obs that exist in alternate ODB are not loosened' ' + echo `pwd`/alt_objects > .git/objects/info/alternates && + echo "$csha1" | git pack-objects --non-empty --all --reflog pack && + rm -f .git/objects/pack/* && + mv pack-* .git/objects/pack/ && + # The pack-objects call on the next line is equivalent to + # git repack -A -d without the call to prune-packed + git pack-objects --honor-pack-keep --non-empty --all --reflog \ + --unpack-unreachable </dev/null pack && + rm -f .git/objects/pack/* && + mv pack-* .git/objects/pack/ && + test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx | + egrep "^$csha1 " | sort | uniq | wc -l) && + echo > .git/objects/info/alternates && + test_must_fail git show $csha1 +' + test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index d7634187aa..3c90c4fc1c 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -35,6 +35,47 @@ test_expect_success 'Extract patches' ' patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1` ' +# Test no confirm early to ensure remaining tests will not hang +test_no_confirm () { + rm -f no_confirm_okay + echo n | \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --from="Example <from@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $@ \ + $patches > stdout && + test_must_fail grep "Send this email" stdout && + > no_confirm_okay +} + +# Exit immediately to prevent hang if a no-confirm test fails +check_no_confirm () { + test -f no_confirm_okay || { + say 'No confirm test failed; skipping remaining tests to prevent hanging' + test_done + } +} + +test_expect_success 'No confirm with --suppress-cc' ' + test_no_confirm --suppress-cc=sob +' +check_no_confirm + +test_expect_success 'No confirm with --confirm=never' ' + test_no_confirm --confirm=never +' +check_no_confirm + +# leave sendemail.confirm set to never after this so that none of the +# remaining tests prompt unintentionally. +test_expect_success 'No confirm with sendemail.confirm=never' ' + git config sendemail.confirm never && + test_no_confirm --compose --subject=foo +' +check_no_confirm + test_expect_success 'Send patches' ' git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors ' @@ -47,7 +88,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'Verify commandline' \ - 'diff commandline1 expected' + 'test_cmp expected commandline1' cat >expected-show-all-headers <<\EOF 0001-Second.patch @@ -89,6 +130,19 @@ test_expect_success 'Show all headers' ' test_cmp expected-show-all-headers actual-show-all-headers ' +test_expect_success 'Prompting works' ' + clean_fake_sendmail && + (echo "Example <from@example.com>" + echo "to@example.com" + echo "" + ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches \ + 2>errors && + grep "^From: Example <from@example.com>$" msgtxt1 && + grep "^To: to@example.com$" msgtxt1 +' + z8=zzzzzzzz z64=$z8$z8$z8$z8$z8$z8$z8$z8 z512=$z64$z64$z64$z64$z64$z64$z64$z64 @@ -175,15 +229,13 @@ test_set_editor "$(pwd)/fake-editor" test_expect_success '--compose works' ' clean_fake_sendmail && - echo y | \ - GIT_SEND_EMAIL_NOTTY=1 \ - git send-email \ - --compose --subject foo \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - --smtp-server="$(pwd)/fake.sendmail" \ - $patches \ - 2>errors + git send-email \ + --compose --subject foo \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches \ + 2>errors ' test_expect_success 'first message is compose text' ' @@ -375,15 +427,117 @@ test_expect_success '--suppress-cc=cc' ' test_suppression cc ' +test_confirm () { + echo y | \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $@ $patches > stdout && + grep "Send this email" stdout +} + +test_expect_success '--confirm=always' ' + test_confirm --confirm=always --suppress-cc=all +' + +test_expect_success '--confirm=auto' ' + test_confirm --confirm=auto +' + +test_expect_success '--confirm=cc' ' + test_confirm --confirm=cc +' + +test_expect_success '--confirm=compose' ' + test_confirm --confirm=compose --compose +' + +test_expect_success 'confirm by default (due to cc)' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config --unset sendemail.confirm && + test_confirm + ret="$?" + git config sendemail.confirm ${CONFIRM:-never} + test $ret = "0" +' + +test_expect_success 'confirm by default (due to --compose)' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config --unset sendemail.confirm && + test_confirm --suppress-cc=all --compose + ret="$?" + git config sendemail.confirm ${CONFIRM:-never} + test $ret = "0" +' + +test_expect_success 'confirm detects EOF (inform assumes y)' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config --unset sendemail.confirm && + rm -fr outdir && + git format-patch -2 -o outdir && + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + outdir/*.patch < /dev/null + ret="$?" + git config sendemail.confirm ${CONFIRM:-never} + test $ret = "0" +' + +test_expect_success 'confirm detects EOF (auto causes failure)' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config sendemail.confirm auto && + GIT_SEND_EMAIL_NOTTY=1 && + export GIT_SEND_EMAIL_NOTTY && + test_must_fail git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches < /dev/null + ret="$?" + git config sendemail.confirm ${CONFIRM:-never} + test $ret = "0" +' + +test_expect_success 'confirm doesnt loop forever' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config sendemail.confirm auto && + GIT_SEND_EMAIL_NOTTY=1 && + export GIT_SEND_EMAIL_NOTTY && + yes "bogus" | test_must_fail git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches + ret="$?" + git config sendemail.confirm ${CONFIRM:-never} + test $ret = "0" +' + +test_expect_success 'utf8 Cc is rfc2047 encoded' ' + clean_fake_sendmail && + rm -fr outdir && + git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" && + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + outdir/*.patch && + grep "^Cc:" msgtxt1 | + grep "=?utf-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>" +' + test_expect_success '--compose adds MIME for utf8 body' ' clean_fake_sendmail && (echo "#!$SHELL_PATH" && echo "echo utf8 body: àéìöú >>\"\$1\"" ) >fake-editor-utf8 && chmod +x fake-editor-utf8 && - echo y | \ GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \ - GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject foo \ --from="Example <nobody@example.com>" \ @@ -405,9 +559,7 @@ test_expect_success '--compose respects user mime type' ' echo " echo utf8 body: àéìöú) >\"\$1\"" ) >fake-editor-utf8-mime && chmod +x fake-editor-utf8-mime && - echo y | \ GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \ - GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject foo \ --from="Example <nobody@example.com>" \ @@ -421,9 +573,7 @@ test_expect_success '--compose respects user mime type' ' test_expect_success '--compose adds MIME for utf8 subject' ' clean_fake_sendmail && - echo y | \ GIT_EDITOR="\"$(pwd)/fake-editor\"" \ - GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject utf8-sübjëct \ --from="Example <nobody@example.com>" \ @@ -445,7 +595,7 @@ test_expect_success 'detects ambiguous reference/file conflict' ' test_expect_success 'feed two files' ' rm -fr outdir && git format-patch -2 -o outdir && - GIT_SEND_EMAIL_NOTTY=1 git send-email \ + git send-email \ --dry-run \ --from="Example <nobody@example.com>" \ --to=nobody@example.com \ diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index bb921af56a..4eee2e9fa6 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -6,19 +6,19 @@ test_description='git svn basic tests' GIT_SVN_LC_ALL=${LC_ALL:-$LANG} +. ./lib-git-svn.sh + +say 'define NO_SVN_TESTS to skip git svn tests' + case "$GIT_SVN_LC_ALL" in *.UTF-8) - have_utf8=t + test_set_prereq UTF8 ;; *) - have_utf8= + say "UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)" ;; esac -. ./lib-git-svn.sh - -say 'define NO_SVN_TESTS to skip git svn tests' - test_expect_success \ 'initialize git svn' ' mkdir import && @@ -171,20 +171,15 @@ test_expect_success "$name" ' test ! -L "$SVN_TREE"/exec-2.sh && test_cmp help "$SVN_TREE"/exec-2.sh' -if test "$have_utf8" = t -then - name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL" - LC_ALL="$GIT_SVN_LC_ALL" - export LC_ALL - test_expect_success "$name" " - echo '# hello' >> exec-2.sh && - git update-index exec-2.sh && - git commit -m 'éï∏' && - git svn set-tree HEAD" - unset LC_ALL -else - say "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)" -fi +name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL" +LC_ALL="$GIT_SVN_LC_ALL" +export LC_ALL +test_expect_success UTF8 "$name" " + echo '# hello' >> exec-2.sh && + git update-index exec-2.sh && + git commit -m 'éï∏' && + git svn set-tree HEAD" +unset LC_ALL name='test fetch functionality (svn => git) with alternate GIT_SVN_ID' GIT_SVN_ID=alt @@ -197,7 +192,7 @@ test_expect_success "$name" \ name='check imported tree checksums expected tree checksums' rm -f expected -if test "$have_utf8" = t +if test_have_prereq UTF8 then echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected fi diff --git a/t/t9108-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh index 8f79c3f251..8f79c3f251 100755 --- a/t/t9108-git-svn-multi-glob.sh +++ b/t/t9109-git-svn-multi-glob.sh diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 9c7b1ad18b..3200ab38ef 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -70,24 +70,26 @@ do done if locale -a |grep -q en_US.utf8; then - test_expect_success 'ISO-8859-1 should match UTF-8 in svn' ' + test_set_prereq UTF8 +else + say "UTF-8 locale not available, test skipped" +fi + +test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' ' ( cd ISO-8859-1 && compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt ) - ' +' - for H in EUCJP ISO-2022-JP - do - test_expect_success '$H should match UTF-8 in svn' ' +for H in EUCJP ISO-2022-JP +do + test_expect_success UTF8 "$H should match UTF-8 in svn" ' ( cd $H && compare_svn_head_with "$TEST_DIRECTORY"/t3900/2-UTF-8.txt ) - ' - done -else - say "UTF-8 locale not available, test skipped" -fi + ' +done test_done diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh index 8f35e294aa..9a24a65b64 100755 --- a/t/t9131-git-svn-empty-symlink.sh +++ b/t/t9131-git-svn-empty-symlink.sh @@ -88,7 +88,7 @@ test_expect_success 'enable broken symlink workaround' \ test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar' test_expect_success 'get "bar" => symlink fix from svn' \ '(cd x && git svn rebase)' -test_expect_success '"bar" becomes a symlink' 'test -L x/bar' +test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -L x/bar' test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y' diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh index b8de59e493..6c4c90b036 100755 --- a/t/t9132-git-svn-broken-symlink.sh +++ b/t/t9132-git-svn-broken-symlink.sh @@ -85,7 +85,7 @@ EOF test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x' -test_expect_success '"bar" is a symlink that points to "asdf"' ' +test_expect_success SYMLINKS '"bar" is a symlink that points to "asdf"' ' test -L x/bar && (cd x && test xasdf = x"`git cat-file blob HEAD:bar`") ' @@ -94,7 +94,7 @@ test_expect_success 'get "bar" => symlink fix from svn' ' (cd x && git svn rebase) ' -test_expect_success '"bar" remains a proper symlink' ' +test_expect_success SYMLINKS '"bar" remains a proper symlink' ' test -L x/bar && (cd x && test xdoink = x"`git cat-file blob HEAD:bar`") ' diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh index fd185011b7..fd185011b7 100755 --- a/t/t9106-git-svn-dcommit-clobber-series.sh +++ b/t/t9137-git-svn-dcommit-clobber-series.sh diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 245a7c3662..36656923ac 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -9,9 +9,8 @@ test_description='Test export of commits to CVS' cvs >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git cvsexportcommit tests, cvs not found' : + say 'skipping git cvsexportcommit tests, cvs not found' test_done - exit fi CVSROOT=$(pwd)/cvsroot @@ -225,11 +224,12 @@ test_expect_success \ test_must_fail git cvsexportcommit -c $id )' -case "$(git config --bool core.filemode)" in -false) - ;; -*) -test_expect_success \ +if ! test "$(git config --bool core.filemode)" = false +then + test_set_prereq FILEMODE +fi + +test_expect_success FILEMODE \ 'Retain execute bit' \ 'mkdir G && echo executeon >G/on && @@ -243,8 +243,6 @@ test_expect_success \ test -x G/on && ! test -x G/off )' - ;; -esac test_expect_success '-w option should work with relative GIT_DIR' ' mkdir W && diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index 86c376088c..8da9ce5459 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -8,6 +8,9 @@ test_description='git fast-export' test_expect_success 'setup' ' + echo break it > file0 && + git add file0 && + test_tick && echo Wohlauf > file && git add file && test_tick && @@ -57,8 +60,8 @@ test_expect_success 'fast-export master~2..master' ' (cd new && git fast-import && test $MASTER != $(git rev-parse --verify refs/heads/partial) && - git diff master..partial && - git diff master^..partial^ && + git diff --exit-code master partial && + git diff --exit-code master^ partial^ && test_must_fail git rev-parse partial~2) ' @@ -259,4 +262,19 @@ test_expect_success 'cope with tagger-less tags' ' ' +test_expect_success 'set-up a few more tags for tag export tests' ' + git checkout -f master && + HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` && + git tag tree_tag -m "tagging a tree" $HEAD_TREE && + git tag -a tree_tag-obj -m "tagging a tree" $HEAD_TREE && + git tag tag-obj_tag -m "tagging a tag" tree_tag-obj && + git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj +' + +# NEEDSWORK: not just check return status, but validate the output +test_expect_success 'tree_tag' 'git fast-export tree_tag' +test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj' +test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag' +test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj' + test_done diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 6a37f71d11..39185db6c9 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -13,14 +13,12 @@ cvs CLI client via git-cvsserver server' cvs >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git-cvsserver tests, cvs not found' : + say 'skipping git-cvsserver tests, cvs not found' test_done - exit fi perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { - test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' : + say 'skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done - exit } unset GIT_DIR GIT_CONFIG @@ -44,7 +42,7 @@ test_expect_success 'setup' ' git add secondrootfile && git commit -m "second root") && git pull secondroot master && - git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ' @@ -267,7 +265,7 @@ test_expect_success 'gitcvs.ext.dbname' \ rm -fr "$SERVERDIR" cd "$WORKDIR" && -git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && +git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" || exit 1 diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index e27a1c5f85..12e0e50822 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -49,14 +49,12 @@ not_present() { cvs >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git-cvsserver tests, cvs not found' : + say 'skipping git-cvsserver tests, cvs not found' test_done - exit fi perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { - test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' : + say 'skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done - exit } unset GIT_DIR GIT_CONFIG @@ -84,7 +82,7 @@ test_expect_success 'setup' ' echo "subdir/file.h crlf" >> .gitattributes && git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* && git commit -q -m "First Commit" && - git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ' diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 6ed10d0933..0bd332c493 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -63,20 +63,11 @@ gitweb_run () { # gitweb.log is left for debugging } -safe_chmod () { - chmod "$1" "$2" && - if [ "$(git config --get core.filemode)" = false ] - then - git update-index --chmod="$1" "$2" - fi -} - . ./test-lib.sh perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || { - test_expect_success 'skipping gitweb tests, perl version is too old' : + say 'skipping gitweb tests, perl version is too old' test_done - exit } gitweb_init @@ -242,7 +233,7 @@ test_debug 'cat gitweb.log' test_expect_success \ 'commitdiff(0): mode change' \ - 'safe_chmod +x new_file && + 'test_chmod +x new_file && git commit -a -m "Mode changed." && gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' @@ -254,7 +245,7 @@ test_expect_success \ gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' -test_expect_success \ +test_expect_success SYMLINKS \ 'commitdiff(0): file to symlink' \ 'rm renamed_file && ln -s file renamed_file && @@ -281,7 +272,7 @@ test_debug 'cat gitweb.log' test_expect_success \ 'commitdiff(0): mode change and modified' \ 'echo "New line" >> file2 && - safe_chmod +x file2 && + test_chmod +x file2 && git commit -a -m "Mode change and modification." && gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' @@ -308,7 +299,7 @@ test_expect_success \ 'commitdiff(0): renamed, mode change and modified' \ 'git mv file3 file2 && echo "Propter nomen suum." >> file2 && - safe_chmod +x file2 && + test_chmod +x file2 && git commit -a -m "File rename, mode change and modification." && gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' @@ -316,7 +307,7 @@ test_debug 'cat gitweb.log' # ---------------------------------------------------------------------- # commitdiff testing (taken from t4114-apply-typechange.sh) -test_expect_success 'setup typechange commits' ' +test_expect_success SYMLINKS 'setup typechange commits' ' echo "hello world" > foo && echo "hi planet" > bar && git update-index --add foo bar && @@ -425,10 +416,15 @@ test_expect_success \ git add 03-new && git mv 04-rename-from 04-rename-to && echo "Changed" >> 04-rename-to && - safe_chmod +x 05-mode-change && - rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink && + test_chmod +x 05-mode-change && + rm -f 06-file-or-symlink && + if test_have_prereq SYMLINKS; then + ln -s 01-change 06-file-or-symlink + else + printf %s 01-change > 06-file-or-symlink + fi && echo "Changed and have mode changed" > 07-change-mode-change && - safe_chmod +x 07-change-mode-change && + test_chmod +x 07-change-mode-change && git commit -a -m "Large commit" && git checkout master' diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index d2379e7f62..33eb51938d 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -14,7 +14,6 @@ if ! type cvs >/dev/null 2>&1 then say 'skipping cvsimport tests, cvs not found' test_done - exit fi cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` @@ -24,12 +23,10 @@ case "$cvsps_version" in '') say 'skipping cvsimport tests, cvsps not found' test_done - exit ;; *) say 'skipping cvsimport tests, unsupported cvsps version' test_done - exit ;; esac diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index b81d5dfc34..4a501c6847 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -7,7 +7,7 @@ test_description='perl interface (Git.pm)' . ./test-lib.sh perl -MTest::More -e 0 2>/dev/null || { - say_color skip "Perl Test::More unavailable, skipping test" + say "Perl Test::More unavailable, skipping test" test_done } diff --git a/t/test-lib.sh b/t/test-lib.sh index 59d82d25e9..b050196cb7 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -3,6 +3,22 @@ # Copyright (c) 2005 Junio C Hamano # +# if --tee was passed, write the output not only to the terminal, but +# additionally to the file test-results/$BASENAME.out, too. +case "$GIT_TEST_TEE_STARTED, $* " in +done,*) + # do not redirect again + ;; +*' --tee '*|*' --va'*) + mkdir -p test-results + BASE=test-results/$(basename "$0" .sh) + (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; + echo $? > $BASE.exit) | tee $BASE.out + test "$(cat $BASE.exit)" = 0 + exit + ;; +esac + # Keep the original TERM for say_color ORIGINAL_TERM=$TERM @@ -94,6 +110,10 @@ do --no-python) # noop now... shift ;; + --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) + valgrind=t; verbose=t; shift ;; + --tee) + shift ;; # was handled already *) break ;; esac @@ -218,18 +238,50 @@ test_merge () { git tag "$1" } +# This function helps systems where core.filemode=false is set. +# Use it instead of plain 'chmod +x' to set or unset the executable bit +# of a file in the working directory and add it to the index. + +test_chmod () { + chmod "$@" && + git update-index --add "--chmod=$@" +} + +# Use test_set_prereq to tell that a particular prerequisite is available. +# The prerequisite can later be checked for in two ways: +# +# - Explicitly using test_have_prereq. +# +# - Implicitly by specifying the prerequisite tag in the calls to +# test_expect_{success,failure,code}. +# +# The single parameter is the prerequisite tag (a simple word, in all +# capital letters by convention). + +test_set_prereq () { + satisfied="$satisfied$1 " +} +satisfied=" " + +test_have_prereq () { + case $satisfied in + *" $1 "*) + : yes, have it ;; + *) + ! : nope ;; + esac +} + # You are not expected to call test_ok_ and test_failure_ directly, use # the text_expect_* functions instead. test_ok_ () { - test_count=$(expr "$test_count" + 1) - test_success=$(expr "$test_success" + 1) + test_success=$(($test_success + 1)) say_color "" " ok $test_count: $@" } test_failure_ () { - test_count=$(expr "$test_count" + 1) - test_failure=$(expr "$test_failure" + 1); + test_failure=$(($test_failure + 1)) say_color error "FAIL $test_count: $1" shift echo "$@" | sed -e 's/^/ /' @@ -237,13 +289,11 @@ test_failure_ () { } test_known_broken_ok_ () { - test_count=$(expr "$test_count" + 1) test_fixed=$(($test_fixed+1)) say_color "" " FIXED $test_count: $@" } test_known_broken_failure_ () { - test_count=$(expr "$test_count" + 1) test_broken=$(($test_broken+1)) say_color skip " still broken $test_count: $@" } @@ -259,20 +309,23 @@ test_run_ () { } test_skip () { - this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') - this_test="$this_test.$(expr "$test_count" + 1)" + test_count=$(($test_count+1)) to_skip= for skp in $GIT_SKIP_TESTS do - case "$this_test" in + case $this_test.$test_count in $skp) to_skip=t esac done + if test -z "$to_skip" && test -n "$prereq" && + ! test_have_prereq "$prereq" + then + to_skip=t + fi case "$to_skip" in t) say_color skip >&3 "skipping test: $@" - test_count=$(expr "$test_count" + 1) say_color skip "skip $test_count: $1" : true ;; @@ -283,8 +336,9 @@ test_skip () { } test_expect_failure () { + test "$#" = 3 && { prereq=$1; shift; } || prereq= test "$#" = 2 || - error "bug in the test script: not 2 parameters to test-expect-failure" + error "bug in the test script: not 2 or 3 parameters to test-expect-failure" if ! test_skip "$@" then say >&3 "checking known breakage: $2" @@ -300,8 +354,9 @@ test_expect_failure () { } test_expect_success () { + test "$#" = 3 && { prereq=$1; shift; } || prereq= test "$#" = 2 || - error "bug in the test script: not 2 parameters to test-expect-success" + error "bug in the test script: not 2 or 3 parameters to test-expect-success" if ! test_skip "$@" then say >&3 "expecting success: $2" @@ -317,8 +372,9 @@ test_expect_success () { } test_expect_code () { + test "$#" = 4 && { prereq=$1; shift; } || prereq= test "$#" = 3 || - error "bug in the test script: not 3 parameters to test-expect-code" + error "bug in the test script: not 3 or 4 parameters to test-expect-code" if ! test_skip "$@" then say >&3 "expecting exit code $1: $3" @@ -342,15 +398,16 @@ test_expect_code () { # Usage: test_external description command arguments... # Example: test_external 'Perl API' perl ../path/to/test.pl test_external () { - test "$#" -eq 3 || - error >&5 "bug in the test script: not 3 parameters to test_external" + test "$#" = 4 && { prereq=$1; shift; } || prereq= + test "$#" = 3 || + error >&5 "bug in the test script: not 3 or 4 parameters to test_external" descr="$1" shift if ! test_skip "$descr" "$@" then # Announce the script to reduce confusion about the # test output that follows. - say_color "" " run $(expr "$test_count" + 1): $descr ($*)" + say_color "" " run $test_count: $descr ($*)" # Run command; redirect its stderr to &4 as in # test_run_, but keep its stdout on our stdout even in # non-verbose mode. @@ -434,7 +491,7 @@ test_create_repo () { repo="$1" mkdir -p "$repo" cd "$repo" || error "Cannot setup test environment" - "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 || + "$GIT_EXEC_PATH/git-init" "--template=$owd/../templates/blt/" >&3 2>&4 || error "cannot run git init -- have you built things yet?" mv .git/hooks .git/hooks-disabled cd "$owd" @@ -444,7 +501,7 @@ test_done () { trap - EXIT test_results_dir="$TEST_DIRECTORY/test-results" mkdir -p "$test_results_dir" - test_results_path="$test_results_dir/${0%-*}-$$" + test_results_path="$test_results_dir/${0%.sh}-$$" echo "total $test_count" >> $test_results_path echo "success $test_success" >> $test_results_path @@ -484,8 +541,81 @@ test_done () { # Test the binaries we have just built. The tests are kept in # t/ subdirectory and are run in 'trash directory' subdirectory. TEST_DIRECTORY=$(pwd) -PATH=$TEST_DIRECTORY/..:$PATH -GIT_EXEC_PATH=$(pwd)/.. +if test -z "$valgrind" +then + if test -z "$GIT_TEST_INSTALLED" + then + PATH=$TEST_DIRECTORY/..:$PATH + GIT_EXEC_PATH=$TEST_DIRECTORY/.. + else + GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) || + error "Cannot run git from $GIT_TEST_INSTALLED." + PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH + GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH} + fi +else + make_symlink () { + test -h "$2" && + test "$1" = "$(readlink "$2")" || { + # be super paranoid + if mkdir "$2".lock + then + rm -f "$2" && + ln -s "$1" "$2" && + rm -r "$2".lock + else + while test -d "$2".lock + do + say "Waiting for lock on $2." + sleep 1 + done + fi + } + } + + make_valgrind_symlink () { + # handle only executables + test -x "$1" || return + + base=$(basename "$1") + symlink_target=$TEST_DIRECTORY/../$base + # do not override scripts + if test -x "$symlink_target" && + test ! -d "$symlink_target" && + test "#!" != "$(head -c 2 < "$symlink_target")" + then + symlink_target=../valgrind.sh + fi + case "$base" in + *.sh|*.perl) + symlink_target=../unprocessed-script + esac + # create the link, or replace it if it is out of date + make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit + } + + # override all git executables in TEST_DIRECTORY/.. + GIT_VALGRIND=$TEST_DIRECTORY/valgrind + mkdir -p "$GIT_VALGRIND"/bin + for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-* + do + make_valgrind_symlink $file + done + OLDIFS=$IFS + IFS=: + for path in $PATH + do + ls "$path"/git-* 2> /dev/null | + while read file + do + make_valgrind_symlink "$file" + done + done + IFS=$OLDIFS + PATH=$GIT_VALGRIND/bin:$PATH + GIT_EXEC_PATH=$GIT_VALGRIND/bin + export GIT_VALGRIND +fi GIT_TEMPLATE_DIR=$(pwd)/../templates/blt unset GIT_CONFIG GIT_CONFIG_NOSYSTEM=1 @@ -520,7 +650,8 @@ test_create_repo "$test" # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$test" || exit 1 -this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') +this_test=${0##*/} +this_test=${this_test%%-*} for skp in $GIT_SKIP_TESTS do to_skip= @@ -538,3 +669,35 @@ do test_done esac done + +# Fix some commands on Windows +case $(uname -s) in +*MINGW*) + # Windows has its own (incompatible) sort and find + sort () { + /usr/bin/sort "$@" + } + find () { + /usr/bin/find "$@" + } + sum () { + md5sum "$@" + } + # git sees Windows-style pwd + pwd () { + builtin pwd -W + } + # no POSIX permissions + # backslashes in pathspec are converted to '/' + # exec does not inherit the PID + ;; +*) + test_set_prereq POSIXPERM + test_set_prereq BSLASHPSPEC + test_set_prereq EXECKEEPSPID + ;; +esac + +# test whether the filesystem supports symbolic links +ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS +rm -f y diff --git a/t/valgrind/.gitignore b/t/valgrind/.gitignore new file mode 100644 index 0000000000..d4ae6676d1 --- /dev/null +++ b/t/valgrind/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/templates diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh new file mode 100755 index 0000000000..d8105d9fab --- /dev/null +++ b/t/valgrind/analyze.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +out_prefix=$(dirname "$0")/../test-results/valgrind.out +output= +count=0 +total_count=0 +missing_message= +new_line=' +' + +# start outputting the current valgrind error in $out_prefix.++$count, +# and the test case which failed in the corresponding .message file +start_output () { + test -z "$output" || return + + # progress + total_count=$(($total_count+1)) + test -t 2 && printf "\rFound %d errors" $total_count >&2 + + count=$(($count+1)) + output=$out_prefix.$count + : > $output + + echo "*** $1 ***" > $output.message +} + +finish_output () { + test ! -z "$output" || return + output= + + # if a test case has more than one valgrind error, we need to + # copy the last .message file to the previous errors + test -z "$missing_message" || { + while test $missing_message -lt $count + do + cp $out_prefix.$count.message \ + $out_prefix.$missing_message.message + missing_message=$(($missing_message+1)) + done + missing_message= + } +} + +# group the valgrind errors by backtrace +output_all () { + last_line= + j=0 + i=1 + while test $i -le $count + do + # output <number> <backtrace-in-one-line> + echo "$i $(tr '\n' ' ' < $out_prefix.$i)" + i=$(($i+1)) + done | + sort -t ' ' -k 2 | # order by <backtrace-in-one-line> + while read number line + do + # find duplicates, do not output backtrace twice + if test "$line" != "$last_line" + then + last_line=$line + j=$(($j+1)) + printf "\nValgrind error $j:\n\n" + cat $out_prefix.$number + printf "\nfound in:\n" + fi + # print the test case where this came from + printf "\n" + cat $out_prefix.$number.message + done +} + +handle_one () { + OLDIFS=$IFS + IFS="$new_line" + while read line + do + case "$line" in + # backtrace, possibly a new one + ==[0-9]*) + + # Does the current valgrind error have a message yet? + case "$output" in + *.message) + test -z "$missing_message" && + missing_message=$count + output= + esac + + start_output $(basename $1) + echo "$line" | + sed 's/==[0-9]*==/==valgrind==/' >> $output + ;; + # end of backtrace + '}') + test -z "$output" || { + echo "$line" >> $output + test $output = ${output%.message} && + output=$output.message + } + ;; + # end of test case + '') + finish_output + ;; + # normal line; if $output is set, print the line + *) + test -z "$output" || echo "$line" >> $output + ;; + esac + done < $1 + IFS=$OLDIFS + + # just to be safe + finish_output +} + +for test_script in "$(dirname "$0")"/../test-results/*.out +do + handle_one $test_script +done + +output_all diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp new file mode 100644 index 0000000000..9e013fa3b2 --- /dev/null +++ b/t/valgrind/default.supp @@ -0,0 +1,45 @@ +{ + ignore-zlib-errors-cond + Memcheck:Cond + obj:*libz.so* +} + +{ + ignore-zlib-errors-value8 + Memcheck:Value8 + obj:*libz.so* +} + +{ + ignore-zlib-errors-value4 + Memcheck:Value4 + obj:*libz.so* +} + +{ + ignore-ldso-cond + Memcheck:Cond + obj:*ld-*.so +} + +{ + ignore-ldso-addr8 + Memcheck:Addr8 + obj:*ld-*.so +} + +{ + ignore-ldso-addr4 + Memcheck:Addr4 + obj:*ld-*.so +} + +{ + writing-data-from-zlib-triggers-even-more-errors + Memcheck:Param + write(buf) + obj:/lib/ld-*.so + fun:write_in_full + fun:write_buffer + fun:write_loose_object +} diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh new file mode 100755 index 0000000000..582b4dca94 --- /dev/null +++ b/t/valgrind/valgrind.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +base=$(basename "$0") + +TRACK_ORIGINS= + +VALGRIND_VERSION=$(valgrind --version) +VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)') +VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)') +test 3 -gt "$VALGRIND_MAJOR" || +test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" || +TRACK_ORIGINS=--track-origins=yes + +exec valgrind -q --error-exitcode=126 \ + --leak-check=no \ + --suppressions="$GIT_VALGRIND/default.supp" \ + --gen-suppressions=all \ + $TRACK_ORIGINS \ + --log-fd=4 \ + --input-fd=4 \ + $GIT_VALGRIND_OPTIONS \ + "$GIT_VALGRIND"/../../"$base" "$@" diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample index 93c605594f..a3f68ae3b4 100755 --- a/templates/hooks--update.sample +++ b/templates/hooks--update.sample @@ -43,10 +43,12 @@ allowdeletetag=$(git config --bool hooks.allowdeletetag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") -if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then +case "$projectdesc" in +"Unnamed repository"* | "") echo "*** Project description file hasn't been set" >&2 exit 1 -fi + ;; +esac # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. diff --git a/templates/this--description b/templates/this--description index c6f25e80b8..498b267a8c 100644 --- a/templates/this--description +++ b/templates/this--description @@ -1 +1 @@ -Unnamed repository; edit this file to name it for gitweb. +Unnamed repository; edit this file 'description' to name the repository. diff --git a/transport.c b/transport.c index 9ae92cd39c..3dfb03c06e 100644 --- a/transport.c +++ b/transport.c @@ -143,7 +143,7 @@ static const char *rsync_url(const char *url) return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url; } -static struct ref *get_refs_via_rsync(struct transport *transport) +static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; struct ref dummy, *tail = &dummy; @@ -151,6 +151,9 @@ static struct ref *get_refs_via_rsync(struct transport *transport) const char *args[5]; int temp_dir_len; + if (for_push) + return NULL; + /* copy the refs to the temporary directory */ strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); @@ -429,7 +432,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons return !!err; } -static struct ref *get_refs_via_curl(struct transport *transport) +static struct ref *get_refs_via_curl(struct transport *transport, int for_push) { struct strbuf buffer = STRBUF_INIT; char *data, *start, *mid; @@ -446,6 +449,9 @@ static struct ref *get_refs_via_curl(struct transport *transport) struct walker *walker; + if (for_push) + return NULL; + if (!transport->data) transport->data = get_http_walker(transport->url, transport->remote); @@ -532,12 +538,15 @@ struct bundle_transport_data { struct bundle_header header; }; -static struct ref *get_refs_from_bundle(struct transport *transport) +static struct ref *get_refs_from_bundle(struct transport *transport, int for_push) { struct bundle_transport_data *data = transport->data; struct ref *result = NULL; int i; + if (for_push) + return NULL; + if (data->fd > 0) close(data->fd); data->fd = read_bundle_header(transport->url, &data->header); @@ -578,6 +587,7 @@ struct git_transport_data { int fd[2]; const char *uploadpack; const char *receivepack; + struct extra_have_objects extra_have; }; static int set_git_option(struct transport *connection, @@ -609,20 +619,23 @@ static int set_git_option(struct transport *connection, return 1; } -static int connect_setup(struct transport *transport) +static int connect_setup(struct transport *transport, int for_push, int verbose) { struct git_transport_data *data = transport->data; - data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0); + data->conn = git_connect(data->fd, transport->url, + for_push ? data->receivepack : data->uploadpack, + verbose ? CONNECT_VERBOSE : 0); return 0; } -static struct ref *get_refs_via_connect(struct transport *transport) +static struct ref *get_refs_via_connect(struct transport *transport, int for_push) { struct git_transport_data *data = transport->data; struct ref *refs; - connect_setup(transport); - get_remote_heads(data->fd[0], &refs, 0, NULL, 0, NULL); + connect_setup(transport, for_push, 0); + get_remote_heads(data->fd[0], &refs, 0, NULL, + for_push ? REF_NORMAL : 0, &data->extra_have); return refs; } @@ -654,7 +667,7 @@ static int fetch_refs_via_pack(struct transport *transport, origh[i] = heads[i] = xstrdup(to_fetch[i]->name); if (!data->conn) { - connect_setup(transport); + connect_setup(transport, 0, 0); get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL); } @@ -677,20 +690,216 @@ static int fetch_refs_via_pack(struct transport *transport, return (refs ? 0 : -1); } -static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +static int refs_pushed(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch(ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + break; + default: + return 1; + } + } + return 0; +} + +static void update_tracking_ref(struct remote *remote, struct ref *ref, int verbose) +{ + struct refspec rs; + + if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE) + return; + + rs.src = ref->name; + rs.dst = NULL; + + if (!remote_find_tracking(remote, &rs)) { + if (verbose) + fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); + if (ref->deletion) { + delete_ref(rs.dst, NULL, 0); + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + free(rs.dst); + } +} + +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + +static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) +{ + fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); + if (from) + fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + else + fputs(prettify_ref(to), stderr); + if (msg) { + fputs(" (", stderr); + fputs(msg, stderr); + fputc(')', stderr); + } + fputc('\n', stderr); +} + +static const char *status_abbrev(unsigned char sha1[20]) +{ + return find_unique_abbrev(sha1, DEFAULT_ABBREV); +} + +static void print_ok_ref_status(struct ref *ref) +{ + if (ref->deletion) + print_ref_status('-', "[deleted]", ref, NULL, NULL); + else if (is_null_sha1(ref->old_sha1)) + print_ref_status('*', + (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" : + "[new branch]"), + ref, ref->peer_ref, NULL); + else { + char quickref[84]; + char type; + const char *msg; + + strcpy(quickref, status_abbrev(ref->old_sha1)); + if (ref->nonfastforward) { + strcat(quickref, "..."); + type = '+'; + msg = "forced update"; + } else { + strcat(quickref, ".."); + type = ' '; + msg = NULL; + } + strcat(quickref, status_abbrev(ref->new_sha1)); + + print_ref_status(type, quickref, ref, ref->peer_ref, msg); + } +} + +static int print_one_push_status(struct ref *ref, const char *dest, int count) +{ + if (!count) + fprintf(stderr, "To %s\n", dest); + + switch(ref->status) { + case REF_STATUS_NONE: + print_ref_status('X', "[no match]", ref, NULL, NULL); + break; + case REF_STATUS_REJECT_NODELETE: + print_ref_status('!', "[rejected]", ref, NULL, + "remote does not support deleting refs"); + break; + case REF_STATUS_UPTODATE: + print_ref_status('=', "[up to date]", ref, + ref->peer_ref, NULL); + break; + case REF_STATUS_REJECT_NONFASTFORWARD: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "non-fast forward"); + break; + case REF_STATUS_REMOTE_REJECT: + print_ref_status('!', "[remote rejected]", ref, + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status); + break; + case REF_STATUS_EXPECTING_REPORT: + print_ref_status('!', "[remote failure]", ref, + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status"); + break; + case REF_STATUS_OK: + print_ok_ref_status(ref); + break; + } + + return 1; +} + +static void print_push_status(const char *dest, struct ref *refs, int verbose) +{ + struct ref *ref; + int n = 0; + + if (verbose) { + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_UPTODATE) + n += print_one_push_status(ref, dest, n); + } + + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + + for (ref = refs; ref; ref = ref->next) { + if (ref->status != REF_STATUS_NONE && + ref->status != REF_STATUS_UPTODATE && + ref->status != REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + } +} + +static void verify_remote_names(int nr_heads, const char **heads) +{ + int i; + + for (i = 0; i < nr_heads; i++) { + const char *local = heads[i]; + const char *remote = strrchr(heads[i], ':'); + + if (*local == '+') + local++; + + /* A matching refspec is okay. */ + if (remote == local && remote[1] == '\0') + continue; + + remote = remote ? (remote + 1) : local; + switch (check_ref_format(remote)) { + case 0: /* ok */ + case CHECK_REF_FORMAT_ONELEVEL: + /* ok but a single level -- that is fine for + * a match pattern. + */ + case CHECK_REF_FORMAT_WILDCARD: + /* ok but ends with a pattern-match character */ + continue; + } + die("remote part of refspec is not a valid name in %s", + heads[i]); + } +} + +static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags) { struct git_transport_data *data = transport->data; struct send_pack_args args; + int ret; + + if (!data->conn) { + struct ref *tmp_refs; + connect_setup(transport, 1, 0); + + get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL, + NULL); + } - args.receivepack = data->receivepack; - args.send_all = !!(flags & TRANSPORT_PUSH_ALL); args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR); args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); args.use_thin_pack = data->thin; args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE); args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); - return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec); + ret = send_pack(&args, data->fd, data->conn, remote_refs, + &data->extra_have); + + close(data->fd[1]); + close(data->fd[0]); + ret |= finish_connect(data->conn); + data->conn = NULL; + + return ret; } static int disconnect_git(struct transport *transport) @@ -760,7 +969,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->set_option = set_git_option; ret->get_refs_list = get_refs_via_connect; ret->fetch = fetch_refs_via_pack; - ret->push = git_transport_push; + ret->push_refs = git_transport_push; ret->disconnect = disconnect_git; data->thin = 1; @@ -787,15 +996,53 @@ int transport_set_option(struct transport *transport, int transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { - if (!transport->push) - return 1; - return transport->push(transport, refspec_nr, refspec, flags); + verify_remote_names(refspec_nr, refspec); + + if (transport->push) + return transport->push(transport, refspec_nr, refspec, flags); + if (transport->push_refs) { + struct ref *remote_refs = + transport->get_refs_list(transport, 1); + struct ref **remote_tail; + struct ref *local_refs = get_local_heads(); + int match_flags = MATCH_REFS_NONE; + int verbose = flags & TRANSPORT_PUSH_VERBOSE; + int ret; + + if (flags & TRANSPORT_PUSH_ALL) + match_flags |= MATCH_REFS_ALL; + if (flags & TRANSPORT_PUSH_MIRROR) + match_flags |= MATCH_REFS_MIRROR; + + remote_tail = &remote_refs; + while (*remote_tail) + remote_tail = &((*remote_tail)->next); + if (match_refs(local_refs, remote_refs, &remote_tail, + refspec_nr, refspec, match_flags)) { + return -1; + } + + ret = transport->push_refs(transport, remote_refs, flags); + + print_push_status(transport->url, remote_refs, verbose); + + if (!(flags & TRANSPORT_PUSH_DRY_RUN)) { + struct ref *ref; + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(transport->remote, ref, verbose); + } + + if (!ret && !refs_pushed(remote_refs)) + fprintf(stderr, "Everything up-to-date\n"); + return ret; + } + return 1; } const struct ref *transport_get_remote_refs(struct transport *transport) { if (!transport->remote_refs) - transport->remote_refs = transport->get_refs_list(transport); + transport->remote_refs = transport->get_refs_list(transport, 0); return transport->remote_refs; } diff --git a/transport.h b/transport.h index 6bbc1a8264..b1c2252766 100644 --- a/transport.h +++ b/transport.h @@ -18,8 +18,9 @@ struct transport { int (*set_option)(struct transport *connection, const char *name, const char *value); - struct ref *(*get_refs_list)(struct transport *transport); + struct ref *(*get_refs_list)(struct transport *transport, int for_push); int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs); + int (*push_refs)(struct transport *transport, struct ref *refs, int flags); int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); int (*disconnect)(struct transport *connection); diff --git a/unpack-trees.c b/unpack-trees.c index 5820ce48f3..6847c2d966 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -7,6 +7,7 @@ #include "unpack-trees.h" #include "progress.h" #include "refs.h" +#include "attr.h" /* * Error messages expected by scripts out of plumbing commands such as @@ -52,36 +53,17 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); } -/* Unlink the last component and attempt to remove leading - * directories, in case this unlink is the removal of the - * last entry in the directory -- empty directories are removed. +/* + * Unlink the last component and schedule the leading directories for + * removal, such that empty directories get removed. */ static void unlink_entry(struct cache_entry *ce) { - char *cp, *prev; - char *name = ce->name; - - if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return; - if (unlink(name)) + if (unlink(ce->name)) return; - prev = NULL; - while (1) { - int status; - cp = strrchr(name, '/'); - if (prev) - *prev = '/'; - if (!cp) - break; - - *cp = 0; - status = rmdir(name); - if (status) { - *cp = '/'; - break; - } - prev = cp; - } + schedule_dir_for_removal(ce->name, ce_namelen(ce)); } static struct checkout state; @@ -105,6 +87,7 @@ static int check_updates(struct unpack_trees_options *o) cnt = 0; } + git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result); for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; @@ -112,11 +95,10 @@ static int check_updates(struct unpack_trees_options *o) display_progress(progress, ++cnt); if (o->update) unlink_entry(ce); - remove_index_entry_at(&o->result, i); - i--; - continue; } } + remove_marked_cache_entries(&o->result); + remove_scheduled_dirs(); for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; @@ -130,6 +112,7 @@ static int check_updates(struct unpack_trees_options *o) } } stop_progress(&progress); + git_attr_set_direction(GIT_ATTR_CHECKIN, NULL); return errs != 0; } @@ -380,8 +363,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options memset(&o->result, 0, sizeof(o->result)); o->result.initialized = 1; - if (o->src_index) - o->result.timestamp = o->src_index->timestamp; + if (o->src_index) { + o->result.timestamp.sec = o->src_index->timestamp.sec; + o->result.timestamp.nsec = o->src_index->timestamp.nsec; + } o->merge_size = len; if (!dfc) @@ -446,7 +431,7 @@ static int verify_uptodate(struct cache_entry *ce, { struct stat st; - if (o->index_only || o->reset) + if (o->index_only || o->reset || ce_uptodate(ce)) return 0; if (!lstat(ce->name, &st)) { @@ -583,7 +568,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_or_noent_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return 0; if (!lstat(ce->name, &st)) { diff --git a/upload-pack.c b/upload-pack.c index 19c24db643..a49d872447 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -397,12 +397,11 @@ static int get_common_commits(void) static char line[1000]; unsigned char sha1[20]; char hex[41], last_hex[41]; - int len; save_commit_buffer = 0; for(;;) { - len = packet_read_line(0, line, sizeof(line)); + int len = packet_read_line(0, line, sizeof(line)); reset_timeout(); if (!len) { @@ -410,7 +409,7 @@ static int get_common_commits(void) packet_write(1, "NAK\n"); continue; } - len = strip(line, len); + strip(line, len); if (!prefixcmp(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ @@ -645,7 +644,7 @@ int main(int argc, char **argv) dir = argv[i]; if (!enter_repo(dir, strict)) - die("'%s': unable to chdir or not a git archive", dir); + die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to fetch/clone from a shallow repository"); if (getenv("GIT_DEBUG_SEND_PACK")) diff --git a/wt-status.c b/wt-status.c index 96ff2f8f56..929b00f592 100644 --- a/wt-status.c +++ b/wt-status.c @@ -15,11 +15,11 @@ int wt_status_relative_paths = 1; int wt_status_use_color = -1; int wt_status_submodule_summary; static char wt_status_colors[][COLOR_MAXLEN] = { - "", /* WT_STATUS_HEADER: normal */ - "\033[32m", /* WT_STATUS_UPDATED: green */ - "\033[31m", /* WT_STATUS_CHANGED: red */ - "\033[31m", /* WT_STATUS_UNTRACKED: red */ - "\033[31m", /* WT_STATUS_NOBRANCH: red */ + GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */ + GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */ + GIT_COLOR_RED, /* WT_STATUS_CHANGED */ + GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */ + GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */ }; enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; @@ -250,10 +250,9 @@ static void wt_status_print_untracked(struct wt_status *s) memset(&dir, 0, sizeof(dir)); - if (!s->untracked) { - dir.show_other_directories = 1; - dir.hide_empty_directories = 1; - } + if (!s->untracked) + dir.flags |= + DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; setup_standard_excludes(&dir); read_directory(&dir, ".", "", 0, NULL); diff --git a/xdiff-interface.c b/xdiff-interface.c index d782f06d99..b9b0db8d86 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -15,11 +15,10 @@ static int parse_num(char **cp_p, int *num_p) { char *cp = *cp_p; int num = 0; - int read_some; while ('0' <= *cp && *cp <= '9') num = num * 10 + *cp++ - '0'; - if (!(read_some = cp - *cp_p)) + if (!(cp - *cp_p)) return -1; *cp_p = cp; *num_p = num; diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 3e97462bdd..02184d9cde 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -293,15 +293,14 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, for (; off1 < lim1; off1++) rchg1[rindex1[off1]] = 1; } else { - long ec; xdpsplit_t spl; spl.i1 = spl.i2 = 0; /* * Divide ... */ - if ((ec = xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, - need_min, &spl, xenv)) < 0) { + if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, + need_min, &spl, xenv) < 0) { return -1; } diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 05bfa41f10..c4bedf0d1c 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -132,7 +132,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (xecfg->flags & XDL_EMIT_COMMON) return xdl_emit_common(xe, xscr, ecb, xecfg); - for (xch = xche = xscr; xch; xch = xche->next) { + for (xch = xscr; xch; xch = xche->next) { xche = xdl_get_hunk(xch, xecfg); s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); |